Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
e205e28
docs(sdd): bootstrap the Command Center project spec for github-6
unional Sep 8, 2026
cc5a24e
docs(sdd): note ADR-0034 colocation as parallel, uncoupled work
unional Sep 8, 2026
5371809
docs(sdd): draft the extension-contract node spec and suite for github-6
unional Sep 8, 2026
34a7856
docs(sdd): close the actor enumeration and align the CFG to its exten…
unional Sep 8, 2026
49f0387
docs(sdd): answer the spec-judge findings on the extension contract
unional Sep 8, 2026
49145cd
docs(sdd): hold the grill loop pending user re-plan
unional Sep 8, 2026
6935d02
docs(sdd): record produced-by, the conductor's field at production
unional Sep 8, 2026
a6234fe
fix(sdd): answer the redrive findings within the scope each names
unional Sep 8, 2026
bd4b055
fix(sdd): anchor the decision scenario to the edge its map row claims
unional Sep 8, 2026
ea0d532
docs(sdd): route the fault-isolation observation to the plan
unional Sep 8, 2026
cdb3ed9
fix(sdd): make every decision node's edges answer its own question
unional Sep 8, 2026
2334a52
feat(lattice): declare this repository's artifact-sets and connections
unional Sep 8, 2026
5874e39
docs(sdd): record the lattice-authoring follow-ups in the ledger
unional Sep 8, 2026
810f713
docs(sdd): record the filed follow-up issue numbers
unional Sep 8, 2026
0a46c8c
feat(command-center): run a host that loads the Truss provider and re…
unional Sep 8, 2026
720d3a6
docs(command-center): add run instructions and record what the skelet…
unional Sep 8, 2026
18ac558
chore: lock the command-center workspace package
unional Sep 8, 2026
e2902fd
refactor(sdd): partition the contract into binding and exchange
unional Sep 8, 2026
c35f57c
feat(lattice): declare the unit-of-change axis
unional Sep 10, 2026
7394623
feat: bundle the CLI's dependencies into dist/cli.js
unional Sep 11, 2026
a83a3d5
docs(lattice): name the strain kinds for the defect, not the property
unional Sep 12, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .agents/plans/6-command-center-contract.log.jsonl
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{"seq": 1, "ts": "2026-09-08T05:10:39Z", "handle": "unional", "kind": "correction", "correction-kind": "judge-iteration", "cause": "governance-preflight-missing", "cause-candidate": true, "summary": "inline spec-producer declared only the placement and format bars; the three lens bars plus lifecycle and gate-validation were never loaded, so the judge blocked before grading content"}
{"seq": 2, "ts": "2026-09-08T05:10:39Z", "handle": "unional", "kind": "correction", "correction-kind": "judge-iteration", "cause": "coverage-gap", "summary": "refreshState carried no contract-membership guard though dispatchAction did, leaving the partial-bind path undefined; the references contract had no use case, CFG or scenario, leaving one acceptance bullet unmet"}
{"seq": 3, "ts": "2026-09-08T05:10:39Z", "handle": "unional", "kind": "correction", "correction-kind": "judge-iteration", "cause": "design-overreach", "summary": "a views capability contract was declared with no use case paying for it; cut to three contracts and recorded as deferred until the TUI shows whether it is a distinct area"}
{"seq": 4, "ts": "2026-09-08T05:33:39Z", "handle": "unional", "kind": "correction", "correction-kind": "judge-iteration", "cause": "unverifiable-assertion", "cause-candidate": true, "summary": "a Then asserted the absence of a store the design says never exists, so nothing records it; a second scenario exercised an introspection surface declared nowhere. Both rewritten against a named artifact"}
{"seq": 5, "ts": "2026-09-08T05:33:39Z", "handle": "unional", "kind": "correction", "correction-kind": "judge-iteration", "cause": "sibling-doc-drift", "cause-candidate": true, "summary": "two regressions traced to the prior round editing beyond what a finding named: a cut concept left standing in the glossary, and an extension reworded into one the graph carries no path to. Remediation edits now scope-audited against the diff before commit"}
{"seq": 6, "ts": "2026-09-08T05:42:21Z", "handle": "unional", "kind": "correction", "correction-kind": "judge-iteration", "cause": "coverage-gap", "summary": "the rewritten decision scenario never pinned how the provider answered, so it did not anchor the edge its scenario-map row claimed; a Given step now names the branch, matching its sibling scenario"}
{"seq": 7, "ts": "2026-09-08T06:24:26Z", "handle": "unional", "kind": "correction", "correction-kind": "user-review", "cause": "malformed-decision-node", "cause-candidate": true, "summary": "user found a decision node whose outgoing edges were not answers to its own question; the sweep found two more instances of the same rule across the six graphs, all corrected"}
119 changes: 119 additions & 0 deletions .agents/plans/6-command-center-contract.plan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
---
cr-ref: github-6
source: https://github.com/cyberuni/cyber-truss/issues/6
status: active
todos:
- content: Bootstrap the SDD project spec (no corpus exists in this repo)
status: completed
- content: Decide the Command Center package split and spec home
status: completed
- content: Record package/dependency boundaries and the three-model distinction
status: completed
- content: Draft the versioned extension contract (references, state, actions)
status: completed
- content: Author the .feature covering load/refresh/resolve/action/unload/version-mismatch
status: completed
- content: Build the walking skeleton to validate the contract before freezing
status: completed
- content: Partition the node so freeze follows what a slice has exercised
status: completed
- content: Spec gate on provider-binding — freeze the exercised half
status: pending
- content: Impl gate on provider-binding against the landed code
status: pending
- content: Impl gate, then handoff via PR closing the issue
status: pending
---

