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.
Proposed
Section titled “Proposed”Three new flags on sdlc capability graph:
--treeprints 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--treeto N levels, roots being level 1. It requires--tree. A node with hidden children shows(+K), K being the count of hidden descendants.--unnestedlists capabilities with noparent_keyand a kind other thansystem, one per line, under a heading that says this is information. It never adds towarnings=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.
Approach
Section titled “Approach”- Add
tree(boolean),depth(positive int, optional) andunnested(boolean) to the input schema ingraph.ts, each with a.describe(). Reject--depthwithout--treewith a clear error. - Write a pure
renderTree(graph, depth)that groupsgraph.nodesbyparentand walks from the nodes with no parent (ghosts included, since they are parents of real nodes). The parent chains are acyclic aftercutParentCycles, so the walk terminates. - Write a pure
unnestedOf(graph)that returns non-ghost nodes withparent === nullandkind !== 'system', and a renderer for it. renderGraphis the sharedcli.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.- The
unnestedlist is output only. Do not push towarningsand do not change the return value0. JSON output does not carry it:CapabilityGraphstays unchanged, since the unnested set is derivable fromnodes. - Update the
graphrow in the Operations table ofsolutions/ontological/lib/model/entities/capability/definition.md(L203): the signature currently reads(--audience user|system). - Rebuild the committed plugin trees, since
.cursor-plugin/packs/sdlc/cli/sdlc.jsbundles this op and the drift guard checks it.
| Area | Note |
|---|---|
solutions/ontological/lib/model/entities/capability/ops/graph.ts | Input schema, renderGraph, new tree and unnested renderers |
solutions/ontological/lib/model/entities/capability/ops/graph.test.ts | New tests beside the footer and audience tests |
solutions/ontological/lib/model/entities/capability/definition.md | Operations table row for graph |
solutions/ontological/.cursor-plugin/packs/sdlc/ | Committed bundle that includes the op; rebuilt, not hand-edited |
Acceptance criteria
Section titled “Acceptance criteria”- AC-1:
sdlc capability graphwith no new flag prints output identical to today’s, and the existinggraph.test.tstests pass unchanged. - AC-2:
--treeprints one line per capability, indented two spaces per depth level, with children in id order, and ghost nodes marked(ghost). - AC-3:
--tree --depth 2prints only levels 1 and 2, and a node with hidden children shows(+K)with the correct count.--depthwithout--treeexits non-zero with a message. - AC-4:
--unnestedlists exactly the non-ghost capabilities with no parent and a kind other thansystem(ungraded kinds count as notsystem), and prints a heading marking the list as information. - AC-5:
--unnestedleaves thewarnings=count unchanged, adds nowarning:line, and the exit code is 0 even when the list is non-empty. - AC-6:
--audience user --treekeeps ancestors marked·, as the table does. - AC-7: Tests in
graph.test.tscover AC-2 to AC-6. - AC-8: The
definition.mdOperations row lists the new flags, andmoon run root:guard-sdlc-plugin-drift(the CI step that checks the committed plugin trees against the vault; it runs theagent-pants checkline below) passes.
Verification
Section titled “Verification”bun test solutions/ontological/lib/model/entities/capability/ops/graph.test.tsbun run solutions/ontological/cli/sdlc.ts capability graph | headbun run solutions/ontological/cli/sdlc.ts capability graph --tree --depth 2 | head -20bun 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.yamlcargo run -q -p agent-pants -- check --config solutions/ontological/agent-pants.yamlmoon run root:guard-sdlc-plugin-driftbun run solutions/ontological/cli/sdlc.ts docs generate --checkdocs 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.
Out of scope
Section titled “Out of scope”- Enforcing nesting or turning unnested capabilities into a warning or gate finding.
- Changing the JSON output shape or the dashboard
GET /api/capabilities/graphendpoint. - Changing the default output from the table to the tree.
- Reparenting any capability that
--unnestedlists.
Dependencies
Section titled “Dependencies”- none