Command Center: a walking skeleton that loads the Truss provider - #13
Open
unional wants to merge 21 commits into
Open
Command Center: a walking skeleton that loads the Truss provider#13unional wants to merge 21 commits into
unional wants to merge 21 commits into
Conversation
No SDD corpus existed in this repo, so kicking off cyber-truss#6 starts on the backfill path: scaffold the project spec before the change request can be explored. One package, `packages/command-center`, with the contract reached through a subpath export — providers in cyberplace and cyber-sdd bind to the contract, and a separate contract package stays reversible until the first external provider ships. Spec is colocated (an npm package excludes `.agents/` from its tarball), capability-first, in intent mode: the project has no source yet, so the capabilities come from the initiative rather than from a tree. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YFKdPvMquYJP5DkcJQ4T5t
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YFKdPvMquYJP5DkcJQ4T5t
The contract is segregated rather than monolithic: every provider implements a small handshake and then declares which of four capability contracts — references, state, views, actions — it implements. Each carries its own version, so refusing an incompatible `actions` contract does not refuse a provider that only ever reported state. Providers are out-of-process. That is what makes the issue's restart criterion testable: the host holds snapshots it never promotes on its own, and an action whose outcome is unknown is never sent a second time. 19 scenarios across five use-case groups. check-suite, check-spec-state and check-spec-structure are clean. Not yet frozen — the spec gate has not run. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YFKdPvMquYJP5DkcJQ4T5t
…sions Self-alignment against the three lens bars, loaded after the spec-judge blocked on a governance pre-flight. The Oracle bar grades the actor enumeration both ways, and the integration author was listed as an actor while carrying no use case. They implement the provider; they never call the host. Reclassified as a stakeholder, which also gives the per-contract version refusal the stakeholder it protects. The Architect and Builder bars both require every stated extension to be a path in the drawn graph. Unloading during an action was claimed as a dispatchAction divergence while its path lives in the unload sub-graph — moved to the entry point that reaches it. Concept tag restated the folder it sits in, so it carried no cross-cutting information; it is lifecycle and provenance. Scenario map named one edge two ways, hiding permutation coverage as two edges. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YFKdPvMquYJP5DkcJQ4T5t
The judge cleared pre-flight and returned CHANGE with six findings. It also confirmed the load-bearing question: per-contract refusal is not the rejected capability-negotiation scheme wearing a new name, because the host omits an incompatible contract rather than adapting to it. refreshState guarded nothing while dispatchAction guarded the identical question, so calling it for a contract the binding does not carry was undefined — which is exactly where the partial-bind path had to be specified. The references contract was declared and never specified, and the issue's fourth acceptance bullet needs it: a fleet completion, an SDD gate result and a Truss obligation are only distinguishable if a reference can be followed into the domain that owns it. It now has a use case, a graph and four scenarios, and the provenance scenario carries all three domains rather than two. The views contract is cut. Nothing needed it, and whether renderable views are a distinct area from state is a question the TUI answers; declaring it now was a commitment made before it had to be. Two scenarios described their trigger in vocabulary belonging to the views and decisions nodes, neither of which exists. The assertions were this node's; the framing was not. 24 scenarios, six use-case groups, six control-flow graphs. All checks green. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YFKdPvMquYJP5DkcJQ4T5t
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YFKdPvMquYJP5DkcJQ4T5t
Ownership governance makes produced-by the conductor's write, recorded when a role produces. The spec-producer ran inline and it went unrecorded. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YFKdPvMquYJP5DkcJQ4T5t
A fresh cold judge, given no memory of the earlier rounds, returned change and CONVERGED: no. Oracle passed — the ISP split, per-contract refusal, the views cut and the separation from the Truss controller/probe contract all held up under independent re-derivation, and all four acceptance bullets are covered. Two defects were mine from the start. A Then asserted that the host stores no approval of its own, which by design is the absence of a store that never exists, so no verifier can settle it; it now asserts what is observable, that a decision keeps the state its provider last reported until that provider says otherwise — the stronger form of the same claim. A barred scenario reached for an introspection surface declared nowhere; it now calls dispatchAction, the way its refreshState counterpart already did. Two were regressions from the previous round editing past what a finding named: the glossary still carried the cut views contract, and a reworded extension claimed a divergence the graph has no path to. The diff is scope-audited: every changed line traces to a named finding, and the judge's non-blocking note on scenario ordering is left alone on purpose. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YFKdPvMquYJP5DkcJQ4T5t
Third cold judge: Oracle and Architect pass, 23 of 24 scenarios pass, one blocking defect — in the scenario the previous round rewrote. The rewrite made the assertion observable but never said how the provider answered the dispatched action, so two implementers could fixture a promptly accepting provider and a never-settling one and both would pass. The guarantee was sound; the coverage claim was not. One Given step names the branch, the way the sibling scenario already did. The forbidden-combinations prose listed two of the three guards the graph carries; resolveReference's was covered by CFG and scenario but unnamed there. The observation that no scenario exercises cross-provider fault isolation is routed to the plan rather than acted on — it is a strategist-owned question about a claim in the actors table, not a defect in a scenario. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YFKdPvMquYJP5DkcJQ4T5t
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YFKdPvMquYJP5DkcJQ4T5t
Caught in review: the Action graph's `domain returns?` node carried three outgoing edges, one of them "provider restarted first" — which is the domain failing to return, not an answer to the question asked. It merged two decisions: did the provider stay up, and if it answered, did it accept. Split into the liveness decision and the accept decision, so the unknown outcome now hangs off the question it actually answers. Sweeping the rule rather than the instance found two more across the six graphs. The Bind graph asked "per-contract version compatible?" and answered it three ways; it now asks how many declared contracts are compatible. The Discover graph hung labelled branches off an action node with no decision in between. Scenario-map edge names follow the corrected graphs. Also adds the two cross-provider fault-isolation scenarios, and narrows the claim they earn: isolation holds against an incompatible contract within a domain and against any failure across domains, and stops at the process, since a provider's contracts share one. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YFKdPvMquYJP5DkcJQ4T5t
An artifact-set is a kind of thing the repo intends to hold, not a list of files that happen to exist. Declaring one is an act of intent, so a set may be empty: the intent is stated and a later change discharges it. That is how a repo adopts a standard it does not yet meet, and it is the backfill case. Emptiness is therefore an obligation on the set, never a strain on a connection — a connection is simply not evaluable until both ends are populated. Connections are undirected; `between` is a pair, never a source and a target. Two sets are declared and empty: cyber-truss has no spec, and command-center has no code. Both are obligations at different stages. Drafting this caught a flaw in its own first draft: a "repository structure" set globbed 2297 files, which is not a kind of thing. Narrowed to the directories AGENTS.md's Layout table actually names — at which point the missing docs/adr/ falls straight out as a conformance strain. Location and schema are backlog C6 and remain open; .truss/lattice.toml is a proposal, not a settled convention. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YFKdPvMquYJP5DkcJQ4T5t
Two stories fall out of the declared-lattice config: a CLI command that writes and validates a declaration, and a skill that decides what belongs in one. Plus a real conformance strain the config surfaced — AGENTS.md documents a docs/adr/ that does not exist. Recorded, not filed. Filing is a separate act and needs permission. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YFKdPvMquYJP5DkcJQ4T5t
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YFKdPvMquYJP5DkcJQ4T5t
…nders it The walking skeleton for cyber-truss#6. The contract had 26 judged scenarios and no implementation, so nothing had tested whether it survives a real process. Now it does. The host discovers providers from a repo-level declaration, spawns each as its own process, reads a handshake, and checks compatibility per capability contract rather than per provider — so a provider whose actions contract is unsupported still binds for state. It holds snapshots it never promotes: when a provider's process is gone the last snapshot is kept and marked stale, never deleted and never quietly made live again. The Truss provider reads two real files, the lattice declaration and the backlog the model already calls an obligation ledger, and invents nothing. Its one simulated field is a connection's evaluation status, flagged as a proposal because held/strained/unevaluated is a backlog item rather than settled model. Three views: the sets and what must hold between them, the obligations, and a force-directed graph of the lattice in the terminal. Edges are drawn undirected, since the glossary bans giving a connection a direction. Building it corrected the contract twice. `--view` exists because a TUI that only renders under a TTY cannot be tested; and the empty case had to be a first-class outcome rather than an error, which is what the declaration's whole backfill story depends on. Written implementation-first rather than test-first, against this repo's own TDD rule. Tests were added after and cover the branches the spec names: partial bind, refusal, stale-never-live, and a contract the binding does not carry. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YFKdPvMquYJP5DkcJQ4T5t
…on proved Verified running in a herdr pane through cyber-mux: all three views and refresh driven remotely, reading real repository state. The contract survived a real out-of-process provider unchanged. Actions, references and the no-replay path are still unexercised, so they should not freeze on the strength of the spec alone either. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YFKdPvMquYJP5DkcJQ4T5t
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YFKdPvMquYJP5DkcJQ4T5t
🦋 Changeset detectedLatest commit: a83a3d5 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
The single 26-scenario node could not reach either gate. Freeze is per file, so freezing it whole would fail the impl gate on actions and references that have no implementation, while not freezing left the landed code ungated. The partition is what unblocks both, and it follows the iteration boundaries rather than a guess at sizing. provider-binding, 16 scenarios: discovery, load, per-contract compatibility, snapshot freshness, release. Every scenario has running code behind it, so this is the half that can freeze and then be judged. provider-exchange, 10 scenarios: references, actions, and the guarantee that an action whose outcome is unknown is never sent twice. Unbuilt, and it stays in draft until a slice exercises it. One scenario changed home. An action in flight when its provider is unloaded now sits under dispatchAction rather than unloadIntegration, because its outcome is a property of the action rather than of the release — leaving it where it was would have split a single decision across two nodes. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YFKdPvMquYJP5DkcJQ4T5t
The lattice documented two orthogonal axes and instantiated one: all nine
sets were governance targets, so the file demonstrated nothing about the
axis the model leans on hardest. `artifact-sets.mdx` names `{code, test}` as
the canonical unit of change and says outright that code and tests are not
one governance target, "because the criteria governing a test differ from
those governing the code it exercises" -- while this file declared them as
exactly that and nothing else.
Six unit-of-change sets now stand beside the six governance targets, and
two of them overlap the governance targets on the same files, which is the
orthogonality claim made concrete rather than asserted: `cyber-truss
implementation` asks whether a change landed everywhere it had to, while
`cyber-truss code` and `cyber-truss tests` ask whether each conforms, cold.
Held `{code, test}` as one set deliberately, with the reason in the file:
split them and the connection has to be traversed from an end, which is
test-first or code-first, and one set has no traversal order.
Two corrections fall out of the pass. `command-center code` was commented
"declared and unpopulated" and has been populated for some time -- renamed
to `command-center implementation`, since the glob covers its tests too.
And `plugin components` is declared empty against a `files` list that
already promises `skills`, so the emptiness is now a reported obligation
rather than a silent inconsistency.
Verified against the provider: 6 unit-of-change and 6 governance-target
sets parse, the two empty sets raise set obligations, and their connections
report `evaluable: false` naming the unpopulated end.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
An installed agent plugin is a copy of a source checkout rather than an npm install, so its directory has no reliable node_modules and a CLI with external dependencies cannot run from it. dist/cli.js now inlines commander and runs with no node_modules present at all. The library entry keeps its dependencies external so a consumer that also uses commander shares one copy, and so the public .d.ts keeps referring to types it can actually resolve. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YFKdPvMquYJP5DkcJQ4T5t
Follows the model rename in #15: incompleteness, obligation, nonconformance. Each now names the defect, so none of the three reads as a property to be scored, and a strain is exactly one of them. The comments renamed here arrive with this branch, so they cannot travel in that PR — `.truss/lattice.toml` does not exist on main yet. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017kFn8pbYyGdDCxEr5SYmks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Opens the Command Center work for #6 and lands the first runnable slice.
Two things happen here. The extension contract is specified and judged, and a walking skeleton is built against it — deliberately in that order, and deliberately stopping short of a freeze.
Why it is not frozen
The contract reached 26 scenarios across three cold review passes with zero lines of implementation behind it. Nothing had tested whether it survives a real process, and freezing a contract before one has met it is the expensive mistake. So the slice was built first, and it corrected the contract twice:
Exercised: discovery, load, the handshake, per-contract version refusal and partial bind, refusing a contract a binding does not carry, snapshot freshness including stale-never-live, unload.
Not exercised, and therefore not frozen: actions, references, the no-replay path.
What runs
Three views — artifact-sets with what must hold between them, open obligations, and a force-directed graph of the lattice in the terminal. Verified running in a herdr pane opened with
cyber-mux open, driven remotely to confirm each view and refresh.The host understands none of what it renders: a snapshot is an opaque payload carrying its provenance and freshness, which is the claim the segregated contract makes.
The Truss provider
Reads two real files and invents nothing:
.truss/lattice.toml(also added here — what this repo declares it should hold) anddocs/backlog.md, which the lattice model already calls a hand-maintained obligation ledger. One field is simulated and flaggedproposal: true: a connection's evaluation status, becauseheld | strained | unevaluatedis a backlog item rather than settled model.Running it against this repo immediately surfaced a real conformance strain, filed as #12:
AGENTS.mddocuments adocs/adr/directory that does not exist, and the backlog cites an ADR inside it. Nothing checks that relation, so it does not score as unstrained — it scores as never checked, which is the case for the application existing.Also here
truss init) and A skill that decides what a repository should declare in its lattice #11 (the authoring skill). They are separate because writing a config is deterministic and deciding what a repository intends to hold is judgement — the split follows the model's own controller spectrum.Review notes
pnpm verifygreen: lint, build, typecheck, 39 tests, knip.private, so no changeset is owed.Refs #6