# github-6 — Command Center application boundaries and versioned extension contract

First Foundations item of initiative [#5](https://github.com/cyberuni/cyber-truss/issues/5).
CR source: [#6](https://github.com/cyberuni/cyber-truss/issues/6).

## Scope

Establish the application-facing contract Cyberfleet and SDD integrations bind to, before
either depends on a host API. Command Center is a separate application package here;
Truss core stays independently usable.

## Constraints carried in from `docs/backlog.md`

- Plugins carry their own dependencies — no capability vocabulary, no `requires[]`, no
`blocked` status (*Settled — do not re-derive*).
- Not an MCP server.
- The Truss controller/probe contract (C1/C7) is a **different** contract from the
Command Center extension contract; relate them, do not merge them.
- Open decision 1 (plugin discovery: `package.json` key vs `.agents/universal-plugin.json`)
is adjacent and unresolved; #6 must say how it relates without settling it by accident.

## Settled

- **One package**, `packages/command-center`, with the contract reached through a subpath
export. Reversible until the first external provider ships.
- **Spec home**: colocated at `packages/command-center/.agents/spec/`, strategy
`capability-first`, intent mode. Truss core stays unspec'd for now.
- **Leash**: `auto-none` — the user ratifies both gates.

## Adjacent: cyber-sdd#6 (ADR-0034)

Adopts colocation of the **node** spec with its subject; project spec and router index
unchanged. Runs in parallel — it migrates nothing, and relocation is a zero-content-delta
`git mv` that preserves `@frozen`. Do not couple this mission to it. Switch over once
`packages/command-center/src/` exists and we know where the subject lives; handoff's
placement pass is already the mechanism.

## Open observation (routed, not acted on)

The actors table names "one area of a domain must not take down the others" as one of the
contract's three hardest requirements, but every failure scenario in the suite is
single-provider — cross-provider fault isolation is structurally implied by per-binding
independence, never demonstrated. Strategist-owned: either add a scenario before freeze, or
soften the claim to say it is structural rather than tested. Raised by the third cold judge.

## Filed follow-ups

- #10 `truss init` — write and validate the lattice declaration
- #11 the skill that decides what a repo declares
- #12 AGENTS.md documents a docs/adr/ that does not exist

## What the skeleton proved

Runs in a herdr pane via `cyber-mux open`; all three views and refresh driven remotely.
The contract survived a real out-of-process provider unchanged — the host carries a Truss
payload it understands nothing about. Two corrections fell out of building it: the empty
case had to be a first-class outcome rather than an error, and a TUI that only renders
under a TTY cannot be tested, so `--view` and a non-TTY single render exist.

Still unexercised by the skeleton, and therefore still unvalidated: actions, references,
and the no-replay path. Those should not freeze on the strength of a spec alone either.

## The partition

One node mixing exercised and unexercised behaviour could not reach either gate: freeze is
per file, so freezing all 26 scenarios would fail the impl gate on actions and references
that have no implementation, and not freezing left the landed code ungated.

- `integration/provider-binding` — 16 scenarios. Discovery, load, per-contract
compatibility, snapshot freshness, release. Every one has running code behind it.
- `integration/provider-exchange` — 10 scenarios. References, actions, and the no-replay
guarantee. Unbuilt, and stays `draft` until iteration 4 exercises it.

The in-flight-action-on-unload scenario moved to exchange: its outcome is a property of the
action, not of the release, and leaving it in binding would have split one decision across
two nodes.

## NEXT

Draft complete at `status: draft`: 24 scenarios, six use-case groups, six control-flow
graphs; check-suite, check-spec-state and check-spec-structure green. Two spec-judge rounds
run — round 1 blocked on governance pre-flight, round 2 returned CHANGE and all six findings
are answered.

Three cold-judge drives run. Defect count 6 -> 4 -> 1; lenses failing 2 -> 2 -> 1; the last
drive passed Oracle and Architect with 23 of 24 scenarios clean. All blocking findings are
answered. Every drive found its defect in text the prior round had touched, so no drive has
yet returned `approve` on an unchanged artifact — a fourth verification drive on the current
files is what would establish convergence.

Leash is `auto-none`: the spec gate is the user's to ratify. Awaiting their call on whether
to run that fourth drive or gate as it stands.
13 changes: 13 additions & 0 deletions .changeset/bundle-cli-dependencies.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
"cyber-truss": patch
---

Bundle the CLI's dependencies into `dist/cli.js`.

An installed agent plugin is a copy of a source checkout, not an npm install, so its directory has
no reliable `node_modules` and a CLI with external dependencies cannot be run from it. The published
`dist/cli.js` now inlines `commander` and runs with no `node_modules` present at all.

The library entry (`.`) is unchanged. Its dependencies stay external so a consumer that also uses
`commander` shares one copy instead of getting a private inlined duplicate, and so the public
`.d.ts` keeps referring to types it can actually resolve.
9 changes: 9 additions & 0 deletions .command-center/providers.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"providers": [
{
"name": "truss",
"command": "node",
"args": ["packages/command-center/providers/truss/provider.mjs"]
}
]
}
197 changes: 197 additions & 0 deletions .truss/lattice.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
# The lattice this repository declares.
#
# An artifact-set is a KIND of thing this repo intends to hold — not a list of files
# that happen to exist. Declaring one is an act of intent, so a set may legitimately 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.
#
# Emptiness is therefore an obligation on the SET, never a strain on a connection.
# A connection between two sets is only evaluable once both are populated.
#
# Connections are UNDIRECTED — `between` is a pair, never a source and a target.
#
# ── the two axes ─────────────────────────────────────────────────────────────
#
# `axis` says which grouping a set is, and the two are orthogonal — a group on one
# need not be a group on the other. The tell is what the set is asked:
#
# unit-of-change artifacts that must move together for a change to be complete.
# Delta-driven, meaningless without a change: DID THIS LAND
# EVERYWHERE IT HAD TO? Members are heterogeneous — different
# kinds bound by one commit. Strain here is incompleteness
# (within one unit) or obligation (across two).
#
# governance-target artifacts the same criteria apply to. State-driven, needs no
# diff: DOES THIS CONFORM TO WHAT GOVERNS IT? Members are
# homogeneous — one criterion over all of them. Strain here is
# nonconformance.
#
# The same files appear on both axes, for unrelated reasons, and that overlap is the
# model working rather than a modelling error. `{code, test}` is one unit of change —
# code that lands without its tests is not a smaller change, it is an incomplete one —
# while code and tests are two SEPARATE governance targets, because tsc and biome hold
# the code and vitest holds the tests to criteria that are not the same criteria.
#
# Note which sets have controllers. Every governance target below has one, and most are
# good. The unit-of-change sets have `controller = "none"` almost throughout — nothing
# in this repository holds artifacts together across a commit. That is the hole the
# model exists to name, visible in this file as a column of "none".

schema = 1

# ── what must move together (axis 1) ─────────────────────────────────────────

[[artifact-set]]
name = "cyber-truss implementation"
axis = "unit-of-change"
controller = "none"
# The canonical `{code, test}` unit. Held as ONE set on purpose: split them and the
# connection between has to be traversed from an end, which is test-first (TDD) or
# code-first (tests that reach for what the code does, never for what it must never
# do). One set has no traversal order, so the question does not arise.
members = ["packages/cyber-truss/src/**/*.ts"]

[[artifact-set]]
name = "cyber-truss spec"
axis = "unit-of-change"
controller = "none"
members = ["packages/cyber-truss/.agents/spec/**"]
# Declared and unpopulated. The obligation is to backfill it.

[[artifact-set]]
name = "command-center spec"
axis = "unit-of-change"
controller = "governance" # SDD gates
members = ["packages/command-center/.agents/spec/**"]

[[artifact-set]]
name = "command-center implementation"
axis = "unit-of-change"
controller = "none"
# `{code, test}` again — the tests live beside the code they exercise, so one glob
# covers the unit.
members = ["packages/command-center/src/**/*.ts"]

[[artifact-set]]
name = "command-center providers"
axis = "unit-of-change"
controller = "none"
# A provider and its registration are one commit: shipping the implementation without
# registering it leaves the host unable to bind it, and registering one that does not
# exist fails at startup. Neither half is coherent alone.
members = ["packages/command-center/providers/**", ".command-center/providers.json"]

[[artifact-set]]
name = "plugin components"
axis = "unit-of-change"
controller = "none"
# The model's headline case for incompleteness strain: add a skill, also wire the manifests and the
# `files` list, in that one commit.
members = [
"packages/cyber-truss/skills/**",
"packages/cyber-truss/commands/**",
"packages/cyber-truss/agents/**",
]
# Declared and unpopulated, and the emptiness is already inconsistent with what ships:
# `packages/cyber-truss/package.json` lists "skills" in `files`. The obligation is to
# ship the components the package manifest already promises, or stop promising them.

# ── what the same criteria govern (axis 2) ───────────────────────────────────

[[artifact-set]]
name = "repository agent config"
axis = "governance-target"
controller = "deterministic-code" # CLAUDE.md is a symlink to AGENTS.md
members = ["AGENTS.md", "CLAUDE.md", "apps/web/AGENTS.md", ".claude/**", ".cursor/**"]

[[artifact-set]]
name = "source layout"
axis = "governance-target"
controller = "none"
# The directories AGENTS.md's Layout table names — one level, not their contents.
members = ["apps/*/", "packages/*/", "docs/*/", "scripts/"]

[[artifact-set]]
name = "cyber-truss code"
axis = "governance-target"
controller = "deterministic-code" # biome, tsc
# Crosses "cyber-truss implementation" and refines neither: that set asks whether the
# change landed everywhere, this one asks whether the code conforms, cold.
members = ["packages/cyber-truss/src/**/*.ts", "!packages/cyber-truss/src/**/*.test.ts"]

[[artifact-set]]
name = "cyber-truss tests"
axis = "governance-target"
controller = "deterministic-code" # vitest
members = ["packages/cyber-truss/src/**/*.test.ts"]

[[artifact-set]]
name = "plugin manifests"
axis = "governance-target"
controller = "deterministic-code" # scripts/sync-plugin-version.mjs, on `pnpm version`
members = [
"packages/cyber-truss/package.json",
"packages/cyber-truss/plugin.json",
"packages/cyber-truss/.plugin/plugin.json",
"packages/cyber-truss/.claude-plugin/plugin.json",
"packages/cyber-truss/.cursor-plugin/plugin.json",
"packages/cyber-truss/.codex-plugin/plugin.json",
".claude-plugin/marketplace.json",
]

[[artifact-set]]
name = "docs site"
axis = "governance-target"
controller = "none"
members = ["apps/web/src/content/docs/**"]

# ── what must hold between them ──────────────────────────────────────────────
#
# A connection between two unit-of-change sets carries OBLIGATION strain — the two ends
# land in different commits by design, so the debt is created now and discharged later.
# A connection reaching a governance target carries NONCONFORMANCE strain, evaluable on a
# cold repository. Incompleteness strain is intra-unit and needs no connection to state.

[[connection]]
between = ["repository agent config", "source layout"]
degree = "high"
holds = "the layout AGENTS.md describes is the layout on disk"

[[connection]]
between = ["cyber-truss code", "cyber-truss tests"]
degree = "high"
holds = "every code unit has a test that exercises it"
# Coverage, between two governance targets. It says nothing about whether the two
# landed together — that is the unit-of-change set, and no controller holds it.

[[connection]]
between = ["cyber-truss spec", "cyber-truss implementation"]
degree = "high"
holds = "the code conforms to its specification"

[[connection]]
between = ["command-center spec", "command-center implementation"]
degree = "high"
holds = "the code conforms to its specification"
# The SDD instance: a spec unit and an implementation unit, discharged at the impl
# gate. Obligation strain is what the spec gate creates and the impl gate settles.

[[connection]]
between = ["command-center providers", "command-center implementation"]
degree = "high"
holds = "every registered provider satisfies the contract the host binds against"

[[connection]]
between = ["plugin components", "plugin manifests"]
degree = "high"
holds = "every component the package ships is declared in the manifests and listed in `files`"

[[connection]]
between = ["plugin manifests", "cyber-truss code"]
degree = "medium"
holds = "the manifests carry the package version and description the code ships"

[[connection]]
between = ["docs site", "cyber-truss code"]
degree = "medium"
holds = "the documented CLI surface is the surface the code exposes"
2 changes: 1 addition & 1 deletion knip.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"$schema": "https://unpkg.com/knip@6/schema.json",
"ignore": ["docs/prototypes/**"],
"ignore": ["docs/prototypes/**", "packages/command-center/providers/**"],
"workspaces": {
"packages/cyber-truss": {
"project": ["src/**/*.ts"]
Expand Down
4 changes: 4 additions & 0 deletions packages/command-center/.agents/spec/decisions/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Decisions

Surfacing a pending decision and returning an answer to the domain that asked. Rendering a
control never grants approval — the answer is the provider's to accept.
4 changes: 4 additions & 0 deletions packages/command-center/.agents/spec/design/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Design

The rules and the model behind Command Center — the *why*. Behavior enacting these rules
lives in the capability folders; decisions are logged append-only in `decisions/`.
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Decisions

The architecture decision log for Command Center. Append-only, descriptive, ungated.
Loading