Skip to content

T-4RB7-capability-graph-tree-output

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

sdlc capability graph prints a flat tab-separated table, so the containment tree is hard to read. Add an indented tree view with a depth limit, and a way to list capabilities that sit outside the tree. Unnested capabilities are normal, so they are reported as information, not as a warning.

solutions/ontological/lib/model/entities/capability/ops/graph.ts defines the op (path ['capability', 'graph']). renderGraph prints one tab-separated row per node: id, parent, kind, audience, state, then the title. A ghost node gets a (ghost) title prefix. A node kept only as an ancestor by --audience gets a trailing ·. A footer line prints counts (capabilities= or shown=N of capabilities=M, ghosts=, related-edges=, wikilink-edges=, warnings=), then one warning: ... line per warning. The only flag is --audience user|system (filterByAudience). The render always returns exit code 0.

Run on this repo it prints 127 capabilities, and 51 of them have no parent and are not kind: system. Nesting is not enforced; the footer and the warnings say nothing about them.

Callers do not parse the table. The skills capability-map and capability-review (solutions/ontological/plugin/plugins/sdlc/skills/) call capability graph --output json. The dashboard serves buildCapabilityGraph as GET /api/capabilities/graph. The only tests of the text render are in ops/graph.test.ts: the footer test in describe('product and audience filter') asserts the capabilities=3 and shown=2 of capabilities=3 prefixes.

Three new flags on sdlc capability graph:

  • --tree prints the containment tree, one capability per line, indented two spaces per level, children ordered by id. Each line is the id, a space, then the title, plus the kind and audience in brackets when set. Ghost and · markers keep their current meaning.
  • --depth N (positive integer) limits --tree to N levels, roots being level 1. It requires --tree. A node with hidden children shows (+K), K being the count of hidden descendants.
  • --unnested lists capabilities with no parent_key and a kind other than system, one per line, under a heading that says this is information. It never adds to warnings= and never changes the exit code.

Default: the table stays the default. The table is the stable surface other tools and scripts may read, and --output json is unaffected either way. The new views are opt-in, so nothing that runs today changes. --tree and --unnested may be combined; --unnested prints after the tree. --audience composes with both.

  1. Add tree (boolean), depth (positive int, optional) and unnested (boolean) to the input schema in graph.ts, each with a .describe(). Reject --depth without --tree with a clear error.
  2. Write a pure renderTree(graph, depth) that groups graph.nodes by parent and walks from the nodes with no parent (ghosts included, since they are parents of real nodes). The parent chains are acyclic after cutParentCycles, so the walk terminates.
  3. Write a pure unnestedOf(graph) that returns non-ghost nodes with parent === null and kind !== 'system', and a renderer for it.
  4. renderGraph is the shared cli.render. Pass the new flags to it so it picks table, tree, or the unnested section. Keep the table path byte-identical when no new flag is set.
  5. The unnested list is output only. Do not push to warnings and do not change the return value 0. JSON output does not carry it: CapabilityGraph stays unchanged, since the unnested set is derivable from nodes.
  6. Update the graph row in the Operations table of solutions/ontological/lib/model/entities/capability/definition.md (L203): the signature currently reads (--audience user|system).
  7. Rebuild the committed plugin trees, since .cursor-plugin/packs/sdlc/cli/sdlc.js bundles this op and the drift guard checks it.
AreaNote
solutions/ontological/lib/model/entities/capability/ops/graph.tsInput schema, renderGraph, new tree and unnested renderers
solutions/ontological/lib/model/entities/capability/ops/graph.test.tsNew tests beside the footer and audience tests
solutions/ontological/lib/model/entities/capability/definition.mdOperations table row for graph
solutions/ontological/.cursor-plugin/packs/sdlc/Committed bundle that includes the op; rebuilt, not hand-edited
  • AC-1: sdlc capability graph with no new flag prints output identical to today’s, and the existing graph.test.ts tests pass unchanged.
  • AC-2: --tree prints one line per capability, indented two spaces per depth level, with children in id order, and ghost nodes marked (ghost).
  • AC-3: --tree --depth 2 prints only levels 1 and 2, and a node with hidden children shows (+K) with the correct count. --depth without --tree exits non-zero with a message.
  • AC-4: --unnested lists exactly the non-ghost capabilities with no parent and a kind other than system (ungraded kinds count as not system), and prints a heading marking the list as information.
  • AC-5: --unnested leaves the warnings= count unchanged, adds no warning: line, and the exit code is 0 even when the list is non-empty.
  • AC-6: --audience user --tree keeps ancestors marked ·, as the table does.
  • AC-7: Tests in graph.test.ts cover AC-2 to AC-6.
  • AC-8: The definition.md Operations row lists the new flags, and moon run root:guard-sdlc-plugin-drift (the CI step that checks the committed plugin trees against the vault; it runs the agent-pants check line below) passes.
Terminal window
bun test solutions/ontological/lib/model/entities/capability/ops/graph.test.ts
bun run solutions/ontological/cli/sdlc.ts capability graph | head
bun run solutions/ontological/cli/sdlc.ts capability graph --tree --depth 2 | head -20
bun run solutions/ontological/cli/sdlc.ts capability graph --unnested; echo "exit=$?"
bun run solutions/ontological/cli/sdlc.ts capability graph --depth 2; echo "exit=$?"
cargo run -q -p agent-pants -- build --yes --config solutions/ontological/agent-pants.yaml
cargo run -q -p agent-pants -- check --config solutions/ontological/agent-pants.yaml
moon run root:guard-sdlc-plugin-drift
bun run solutions/ontological/cli/sdlc.ts docs generate --check

docs generate --check should report no change: its artifacts come from entities, the config schema and site reference pages. If the site reference pages list op flags, regenerate them with docs generate and commit the result.

  • Enforcing nesting or turning unnested capabilities into a warning or gate finding.
  • Changing the JSON output shape or the dashboard GET /api/capabilities/graph endpoint.
  • Changing the default output from the table to the tree.
  • Reparenting any capability that --unnested lists.
  • none

← Back to Tasks