capability-graph — One capability graph, two projections, and no second inventory of what this repository can do
One typed, deterministic capability graph composes the registries that already own their facts — it does not replace them — and adds typed relations between them that are inferred where a stable convention exists and declared where it does not. Both the running server and the published site render from that one graph: the static projection is the snapshot, the runtime projection is the snapshot plus live state. Navigation, overview, detail, search, the relation view and the coverage matrix are all derived, a dangling relation fails generation rather than rendering as a missing section, and a capability that exists with no documentation, no test or no enforcement is visible as such rather than indistinguishable from one that has them.
ACTIVE 6 of 31 issues done
- ready
- 8
- blocked
- 16
- active
- 1
- verify
- 0
- done
- 6
- cancelled
- 0
Problem
The executable already declares a capability once and projects it into MCP, HTTP, OpenAPI, Swagger, the command line and now the Cockpit (ADR 2, ADR 4, ADR 12). Everything the repository knows that is not a capability is outside that model: the skills, the rules, the decisions, the documents, the use cases, the examples, the benchmarks and the deployment are each authoritative in their own directory and related to the capabilities only in the reader's head. The Cockpit, the landing page, the navigation, the site indexes and the search each therefore assemble their own view of what exists, and every new subsystem costs an edit in each of them. The failure this produces is not visual: a capability with no documentation, no test and no enforcement looks exactly like one that has all three, because nothing resolves the relation.
Outcome
One typed, deterministic capability graph composes the registries that already own their facts — it does not replace them — and adds typed relations between them that are inferred where a stable convention exists and declared where it does not. Both the running server and the published site render from that one graph: the static projection is the snapshot, the runtime projection is the snapshot plus live state. Navigation, overview, detail, search, the relation view and the coverage matrix are all derived, a dangling relation fails generation rather than rendering as a missing section, and a capability that exists with no documentation, no test or no enforcement is visible as such rather than indistinguishable from one that has them.
Current state
The Cockpit is a projection of the registry and the index with its own graph derivation (ADR 12, feature/ai-artifacts). Web surfaces are being made discoverable from their producers (ADR 13, in flight). Skills, rules, decisions, use cases, documents, benchmarks and — after the deployment milestone — deployments are each indexed as kinds, and none of them is related to a capability by anything a machine reads.
Desired state
A contributor registers a capability in the subsystem that owns it, runs generation, and the Cockpit, the navigation, the search, the graph, the documentation index, the coverage matrix, the static snapshot and the runtime metadata all change. Nothing else has to be edited, and CI refuses a change that leaves any of them stale.
Scope
- apps/majordomus-cli/src/graph.rs
- apps/majordomus-cli/src/capability
- apps/majordomus-cli/src/cockpit
- apps/majordomus-cli/src/generate.rs
- share/cockpit
- site
- docs
- .ai/repo/rules/project
- .ai/repo/adrs
- .ai/repo/skills
- test/cases
- .github/workflows
Out of scope
- A hand-maintained menu, page list or capability inventory in any format, including a YAML one
- Replacing any registry that is already authoritative for what it owns
- A client-side application framework; the Cockpit stays server-rendered with progressive enhancement
- A layout description language with coordinates, columns or component trees as data
- An external search service for a graph this size
- Mutating controls in the Cockpit beyond what the existing authorisation architecture already makes safe
Acceptance criteria
- The graph composes the existing authoritative registries and introduces no second store of what they own
- Every node has a stable identity that survives a title change, identities are unique, and generation is deterministic over identical inputs
- Relations are typed, inferred only from stable conventions, and a relation that does not resolve fails generation with a message naming both ends
- The static and the runtime projections come from the same graph, and their representations are equal once runtime-only fields are removed
- A surface that requires a running backend is not offered as usable in the static projection, and that decision comes from availability metadata rather than from any check on the page's location
- Navigation, overview, detail, search and the coverage matrix are all derived, and adding a capability requires no edit to any of them
- The coverage matrix reports documentation, tests, enforcement and benchmarks only where an artifact or a resolved relation supports the claim
- The Cockpit is usable on a narrow phone viewport and its core information is present before JavaScript runs
- Each enforcement rule introduced here has a check that CI executes, proved by a negative test
Validation
- bash test/run.sh
- bin/majordomus doctor
- cargo test --workspace --all-features
- majordomus generate --check
- scripts/cockpit-probe
Evidence required
- federated_graph
- stable_identity
- relations_resolve
- dual_projection_parity
- derived_surfaces
- coverage_matrix
- mobile_and_baseline
- rules_enforced
- extensibility_proved
Risks
- Composing eight registries into one model invites a ninth store that holds the composition. The aggregate is derived on every build and committed only as a generated artifact; anything hand-edited in it is a defect.
- Inference from names is fragile the day two subsystems name something the same way. Inference is allowed only over stable identities, and ambiguity is an explicit declaration rather than a heuristic.
- A coverage matrix that reports what metadata claims rather than what exists is worse than no matrix, because it looks like evidence. Every green cell must resolve to an artifact.
- The Cockpit and the web surfaces are being built on other branches right now. This milestone extends them and must land after them rather than beside them.
- A graph view is the part everyone wants to build first and the part that matters least if the model underneath is wrong. The model comes first, and the visualisation reads it.
Issues
Each one is an execution contract with its own acceptance criteria, validation command and required evidence. The wave column is the layer of the dependency graph it sits in.
| issue | status | wave | depends on | title |
|---|---|---|---|---|
| I1001 | DONE | 0 | — | One typed graph composes the registries that already own their facts |
| I1002 | DONE | 1 | I1001 | Relations are typed, inferred from stable identities, and never dangle |
| I1003 | DONE | 1 | I1001 | Skills, rules, decisions, documents, use cases and benchmarks enter the graph as themselves |
| I1004 | DONE | 1 | I1001 | Availability and visibility are metadata, not conditions in a template |
| I1005 | DONE | 2 | I1002, I1004 | The graph is a generated artifact with a schema and no unsafe field |
| I1006 | READY | 3 | I1005 | The runtime reads the graph through the registry, like every other read |
| I1007 | READY | 3 | I1005 | The site renders the same graph, generated once |
| I1008 | BLOCKED | 4 | I1006, I1007 | The two projections are proved to be the same model |
| I1009 | READY | 3 | I1004, I1005 | Every menu in this repository comes from one derivation |
| I1010 | READY | 3 | I1005 | The overview is what exists, counted rather than claimed |
| I1011 | READY | 2 | I1002 | The coverage matrix reports evidence, and never a green cell without one |
| I1012 | READY | 3 | I1005 | One detail view, assembled from what a node actually has |
| I1013 | BLOCKED | 4 | I1007 | Search over the graph, generated, with nothing to run |
| I1014 | BLOCKED | 4 | I1002, I1007 | The relation view reads the graph and holds no data of its own |
| I1015 | ACTIVE | 2 | I1004 | A surface with no server behind it is not offered as if it had one |
| I1016 | BLOCKED | 4 | I1009 | The Cockpit works on a phone and before JavaScript runs |
| I1017 | BLOCKED | 4 | I1006 | Live state is an overlay that degrades to nothing |
| I1018 | BLOCKED | 3 | I1011, I1015 | The surface rules exist as checks, proved by making them fail |
| I1019 | BLOCKED | 4 | I1018 | One skill runs the whole loop, and is itself in the graph |
| I1020 | BLOCKED | 4 | I1002, I1007 | The graph's health is answered by the doctor that already answers everything else |
| I1021 | BLOCKED | 5 | I1014, I1015, I1016 | A browser proves the interface, at the widths people use |
| I1022 | BLOCKED | 4 | I1007 | Drift in any derived surface fails the build |
| I1023 | READY | 3 | I1005 | Nothing private reaches a published page |
| I1024 | BLOCKED | 5 | I1014 | The interface has a weight, and it is measured |
| I1025 | DONE | 1 | I1001 | The federated model and its two projections are an architecture decision |
| I1026 | BLOCKED | 4 | I1012, I1025 | The architecture explains itself, from inside itself |
| I1027 | BLOCKED | 5 | I1018, I1019 | A synthetic capability proves the architecture instead of describing it |
| I1028 | BLOCKED | 4 | I1009 | The inventories this replaces are removed, not left running beside it |
| I1029 | BLOCKED | 6 | I1021, I1022, I1023 | The gate refuses what would silently break the interface |
| I1030 | BLOCKED | 7 | I1010, I1013, I1017, I1020, I1024, I1026, I1027, I1028, I1029 | The milestone is accepted on its evidence |
| I1031 | READY | 0 | — | A served surface says which checkout it is serving |
The graph
flowchart LR
I1001["I1001<br/>One typed graph composes the registries that already own their facts"]:::done
I1002["I1002<br/>Relations are typed, inferred from stable identities, and never dangle"]:::done
I1003["I1003<br/>Skills, rules, decisions, documents, use cases and benchmarks enter the graph as themselves"]:::done
I1004["I1004<br/>Availability and visibility are metadata, not conditions in a template"]:::done
I1005["I1005<br/>The graph is a generated artifact with a schema and no unsafe field"]:::done
I1006["I1006<br/>The runtime reads the graph through the registry, like every other read"]:::ready
I1007["I1007<br/>The site renders the same graph, generated once"]:::ready
I1008["I1008<br/>The two projections are proved to be the same model"]:::blocked
I1009["I1009<br/>Every menu in this repository comes from one derivation"]:::ready
I1010["I1010<br/>The overview is what exists, counted rather than claimed"]:::ready
I1011["I1011<br/>The coverage matrix reports evidence, and never a green cell without one"]:::ready
I1012["I1012<br/>One detail view, assembled from what a node actually has"]:::ready
I1013["I1013<br/>Search over the graph, generated, with nothing to run"]:::blocked
I1014["I1014<br/>The relation view reads the graph and holds no data of its own"]:::blocked
I1015["I1015<br/>A surface with no server behind it is not offered as if it had one"]:::active
I1016["I1016<br/>The Cockpit works on a phone and before JavaScript runs"]:::blocked
I1017["I1017<br/>Live state is an overlay that degrades to nothing"]:::blocked
I1018["I1018<br/>The surface rules exist as checks, proved by making them fail"]:::blocked
I1019["I1019<br/>One skill runs the whole loop, and is itself in the graph"]:::blocked
I1020["I1020<br/>The graph's health is answered by the doctor that already answers everything else"]:::blocked
I1021["I1021<br/>A browser proves the interface, at the widths people use"]:::blocked
I1022["I1022<br/>Drift in any derived surface fails the build"]:::blocked
I1023["I1023<br/>Nothing private reaches a published page"]:::ready
I1024["I1024<br/>The interface has a weight, and it is measured"]:::blocked
I1025["I1025<br/>The federated model and its two projections are an architecture decision"]:::done
I1026["I1026<br/>The architecture explains itself, from inside itself"]:::blocked
I1027["I1027<br/>A synthetic capability proves the architecture instead of describing it"]:::blocked
I1028["I1028<br/>The inventories this replaces are removed, not left running beside it"]:::blocked
I1029["I1029<br/>The gate refuses what would silently break the interface"]:::blocked
I1030["I1030<br/>The milestone is accepted on its evidence"]:::blocked
I1031["I1031<br/>A served surface says which checkout it is serving"]:::ready
I1001 --> I1002
I1001 --> I1003
I1001 --> I1004
I1001 --> I1025
I1002 --> I1005
I1002 --> I1011
I1002 --> I1014
I1002 --> I1020
I1004 --> I1005
I1004 --> I1009
I1004 --> I1015
I1005 --> I1006
I1005 --> I1007
I1005 --> I1009
I1005 --> I1010
I1005 --> I1012
I1005 --> I1023
I1006 --> I1008
I1006 --> I1017
I1007 --> I1008
I1007 --> I1013
I1007 --> I1014
I1007 --> I1020
I1007 --> I1022
I1009 --> I1016
I1009 --> I1028
I1010 --> I1030
I1011 --> I1018
I1012 --> I1026
I1013 --> I1030
I1014 --> I1021
I1014 --> I1024
I1015 --> I1018
I1015 --> I1021
I1016 --> I1021
I1017 --> I1030
I1018 --> I1019
I1018 --> I1027
I1019 --> I1027
I1020 --> I1030
I1021 --> I1029
I1022 --> I1029
I1023 --> I1029
I1024 --> I1030
I1025 --> I1026
I1026 --> I1030
I1027 --> I1030
I1028 --> I1030
I1029 --> I1030
classDef done stroke:#16a34a,fill:#052e16,stroke-width:2px
classDef active stroke:#2563eb,fill:#eff6ff,stroke-width:2px
classDef verify stroke:#7c3aed,fill:#f5f3ff,stroke-width:2px
classDef ready stroke:#0891b2,fill:#ecfeff,stroke-width:2px
classDef blocked stroke:#b45309,fill:#fffbeb,stroke-width:2px
classDef cancelled stroke:#6b7280,fill:#f9fafb,stroke-width:2pxCanonical record: .ai/repo/project/milestones/capability-graph.yaml. Read it back with majordomus plan show capability-graph.