Skip to content

T-31MH-capability-graph-opens-folded

State: planning/draft · Impact: medium · Complexity: medium

The System page’s Capabilities source opens fully unfolded, which is too small to read at 127 capabilities. Open it folded to a default depth, and group the capabilities that have no parent so they read as a set and not as 51 stray top-level boxes. Nesting is a modelling aid, so an unnested capability is normal and must not look like a defect or imply a parent.

  • solutions/ontological/experiences/system-graph/app/pages/apps/system-graph/index.vue sets the fold after each load (about L599-602): setCollapsed(source.value === 'capabilities' ? new Set() : defaultCollapsed(t)). The comment above it says a capability corpus is “a few dozen nodes in a handful of trees, which reads better open than folded”. That is stale: docs/planning/capabilities/ holds 127 files.
  • Of those, 54 have parent_key: null. Three of the 54 are kind: system roots (C-0050, C-8FKM, C-HRNS). The other 51 are unnested and are not systems. A fourth kind: system capability, C-0001, has a parent (C-0055).
  • app/lib/collapse.ts already has buildTree, collapseToDepth, depthOfCollapse, maxDepth, expandTo, collapseAll, expandAll and defaultCollapsed. defaultCollapsed folds every container that is not a root, which equals collapseToDepth(tree, 1).
  • components/SystemGraphControls.vue has the Depth slider (id="sg-depth", update:depth), and reads the depth back with depthOfCollapse.
  • app/lib/capability-projection.ts (projectCapabilities) maps a capability to a SystemNode with parent and kind. Every node with no parent is a root in tree.roots, and app/lib/diagram.ts (L347-350) lays the roots out as the top level through layoutLevel(null, roots, ...). Nothing tells a system root from an unnested one.
  • tests/collapse.test.ts covers the fold helpers. The package’s test script is bun test tests (its tests import bun:test).
  • The Capabilities source opens folded to depth 1: each root is open and shows its direct children, everything below is folded. This is the existing defaultCollapsed result, so the page can use it for every source. Depth 1 is picked because the system roots then show their subsystems, which is the level the planning views are read at, while the 100-odd deeper capabilities stay behind a fold. Decision: depth 1. The constant lives in one place (CAPABILITY_DEFAULT_DEPTH), so a later change to 2 is a one-line edit.
  • Roots are split into two groups by a pure function: system roots (kind: system) and unnested capabilities (no parent, not kind: system). The diagram draws the unnested group inside a labelled “Unnested” band, drawn behind the boxes. The band is a drawing aid only. It is not a node, it has no id in the graph, and it adds no edge, so ids, edges, selection and URLs are unchanged.
  • The stale comment in index.vue is replaced with one that states the real count and the chosen depth.
  1. Add app/lib/capability-groups.ts with partitionRoots(tree: Tree): { systems: string[]; unnested: string[] }. A root whose node kind is system goes in systems; any other root goes in unnested. Order is payload order. Also export CAPABILITY_DEFAULT_DEPTH = 1 and defaultCapabilityFold(tree: Tree): Set<string> returning collapseToDepth(tree, 1).
  2. In index.vue, call defaultCapabilityFold(t) for the capabilities source, drop the new Set() branch, and rewrite the comment.
  3. In app/lib/diagram.ts, compute the bands inside layoutDiagram from partitionRoots, so layoutDiagram’s call sites (SystemGraphDiagram.vue, lib/derive.ts) keep their signature. Order the top-level roots systems first, then unnested. Add a field bands: DiagramBand[] to DiagramLayout, where DiagramBand = { label: string; x: number; y: number; width: number; height: number }. For the capabilities source it holds one band, labelled “Unnested”, whose rectangle is the bounding box of the placed unnested roots grown by PADDING; it is empty when there are no unnested roots or for other sources. SystemGraphDiagram.vue reads layout.bands and draws each as a non-interactive element behind the nodes, with its label, using only --d-* tokens.
  4. Add tests (bun:test) in tests/capability-groups.test.ts for the partition and the fold.
  5. Check by eye against sdf-serve that the page opens folded and the band appears.
AreaNote
solutions/ontological/experiences/system-graph/app/lib/capability-groups.tsNew pure partition and default fold
solutions/ontological/experiences/system-graph/app/pages/apps/system-graph/index.vueUse the default fold; fix the stale comment
solutions/ontological/experiences/system-graph/app/lib/diagram.tsTop-level order and band geometry
solutions/ontological/experiences/system-graph/app/components/SystemGraphDiagram.vueDraw the labelled band
solutions/ontological/experiences/system-graph/tests/Partition and fold tests
  • AC-1: partitionRoots puts every kind: system root in systems and every other root in unnested, and a node with a parent is in neither. A test builds a tree with both and asserts the two lists.
  • AC-2: defaultCapabilityFold equals collapseToDepth(tree, CAPABILITY_DEFAULT_DEPTH), and a test asserts that roots stay visible and their direct children are visible while deeper nodes are folded.
  • AC-3: Loading the Capabilities source opens at that fold with no collapsed value written to the URL, and the Depth slider reads 1.
  • AC-4: The graph payload is unchanged: node ids, parents and edges are identical with and without the band, asserted in a test over buildTree.
  • AC-5: The diagram shows a band labelled “Unnested” around the unnested roots and none around system roots; the band is not selectable and has no node id. A test over layoutDiagram asserts layout.bands has one entry that contains every unnested root’s box and no system root’s box.
  • AC-6: The comment in index.vue no longer says “a few dozen nodes”, and the new one states the depth and why.
  • AC-7: The band and its label use only --d-* design tokens.
  • CI is the typecheck. vue-tsc and nuxi typecheck are never run locally.
  • cd solutions/ontological/experiences/system-graph && bun test tests
  • cd solutions/ontological/desktop/src-nuxt && bun run test
  • cd solutions/ontological/desktop/src-nuxt && bun run generate
  • Run against sdf-serve, the headless server from open PR #2546, open the System page, choose the Capabilities source, and check the fold depth and the Unnested band. Every /api/* request over HTTP needs the platform token; a browser without it must be given one in Settings.
  • Giving unnested capabilities parents. That is the containment pass over the corpus in the milestone, and it is a modelling task, not a view task.
  • The sdlc capability graph tree output.
  • Grouping in the matrix, chord, table or outline views.
  • Treating an unnested capability as a finding anywhere.
  • none

← Back to Tasks