Inference-depth anchoring playground: vertical + radial modes

The graph's follows/supports edges encode derivation (premise ->
conclusion); the layout can now express that rank spatially, as a
session-only playground toggle (nothing promoted yet):

- inferenceDepths: longest chain from a base node, CYCLE-SAFE by
  SCC condensation (Tarjan, iterative) — the DAG property is typical,
  not guaranteed; cycle members share their component's depth (mutual
  support = equal footing). O(V+E), pure, deterministic.
- DepthAnchor force: per-node target scalar — vertical mode rows
  (baseline at the anchor centroid, arrows up), radial mode rings
  (foundations center, outer boundary = edge of knowledge). The rank
  is computed, not simulated: pairwise up-forces can't give baselines.
- Mode projections: gravity x-only in vertical / dropped in radial;
  cluster pull x-only in vertical / tangential-only in radial; the
  anchor re-blend preserves user arrangement in the FREE dimension
  only (x / angle) — meaning owns the constrained axis, memory owns
  the free one.
- setPhysicsTuning reheats (alpha 1): toggling a mode re-forms the
  graph live and commits the new rest state; drag release is springy
  along the constrained axis (node returns toward its level/ring).
- Sweep on the real graph: strength 0.8 = 215/216 arrows correct with
  organic sag; 1.5 = crisp rows. 174 base nodes, max chain depth 7,
  zero cycles in current data. replay-layout --depth=<mode> bakes a
  mode into the ledger for rest-state experiments.
This commit is contained in:
amosblomqvist
2026-09-13 10:20:50 +03:00
parent 7ebdf065c8
commit 3a1c18b9de
5 changed files with 422 additions and 8 deletions
+18 -1
View File
@@ -7,10 +7,17 @@
* Reads ONLY the ledgers (nodes / edges / mappings / sources); writes ONLY
* data/graph/positions.json — no LLM calls, no extraction, no chat records.
*
* npx tsx scripts/replay-layout.ts
* npx tsx scripts/replay-layout.ts [--depth=vertical|radial]
*
* --depth regenerates the ledger WITH inference-depth anchoring (the
* playground mode baked into the rest state); omit it for the free layout.
*/
import { graphStore } from "../src/store/graph.ts";
import { detectCommunities } from "../src/core/clusters.ts";
// --depth=vertical|radial: bake the playground depth mode into the ledger.
const depthArg = process.argv.find((a) => a.startsWith("--depth="));
const depthMode: "off" | "vertical" | "radial" =
depthArg === "--depth=vertical" ? "vertical" : depthArg === "--depth=radial" ? "radial" : "off";
import { incrementalLayout } from "../src/core/layout.ts";
import type { GraphNode, NodeMapping, Position } from "../src/core/contracts.ts";
@@ -74,6 +81,16 @@ function main() {
positions = incrementalLayout(batchNodes, activeEdges, positions, {
// The same partition the real settle would see for this batch.
clusters: detectCommunities(batchNodes, activeEdges).communities,
...(depthMode === "off"
? {}
: {
depthAnchor: {
mode: depthMode,
strength: 0.8,
spacing: 60,
...(depthMode === "radial" ? { baseRadius: 220 } : {}),
},
}),
});
for (const [id, prev] of before) {
const now = positions.get(id)!;