Skip to content

Commit 010d84c

Browse files
rsalusReed SalusCopilotclaudelvlup-sw-pr-autoupdate[bot]
authored
refactor: wiring-closure remediation — code (structural-closure delta audit) (#1733)
* feat(workflow): make cancellation replayable * feat(views): project attributable gate reliability * feat(orchestrate): persist phase gate evidence * feat(orchestrate): persist ladder gate evidence * feat(gates): enforce durable runner ownership census * test: align evidence event emission ownership Expect canonical gate evidence to be auto-emitted while keeping deferred admission records planned. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: ac72a05e-c0f7-488c-925c-af2c53499d2d * docs: assess structural principles adoption Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: cf6ee313-1c4d-4356-8dc8-8628375e0579 * docs: recommend phase-gate dogfood remediation Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * docs: broaden dogfood remediation to structural closure Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * docs: align structural closure with workflow IR Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * docs: inventory structural drift seams Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * docs: define empirical structural acceptance gates Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * docs: distinguish action API from MCP projection Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * docs: persist structural closure review Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * docs: remove obsolete diagram styling Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * docs: eliminate report and review deltas Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * docs: colocate structural closure artifacts Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * docs: track structural closure audit package and specs Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(views): wire gate-reliability read model into production composition (BASE-002) The gate-reliability projection existed with zero production importers, failing the module-intent gate (DR-7) as dead-in-prod. Register it in the production materializer and expose it as the diagnostic-only \gate_reliability\ view action so the read model reaches a real consumer. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(types): retire six double-widening casts at their owning boundaries (BASE-001) The candidate failed its own blocking type-debt gate on four files. Replace the casts with typed narrowing instead of raising budgets: - readPhaseAttemptId() narrows the passthrough phase-attempt stamp once - isEventSourced() accepts unknown and narrows via 'in' - hasImmediateTransaction() is the single narrowing boundary for the driver's BEGIN IMMEDIATE wrapper The baseline ratchets DOWN (87 -> 81 casts); sqlite-backend.ts and workflow/cleanup.ts drop out of the baseline entirely. Also refreshes the EventTypes golden pin (159 -> 164): the candidate added the five-event cancellation process-manager contract after the pin was last set. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(gates): tolerate script-runner preamble in check_integration_suite (WFQ-003) The gate ran the suite through npm, whose banner is concatenated with the vitest JSON reporter output on one stream, so JSON.parse(stdout) threw and a GREEN suite failed closed with parseError (#1537). parseVitestResult now scans stdout for complete top-level JSON objects (string-aware, reporter-blob-first) instead of requiring the whole stream to be one document. Fail-closed semantics are unchanged: bare arrays, empty objects, counter-less objects, and pure banner noise still return null -> shape-mismatch. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(delegate): make task_complete terminal and move the cumulative suite to the wave boundary (WFQ-004) The task-completion runbook emitted task_complete at step 5 and only ran the blocking check_integration_suite at step 6, so a task could be recorded complete and only then fail its last blocking gate. The cumulative gate also contradicted its own action description by running per task, giving three owners for the same verification claim. - task_complete is now the terminal step of TASK_COMPLETION - check_integration_suite runs exactly once per wave in AGENT_TEAMS_SAGA, before post_delegation_check - delegate SKILL.md declares a verification-ownership contract (one owner per claim); the implementer prompt and verification ladder match it - runbook-shape tests pin both invariants - corrects three runbooks' declared autoEmits, which had drifted from the canonical gate runner's admission.evidence-recorded emission Also fixes the skills renderer self-invocation guard, which compared import.meta.url against a naive file:// concatenation and therefore never fired on Windows — npm run build:skills exited 0 without rendering anything. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(readiness): scope delegation readiness to the active wave on every surface (WFQ-002) Readiness derived expected from every historical task.assigned event on the stream, so a 4-task wave inside a 17-task workflow waited on 17 worktrees (CB-2). prepare_delegation already scoped via computeScopedWorktrees, but the delegation_readiness view action did not — the two surfaces reported different readiness for the same wave. - moves the scoping core beside the projection it scopes, so both consumers share ONE implementation - adds scopeReadinessToWave() and threads a tasks[] filter through the delegation_readiness action - extracts canonicaliseTaskId into the dependency-free utils/task-id leaf so views does not import orchestrate - regression tests replay 18 historical assignments and assert a 4-task wave is ready after exactly 4 worktree.created events Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(event-store): repair a diverged stream-version gate at startup + prove concurrent append atomicity (EFF-001) CB-1 reproduced events.sequence / stream-version divergence (Expected sequence 236, actual 235). The candidate added BEGIN IMMEDIATE, but nothing reconciled a store that ARRIVES diverged, and the only cross-instance test covered sequential interleaving, which the per-instance promise mutex already satisfies. - SqliteBackend.repairSequenceHighWaterMarks() runs in initialize(), before any traffic. A gate TRAILING MAX(events.sequence) is raised to the durable tail (the tail is authoritative, the counter derived); a gate LEADING it is left monotonic and reported, never lowered. Both log loudly. - New tests fire 50 competing appends from two instances at one stream with no awaits between them and assert dense unique sequences and zero lost writes, plus restart-safe appendability from a third instance. - Repair tests seed a diverged gate on a real SQLite file. Verified non-vacuous: disabling the repair call turns GateTrailsEventTail_RepairedBeforeServingTraffic red. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat(projections): emit a typed projection-degraded signal on cursor/tail disagreement (EFF-002) CB-8: workflow views served a silently stale fold — a cancelled workflow still reported at plan-review, 7 of 10 completed tasks visible, lag past 500s — with nothing on the response saying the answer did not derive from the event tail. - projections/freshness.ts is the pure comparison: a fold is fresh only when its cursor equals MAX(events.sequence). Behind means incomplete; ahead means the fold and the log contradict each other (snapshot over a pruned store). Both degrade; the worst offender leads. - handleView is the single chokepoint, so every view action inherits the check rather than each handler re-implementing it. Degraded reads carry a typed _meta.projectionDegraded and a warn log. - ViewMaterializer.getStreamCursors enumerates a stream's folds without disturbing LRU order. Conservative by construction: failed results, cold reads with no cached fold, and any fault inside the probe leave the response untouched. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * test(adapters): assert the trusted context CLI adapters actually forward The candidate made buildCli stamp callerIdentity from the transport (a caller cannot self-assert its principal), but four adapter tests still asserted the raw context they constructed — 22 failures across parameterized cases. expectedTrustedContext() applies the real deriveLocalOperatorIdentity rather than loosening the assertion to objectContaining, so a regression that dropped or altered the derived stamp still fails. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * test(gates): drive ladder gates through the real trusted-dispatch preconditions 23 failures across 10 files, one root cause: the candidate routed the ladder gates through the canonical durable-evidence runner, which reads its caller's authorization from the ambient dispatch scope and binds evidence to an active phase attempt. Tests that call handleOrchestrate directly (bypassing dispatch) supplied neither, so every case exercised TRUSTED_CALLER_REQUIRED or ACTIVE_PHASE_ATTEMPT_REQUIRED instead of the behaviour under test. - runAsTrustedCaller() composes the exact primitives core/dispatch.ts uses (snapshotCallerAuthorization + mintDispatchContext + runWithDispatchContext) so the harness cannot drift from production plumbing - seedActivePhaseAttempt() starts the workflow a gate legally runs inside - assertions move from the retired gate.executed emitter to the canonical admission.evidence-recorded record, matched on the runner's observation source; the escape-hatch test now pins the durable evidence digest and the carrier's reference to it - parity normalizers drop evidenceReferences: each arm owns a separate store, so the content-addressed evidenceId is arm-local provenance, not part of the CLI/MCP payload contract Deliberately does NOT mock durable-gate-producer — stubbing it would also stub the durable-evidence append these gates exist to perform. MCP suite failures drop from 36 to 13. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(evals): restore the seeded-defect catch-rate benchmark to measuring real verdicts Every cell of the catch-rate benchmark was recording 'invalid' — the driver's fail-honest path working correctly over a harness that could not run a gate, so the enforcement-floor measurement was silently meaningless. Three stacked preconditions, each masking the next: - the driver is the transport for these handlers, so it must open the ambient trusted dispatch scope (TRUSTED_CALLER_REQUIRED) using the same primitives core/dispatch.ts uses, and start the workflow the gate binds evidence to (ACTIVE_PHASE_ATTEMPT_REQUIRED) - corpus fixture ids are PATHS ('test-adequacy/defect-01'); the '/' makes both the derived taskId and phaseAttemptId malformed as schema-validated identities, which the gate rejects as INVALID_GATE_SCOPE Flattening is injective, so cells stay distinguishable. MCP suite failures drop from 13 to 11. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(cancel): guard the process manager against a nullish event store + align compensation tests with verify-then-decide Closes the last 11 MCP suite failures. Two were product bugs, not stale tests. Product: - handleCancel useEventFirst gate used "!== null", which does not exclude undefined -- the legacy two-arg call handleCancel(input, stateDir) passes no store at all. The pre-existing event-first block was shielded by an outer "if (eventStore)", but the new cancellation process manager runs BEFORE that guard, so an undefined store reached appendCancellationFactOnce and crashed with "Cannot read properties of undefined". Same latent trap hardened in cleanup.ts, which dereferences eventStore! under the identical guard. Tests, aligned to contracts the candidate deliberately changed: - compensation mocks now return durableOutcomes; omitting it means compensation ran outside the process manager, which must fail closed as COMPENSATION_PARTIAL, so those mocks were exercising the fail-closed path - compensation-fact idempotency keys are asserted at their new owner (the process manager), without mocking away the emission under test - branch deletion moved from "swallow every failure" to verify-then-decide, so a delete that errors while the branch REMAINS is now honestly "failed". Added the complementary case: a delete raced by a concurrent cleanup still reports "executed" because the outcome was achieved. - the compensation integration fixture is a real git repo with a real origin; compensation verifies outcomes now, so a non-repo temp dir correctly fails MCP suite: 9167 passed, 0 failed. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * refactor: keep the escape-hatch ratchet neutral across this work The DR-14 non-null/`as` ratchet counts postfix assertions in production sources (test files are skipped), and — like the type-debt gate — it matches inside COMMENTS too. This work had pushed the measured delta from 36 to 38. Replaces the assertions introduced here with real narrowing (destructure + explicit undefined guards in the freshness fold, defined-checks in the gate and runbook assertions) and rewords the one comment that tripped the counter. Measured delta is back to 36 — exactly the pre-existing candidate level, so this work contributes zero new escape-hatch debt. The 36 remain a genuine candidate-wave breach of the declared budget of 20, tracked separately. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * refactor: retire every non-null assertion the v2.12 proof-substrate wave introduced The DR-14 ratchet measured 36 new postfix non-null assertions against a declared budget of 20. Each was a place the checker was silenced rather than convinced. All 22 in changed production sources are now replaced with real proof: - compensation.ts (9): ProcessManagedCompensationOptions narrows the eventStore/featureId/cancelProcess triple ONCE at the boundary that already validates them, so the helpers read the fields directly. Re-asserting per use would have silently survived a caller that stopped validating. - gate-provider-registry.ts (6): the Levenshtein DP rows become Int32Array, whose indexing is typed number rather than number|undefined -- the dense-array friction disappears instead of being suppressed. A missing registration is now a typed MISSING_GATE_PROVIDER diagnostic rather than a crash. - select-evidence.ts (3), evidence-subject.ts (1), gate-reliability-view.ts (1): destructured first-element guards; an unmeasurable gate reports emptyMetric, matching the module's own "null rather than pretending healthy" stance. - cancel.ts (1): bind the dispatch context once instead of re-reading it. - cleanup.ts (1): narrow the event store into a local, which also removes the nullish trap fixed in the previous commit. The nonNull axis of the ratchet now passes. It immediately surfaced a SECOND breach behind it: asCast is +70 over baseline against a budget of 5 (+50 excluding `as const`, which the counter conflates with escape hatches despite being a narrowing). That is a separate wave-sized debt, reported not hidden. MCP suite: 9167 passed, 0 failed. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat(admission): converge equivalent concurrent gate evidence on one canonical active record (EFF-003) The runner keys idempotency on evidenceId, which derives from the caller's operationId. Two executions of the SAME logical gate -- identical (requirementId, phaseAttemptId, providerRef, subject) -- arriving under distinct operationIds mint distinct evidenceIds, read history before either has appended, and both land with no predecessor. Neither supersedes the other, so the scope carried two competing active chains: the "competing active-predecessor evidence chains" the audit named. Reproduced first, then fixed. selectEvidence now converges a scope whose active records all make the same statement onto the lowest evidenceId, reporting each collapsed duplicate as a CONVERGED_EQUIVALENT_EVIDENCE diagnostic. This is the exact complement of the contradiction rule: agreement has one answer, so an arbitrary arrival order must not decide it; DISAGREEMENT keeps every record active and raises the contradiction, because a disagreement must deny admission rather than be silently collapsed into whichever arrived first. Tests race two executions released only once both providers have entered, so neither can observe the other's append: - equivalent verdicts -> exactly one active, and the same one under reversed arrival order - contradictory verdicts -> both stay active and the contradiction is raised - no success carrier is observable before the awaited append resolves MCP suite: 9170 passed, 0 failed. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(win32): recognise bun/bunx as Windows cmd shims `resolveIntegrationCommand` classifies `bun` as a script runner, so `check_integration_suite` can spawn a bare `bun` -- but `WINDOWS_CMD_SHIMS` listed only npm/npx/pnpm/yarn/corepack. On Windows bun ships as a `.cmd` shim like the rest, so a bun-based project's integration gate failed to launch. A test now pins the two lists together: every runner the gate will spawn must also be recognised as a shim. Same root cause blocked the packaged-binary harness: `ensureBinaryBuilt` spawned `bun` without a shell and reported the resulting ENOENT as an opaque "build-binary.ts failed (exit null)" with empty stdout/stderr. It now reuses `needsWindowsShell` and surfaces `result.error`. With bun installed, test/process/compiled-binary-mcp.test.ts passes -- the last environmental blocker on the packaged-proof surface EFF-007 needs. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * test(packaged): prove reserved-event append authorization in the shipped binary (EFF-007) SHIP-F001 lists event.append among the built-in actions with no located compiled-binary proof: the reserved-proof-event guard was verified in-process only. A guard that exists in src/ but is absent or inert in the shipped artifact protects nothing -- and this is the action that would let a caller forge admission evidence straight into the log. Drives the COMPILED BINARY over MCP: - a reserved admission fact and a reserved cancellation fact are both rejected with RESERVED_EVENT_TYPE through generic append - the rejection precedes persistence: the stream never contains the fact, so a guard that denied the caller while still writing would fail here - a non-reserved event on the same surface still appends, so the guard is scoped rather than a blanket denial that would merely look safe Verified non-vacuous: neutralising the guard turns the two rejection cases red. Whole MCP suite now green in this environment -- 735 files, 9177 passed, 0 failed, including the packaged-process tests that could not run before bun. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat(config): make CLI and MCP configuration writes atomic with typed corruption recovery (EFF-008) config.ts and mcp.ts overwrote user-owned JSON in place. A failed writeFileSync leaves the target neither the old configuration nor the new one, and the next read fails on a file the user never edited -- configuration is the effect class where "partially applied" is strictly worse than "not applied". src/operations/atomic-json.ts is now the single primitive both writers route through: serialize, VALIDATE by parsing the bytes back, write to a temp file in the same directory, fsync, rename, and unlink the temp on any failure. Reads raise a typed ConfigParseError instead of defaulting. That last part is the sharpest edge: readMcpConfig previously returned `{}` for a file it could not parse, so the next merge-and-write would silently DELETE every MCP server the user had configured. Absence and corruption are now distinct -- absence is a normal first-run state, corruption is an operator-visible fault. Failure injection covers each step (write / fsync / rename) through an injectable fs seam and asserts the prior configuration survives byte-identical with no temp artifact left behind, plus the no-prior-file case leaving no target at all. Root operations suite: 101 passed. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * P01-03: complete evidence and admission algebra with exhaustive types and exit-proof tests - Add ContradictionRecordV1Schema (active-evidence + downstream-event arms) - Add ContradictionStatementSchema (satisfied/unsatisfied/indeterminate) - Add ReassessmentRequestV1Schema and ReassessmentOutcomeV1Schema - Add ADMISSION_EVENT_TYPES constant map and ADMISSION_EVENT_TYPE_VALUES array - Register admission event identity round-trips in mapInternalToExternalType/mapExternalToInternalType - Add 46 exit-proof tests demonstrating: * Bare booleans cannot satisfy requirements, evidence, or decisions * Malformed evidence subjects (missing kind, missing ID, extra fields, path traversal) are rejected * Malformed/mismatched artifact content digests are rejected * Contradiction and reassessment types are exhaustive and immutable * Admission event vocabulary is closed and round-trips correctly through type maps Exit proof: bare booleans cannot satisfy requirements; malformed subjects or artifact digests are rejected. Source coverage: transition tasks 007, 008, 011, 012. * P06-01: Add guard classification corpus and totality tests (DR-1) Introduces a typed, machine-readable classification of all 43 legacy guard predicates across the Exarchos workflow engine, satisfying the P06-01 exit proof for DR-1. Files added: - servers/exarchos-mcp/src/workflow/__fixtures__/guard-classification.ts Typed classification record mapping every guard ID to exactly one of the six DR-1 categories: route-condition, admission-requirement, bounded-loop-rule, approval, waiver, or obsolete-predicate. Covers 37 guards from guards.ts + 6 composite/private guards from hsm-definitions.ts (merge-pending-entry, merge-pending-exit, and 4 composeGuards composites). Includes flaggedForRemediation entries with defectNotes for known behavioral anomalies. - servers/exarchos-mcp/src/workflow/guard-classification.test.ts 34 focused tests verifying: (1) Classification totality - every known guard ID is classified; (2) Uniqueness - no guard classified twice; (3) Valid categories - all categories in the DR-1 set; (4a) Behavioral pins for obsolete no-ops (implementation-complete, always); (4b) Behavioral pins for bounded-loop guards (revisions-exhausted, synthesize-retryable); (4c) Mutual exclusivity of route-condition guard pairs at forks; (5) Corpus exhaustiveness - every transition guard has a fixture, every fixture edge has exactly one pass and one fail fixture. The test in (5) enumerates the topology programmatically so that adding a new transition without a fixture causes an immediate test failure. The existing LegacyTransitionCorpus_AllFixtures_HaveStableVerdicts test already evaluates all corpus fixtures against the real guard implementations; this commit adds the classification layer on top of that. Known defects pinned (not fixed - scope discipline): - implementation-complete always returns true (no-op) - all-tasks-complete vacuously passes on empty task arrays - plan-review-complete and human-unblocked accept plain mutable booleans with no attribution, scope, or expiry - root-cause-found, brief-complete, design-artifact-exists are dead code * fix(gates): stop check_test_adequacy passing vacuously (P02-04 / WFQ-005) The kill probe is the sole load-bearing per-task verification gate, and it reported PASS in three cases where it had proved nothing: 1. Changed-file discovery diffed the checked-out HEAD instead of the named task branch, so an orchestrator calling from the main worktree saw an empty diff for a branch that plainly added tests, and short-circuited to the no-new-tests advisory pass. changedFilesFor now takes the branch as the HEAD side of the diff. 2. A git failure was swallowed into an empty file list, which became the same advisory pass. Discovery now returns a discriminated result and the probe fails closed with a new diff-failed discriminant. 3. no-new-tests was an advisory pass on every tier, including the medium/high tiers where the probe is required. It now blocks on those tiers and stays advisory only for low/unset. Observed live: the gate returned passed:true with 'nothing to probe - task adds no tests' against an 865-line, 46-test file on task P01-03. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * P01-04: reconstruct frozen phase-attempt admission state in the event fold Persists complete requirement sets and decision state in the replay fold so a phase attempt's admission state can be reconstructed from persisted facts alone (transition tasks 019, 020, 040). admission/phase-attempt-state.ts — a pure fold over the append-only admission histories. It groups an attempt's `admission.requirement-resolved` facts by the writer-frozen `requirementSetDigest`, treating each digest as one immutable generation of the complete requirement set; the generation named by the attempt's last resolution is active, earlier ones stay auditable. Evidence binds only to a requirement in the active frozen set, and a decision is promoted only when its own `requirementSetDigest` matches that set — so an attempt that never froze requirements can never carry a trusted decision. Malformed persisted facts have one explicit policy: QUARANTINE. Every payload is safeParsed against its registered schema; anything that fails, or that parses but cannot be reconciled with the frozen set, is excluded from the trusted slots, reported as a typed diagnostic, and marks the owning attempt and the fold `'contested'`. Nothing is silently dropped and nothing is silently trusted, and replay stays total. Identity is parsed, never cast: diagnostics and `selectPhaseAttempt` obtain branded ids through the P01-03 zod schemas, so an unvalidated string can never stand in for a phase-attempt, requirement, evidence, or decision id. views/workflow-state-projection.ts folds `admission.requirement-resolved` and `admission.transition-decided` (previously identity cases) into the canonical workflow-state fold and rebuilds the attempt state from history on every admission event, so the incremental projection and a from-zero replay agree. `admissionProofOf` now defaults per field, letting a state persisted before this contract version accept new attempt facts without backfill. Exit proof: replay reconstructs the same active attempt, requirements, evidence, and decision without current policy or external I/O. * P04-03: prove artifact-store containment (digest, traversal, atomic publish, concurrency) Harden the content-addressed artifact store and prove its containment guarantees for evidence artifacts: - Add a path-containment primitive (artifact-path.ts) that rejects traversal, absolute, drive-relative, UNC, mixed-separator, and NUL keys with a typed ArtifactPathError; wire it into the store's digest-derived path resolution (defense-in-depth). - Add write-side digest validation: put() rejects a caller-declared digest that disagrees with the content before anything reaches disk. - Make the async publish durable (open/fsync/close before rename) and clean up the staged temp on publish failure; add an injectable IO seam so crash-injection is possible without module mocking. - Expose a packaged entry-point barrel (artifacts/index.ts) and prove the guarantees through it. - Exit-proof fixtures for traversal, digest mismatch, partial publish, and concurrent collision at both the store and evidence-artifact entry points. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * P01-05: fix Int32Array index typing under noUncheckedIndexedAccess The levenshtein helper's typed-array reads are number|undefined under noUncheckedIndexedAccess (index signature), so tsc --noEmit was red on the integration tip. Assert the provably in-bounds DP reads and correct the stale comment. Behaviour and suggestion ranking are unchanged. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * P02-06: add calibrated decomposition & risk plausibility signals check_task_decomposition previously accepted whatever risk/boundary stamps a planner declared. Add calibrated, independently-testable plausibility signals (breadth, behavior-count, historical-size, risk/boundary uniformity) that emit STRUCTURED CHALLENGES (typed findings) rather than silently accepting implausible decompositions or hard-failing. Blanket low-risk / no-boundary stamps across a large task set and oversized tasks are now challenged; an author suppresses a specific challenge only with a recorded, non-empty override rationale (missing/empty never suppresses). Baseline is injectable with a sane default and a deterministic deriveBaseline helper (no git shell-out at check time). Wired into the handler as an additive 'plausibility' result field + report section; does not flip 'passed'. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * P02-05: separate plan-time and post-implementation coverage semantics Parse the canonical unified spec (Design & Rationale + DR-N) in check_plan_coverage and split spec_coverage_check into an explicit, typed lifecycle phase so future test paths are valid planning declarations while post-implementation coverage still demands real passing tests. - plan-coverage.ts (WFQ-006): parseDesignSections now prefers DR-N requirement headings under the unified ## Design & Rationale section (scoped to the design region), so current-template specs no longer trip NO_DESIGN_SECTIONS or need a duplicate compatibility heading. DR-N sections resolve coverage from the decomposition's explicit **Implements:** DR-N references, keeping unimplemented DR-N as traceability gaps. - spec-coverage-check.ts (WFQ-010): a typed phase input selects plan-syntax validation (well-formed forward declarations, no disk access, no execution) vs post-implementation coverage (existence + real test execution). Defaults to post-implementation for backward compatibility. extractTestFiles also reads the unified **Files:** list. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * P01-05: add canonical evidence-ownership census with exit-proof tests Route the P01-05 exit proof through a single ownership census that fails closed on the three ways evidence production can be subverted: an alternate direct emitter (static source scan for .append of admission.evidence-recorded outside the canonical durable runner), an unregistered provider (every enforceable orchestrate gate must resolve through the one registry), and a success without durable evidence (behavioural witness of the real runGate). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * P04-06: deterministic rehydration fallback precedence under degradation Declares REHYDRATION_SOURCE_PRECEDENCE (event-fold > summary-snapshot > state-store) and a pure planRehydrationSource() that maps a snapshot's position relative to the durable event tail onto a source. Consumes the P01-02 freshness verdict rather than inventing a second degradation signal: a snapshot whose cursor sits past the tail (projection-ahead) is discarded and re-folded from the authoritative log, with the result flagged degraded via _meta.projectionDegraded; a lagging snapshot is folded forward. The handler now surfaces _meta.rehydrationSource and never silently trusts a stale/contradictory projection. Existence invariant preserved: cold probes of unknown featureIds stay side-effect-free (EFF-004). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * P06-02: closed edge-condition evaluator with compile-time rejection Add a closed, declarative edge-condition AST (seven approved nodes: eventObserved, factPresent, factEquals, counterCompare, all, any, not) with compile/import-time validation that rejects unknown node kinds, executable values, prototype-pollution keys, escape-hatch properties, and undeclared references. Add a pure, total three-valued (Kleene K3) evaluator whose connectives satisfy De Morgan's laws and which returns indeterminate for unknown/malformed facts, plus deterministic fail-closed route selection for zero-match, multi-match, and leading-indeterminate cases. Implements DR-1, DR-2, DR-9, DR-10 (transition tasks 009, 010). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * P06-03: monotonic requirement resolution with requirement-strength partial order Fold the complete input lattice (phase kind, risk tier, boundary status, reliability, gate declarations, policy floor) into one deeply-frozen ResolvedRequirements set, monotone in every danger dimension by construction (join of monotone contributions). - requirement-context.ts (Task 017): normalized input lattice; removes the default-low / default-non-boundary coercions. Absent/malformed risk -> 'unknown' (never 'low'), boundary -> 'indeterminate', reliability derived from projections/freshness -> 'unknown'. - requirement-strength.ts (Task 042): the requirement-strength partial order (atLeastAsStrong, compareStrength) and its LUB (joinRequirements); reflexive, antisymmetric, transitive, genuinely partial; join is a true least upper bound. - requirement-resolution.ts (Task 018): resolveRequirements(context) -> FrozenResolvedRequirements; total, pure, deterministic, deeply frozen. unknown risk projects to 'high' (never 'low'); indeterminate boundary projects to touching; reliability consumed only as monotonic corroboration. Exit proof covered by 52 tests; 7 manual kill-probes confirmed discriminating. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * P02-07: validate skill examples against live MCP schemas; fix doc drift and toolchain truth Add a mechanical skill-example validator that extracts exarchos_<tool>({...}) invocation examples from skills-src/ and commands/ and validates them against the live TOOL_REGISTRY schemas (the same zodToJsonSchema projection the describe handler surfaces). Flags unknown tools/actions, unknown params, type mismatches, enum mismatches, and out-of-range numeric literals. A walk-the-tree test pins every real documented example clean (WFQ-011 drift guard). Fix the drift the validator found: oneshot set->update/transition; plan output->outputFile and drop invalid spec_coverage_check threshold; review check_security_scan repoRoot/baseBranch->diffContent; synthesize/shepherd reconstruct_stack->validate_pr_stack; delegate DOC-1/2/4 (prepare_delegation is read-only, canonical .worktrees/<taskId>-<taskName> path, serialize_merge dryRun:false + shared-mutating fallback). Correct the toolchain-resolution guide (retired scaffolding, per-PM test-command table). Toolchain truth: bun projects with a committed test:run script now resolve 'bun run test:run' (not native 'bun test'), so both supported workspaces (npm root, bun servers/exarchos-mcp) resolve the same intended runner + timeout policy. Make root vitest unit-tier testTimeout explicit (WFQ-015). Pin top-level 'mutation' config shape (WFQ-013/DOC-5). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * P04-02: Cancellation process manager saga (fencing, retries, manual intervention) Add a replayable cancellation process-manager engine over the event log that records cancellation intent, compensation intent/result, fencing epoch, bounded retries, and a manual-intervention-required terminal state as replayable events. State is a pure fold; completed compensations are never re-issued across restart or takeover; stale-epoch writes are rejected inside the atomic append; cancellation cannot report complete until every compensation outcome is durably recorded. - Add 3 event types additively: cancel.ownership-acquired, cancel.compensation-retry-scheduled, cancel.manual-intervention-required (registry, emission map, zod data schemas; count pins 164->167). - New engine src/workflow/cancel-process-manager.ts (fold + fencing guard + retry/escalation decisions + completion gate + atomic fenced append). - Exit-proof + unit tests src/workflow/cancel-process-manager.saga.test.ts. - workflow-state-projection: no-op cases for the 3 new event types. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * P04-01: effect algebra and observable delivery Typed effect carriers (success/error/dry-run) with a provably no-effect dry-run mode; a static effect-ownership ledger census mapping every filesystem/process/network occurrence in the shipped source to a typed owner and failing on indeterminate ownership or stale cover; a typed delivery algebra (required vs best-effort) where required failures throw a propagating RequiredDeliveryError and best-effort failures become observable carriers; and a static silent-swallow check for required delivery paths. Post-append hook and channel pushes in the event-store composite now route through the typed algebra, removing the inline catch {} swallows. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * P03-01: Freeze contract authority (pins + lockfile + fail-closed verify) Adds src/contract/ authority-pin module: content-addressed digests for the six frozen authorities (Strategos.Contracts stand-in, MCP protocol + SDK, ActionId registry, compatibility policy, invariant catalog), a checked-in approved lockfile, and a fail-closed verify entry point that blocks generation/release on floating, unapproved, mismatched, or missing authorities. Head of the PROGRAM-03 chain (CTR-012, API-001). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * P05-04: install and cache freshness gate blocks stale/mixed installs Add a typed, content-addressed install-identity record across the five mismatch dimensions (binary, plugin, skill, schema, cache) and a freshness verification that blocks before workflow execution with a typed, per-dimension actionable error. Digests are deterministic and line-ending/path-separator normalized for cross-platform (Windows author, Linux CI) stability. The schema dimension is additionally enforced at store open: SqliteBackend.initialize now refuses a store written under a newer schema than the binary understands (SchemaVersionTooNewError), mirroring the forward-only migration policy. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * P06-04: three-valued policy evaluation with scoped expiring waivers Add pure admission policy evaluation over active evidence plus a scoped, expiring, authorized waiver model and a trusted issuer authority. Missing, stale, contradictory, malformed, unauthorized, or failed evidence each denies; indeterminate is a first-class, fail-closed verdict distinct from deny. Waivers apply only to declared subjects and requirements, before expiry, and from an authorized principal; a valid waiver never rewrites failed evidence, which stays reported in recordedFailures under an allow-via-waiver. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * P03-08: define signed extension trust with fail-closed admission Add servers/exarchos-mcp/src/extensions: a signed-manifest extension trust boundary (API-009). Admission composes six independent, independently-seedable fail-closed modes before execution: - untrusted: Ed25519 signature must chain to a configured, injectable trust root (node:crypto). - revoked: identity on a current, authentic revocation list. - stale-revocation: missing/forged/expired/older-than-horizon revocation data fails closed (freshness). - rollback: version below the persisted admitted high-water mark (monotonic version ledger). - over-quota: declared quota exceeds host budget, or content exceeds the declared ceiling. - mutated: content does not match the manifest's immutable sha256 digest. TOCTOU resistance: content is loaded exactly once, the digest is verified on those in-memory bytes, and executeExtension runs that verified buffer with no path parameter, so a file mutated after verification can never execute. Isolation is a declared, typed policy checked as a subset of the host posture's capability set (reuses capabilities/posture-mapping), and the content digest reuses the repo's ContentDigestV1 schema. 56 co-located tests; manual kill-probes confirmed each guard discriminates. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * P04-02: wire cancellation saga engine into the live cancel path Route handleCancel and executeProcessManagedCompensation through the cancel-process-manager saga engine: ownership epoch acquisition, fenced appends, decideCompensationAction retry ladder, buildCancelReadiness completion gate, and the manual-intervention-required terminal. Adds integration exit-proof tests through the public cancel entry point. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * P04-05: route git/worktree mutation through one typed VCS owner Introduce the single typed VCS mutation owner (EFF-010 / EFF-011): every branch/worktree create+delete and provider PR/merge routes through VcsMutationOwner, which enforces four contracts on P04-01 EffectOutcome carriers -- provider idempotency keys (duplicate key replays the recorded outcome, so duplicate requests cannot create duplicate PRs/merges/branches/ worktrees), P04-02-style epoch fencing (a stale owner is rejected), durable intent-before-effect convergence + partial-failure compensation (fixes the observed non-atomic setup_worktree defect that left worktrees+branches with no event), and capability-aware dry-run fallback. Adds an architecture census (vcs-ownership.ts) that fails closed on any direct worktree/branch mutation outside the declared owner surface, mirroring effect-ledger.ts / gate-ownership-census.ts. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(integration): repair four cross-package regressions from parallel merges - effect-ledger: declare filesystem ownership for contract/ (P03-01) and extensions/ (P03-08); the census correctly flagged them as INDETERMINATE_OWNER. - setup-worktree.test: bun projects with a test:run script now resolve 'bun run test:run' (P02-07/WFQ-015). The old expectation encoded the behavior P02-07 deliberately fixed; added a fallback test so both branches stay covered. - cancel.test: cancel writes route through AtomicAppender.decideOnce, which stamps the idempotency key on the event rather than passing an append option. Assert against the persisted stream instead - strictly stronger. - toolchain-resolution.md: keep the retired-verb note and its replacement on one line so the docs-onboard sweep's migration-note exemption applies. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * P03-02: Close envelopes, security, and compatibility with total carriers Define the closed, total contract surface that P03-01 freezes and P03-03/04/05 generate against. Every failure in every layer (protocol, authorization, task, handler, output, presenter) maps to a stable contract error code and a stable CLI exit code; totality is a compile-time guarantee via Record<FailureLayer> and never-exhaustiveness switches. - error-families.ts: six FailureLayers, CONTRACT_EXIT_CODES, FAMILY_DEFAULTS (Record = totality by construction), STABLE_ERROR_REGISTRY, contractError, exitCodeForError, toErrorEnvelope, assertNever; six-layer exit proof + CLI-parity cross-check against adapters/cli.ts; 4 compile-time totality proofs. - envelope.ts: OutputKind (baseline/capped/degraded/error), classifyOutput, describeOutputKind (never-switch), capped/degraded carriers around the existing economy seam; re-exports canonical envelope schemas (no fork). - request-context.ts: authenticated context consuming P01-07 caller identity; protected fields stripped so callers cannot self-assert issuer/role/timestamp; replay identity returns stored result or a typed subject-conflict. - compatibility.ts: explicit version negotiation, directional migration (declares forward/backward), change-class taxonomy, mixed-version refusal. - contract-surface.ts: canonical structural serialization digested as the new additive 'contract-surface' pinned authority; lock re-approved (P03-02). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * P05-04: wire install freshness gating at the dispatch chokepoint Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * P06-05: atomic transition chokepoint, freeze-requirements projection, and cleanup Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(effect-ledger): declare filesystem ownership for install/ P05-04's collect-identity.ts introduced real filesystem effects under install/, which the census correctly flagged as INDETERMINATE_OWNER. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * P05-01: reproducible source-linked artifacts + signed release manifest Add servers/exarchos-mcp/src/release/: build-time source + contract identity, a signed (Ed25519 + canonical-JSON) release manifest embedding source/contract/install identity and raw-byte asset digests, and a fail-closed installer verifier (signature -> source -> contract -> asset) with an argv CLI seam. Wire tested verification primitives into get-exarchos.ps1/.sh delegating to that CLI. Reuses P03-01 contract-authority digesting, P05-04 install-identity, and P03-08 trust-root signing rather than duplicating them. JS bundle + the manifest/identity layer are byte-reproducible (pinned by test); the native bun --compile binary carries a 2-byte upstream nonce (reported, not fixed). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(effect-ledger): declare filesystem ownership for release/ P05-01's release-verify-cli.ts introduced filesystem effects under release/. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * P03-07: emit-once artifact agreement check + capability shim ratchet Add artifact-agreement checker that canonically digests each standard artifact (skills tree, binding block) as it appears across source / package / install / cache and fails on any disagreement, mirroring P03-01 authority-digest (text) and P05-04 install-identity (tree) normalization so Windows/Linux CI agree. Add a shim registry + ratchet enumerating capability-required thin shims, each with an approved capability reason + expiry; an unregistered or expired shim fails the ratchet. Mark the genuine per-runtime command-discovery adapter (copilot+cursor) with a SHIM governance marker. Implements API-008, CTR-011. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(module-intent): declare intent for census gates and the staged admission chokepoint The DR-7 module-intent gate was red on the integration branch. Two categories: - Four structural-conformance census modules (gate-ownership-census, effect-ledger, vcs-ownership, skill-example-validator) are test-invoked gate machinery by design - the same class as the existing *-seam.ts modules. Added to declared-test-infra with per-member rationales. - transition-command.ts is production code deliberately staged behind the legacy HSM cutover (P07-01 shadow -> P07-02 migrate -> P07-05 delete). Given a RESERVED header, which is exactly DR-7's intent. vcs/mutation-owner.ts remains dead-in-prod pending P04-05's call-site rewiring. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * P03-03: Build the deterministic contract compiler Compile the live Exarchos meta-model (derived from TOOL_REGISTRY + the P03-02 closed contract surface) into byte-stable runtime descriptors, schemas, a type manifest, a compatibility report, and proof fixtures. - meta-model.ts: typed ten-dimension policy model + Zod admission schema, derived from the live registry (task-layer error codes gated on task policy, CRLF-normalized descriptions for cross-platform stability). - compile.ts: fail-closed pipeline gated on verifyContractAuthority(); refuses missing/invalid policy fields and surface-incompatible error codes/output kinds/versions/schemas with typed diagnostics. - descriptors.ts / fixtures.ts / generate.ts: content-addressed descriptors, hoisted carrier schemas, and a checked-in proof-fixture baseline with a drift guard. Exit proof covered by tests: repeated generation is byte-identical; missing policy field, incompatible schema, and floating/unapproved authority all fail; output stable across key-order and CRLF/LF differences. Manual kill-probes confirmed each exit-proof test is discriminating. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * chore(module-intent): allowlist the contract-artifact generator entry point Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * P06-06: explainable decisions and safe schema-constrained remediation Add total explainDecision(TransitionDecided) surfacing per-requirement results, evidence references, policy identity, stable reason codes, and waived-but-recorded failures. Add remediateDenial mapping every PolicyDenyReason to a safe next_actions verb or a stable terminal reason (compile-time totality via assertNever). Prove remediation is data, not mutation, via a type-aware source-import census (remediation-purity). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * P07-01: Shadow decisions and event-sourced cutover gate Run the legacy HSM guard decision and the evidence-backed admission decision side-by-side (legacy stays authoritative, shadow is recording-only), classify typed disagreements, and gate enforcement enablement on deterministic-corpus + live-evidence conditions. - shadow-decision.ts: side-by-side runner, 5-class disagreement classifier (agree / legacy-allow-admission-deny / legacy-deny-admission-allow / admission-indeterminate / shadow-error), explained-vs-unexplained dispositions, event producers. Behaviour preservation is structural: the authoritative legacy decision is returned by reference; shadow adjudication is a try/catch thunk that can only ever yield shadow-error, never propagate. - cutover-gate.ts: 4 independent gate conditions (deterministic-corpus clean, >=20 live attempts, all phase kinds covered, both allow+deny outcomes) with a typed unmet-condition report; enforcement-enabled data producer throws unless the gate is satisfied (event-sourced flip). - hsm-transition-guard.ts: non-invasive, optional, error-isolated, passive shadowObserver hook surfacing the authoritative legacy outcome after the composite-guard walk. Absent in every production caller, so behaviour is byte-identical. Deterministic corpus (108 P06-01 fixtures): 9 disagreements, all legacy-allow-admission-deny, all explained-legacy (documented permissive guard characterizations + bypass fixtures), 0 unexplained. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * chore(module-intent): declare intent for P06-06 explanation and purity census Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * chore(module-intent): RESERVE the cutover gate pending live shadow evidence Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * P04-05: route real worktree and merge call sites through the VCS mutation owner Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * P06-07: event-sourced bootstrap and cross-version reassessment Bootstrap pre-admission workflows into a frozen requirement generation by appending admission.requirement-resolved facts only (no mutable backfill, historical replay unchanged, idempotent via decideOnce). Reassess an attempt under an explicit newer policy version: monotonicity judged by P06-03 atLeastAsStrong, a weakening requires an applicable/unexpired/authorized P06-04 waiver else fails closed. Authenticity guard binds supplied prior obligations to the persisted frozen digest. No event-registry changes. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * chore(module-intent): RESERVE P06-07 bootstrap and reassessment pending cutover Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(poc-acceptance): admit the admission chokepoint as an AtomicAppender consumer P06-05's transition-command.ts appends the admission decision and the phase lifecycle sibling in one decideOnce transaction - it consumes the substrate rather than hand-rolling a transaction, which is what the AC3 gate encourages. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * P05-03: prove generated projection containment (present + selected) Add a governed projection-containment proof (src/projection-containment.ts): enumerate every generated projection (skill, alias, agent, hook, manifest, instruction, runtime) from the renderers' own committed outputs, then verify each is PRESENT in the packaged layer by content digest (reusing P03-07's digestText) and SELECTED by resolution order (a stale/duplicate copy cannot win). Fail-closed typed diagnostics name the offending projection. Also proves each projection root is actually shipped via package.json files[] (checkShippedCoverage) and FIXES a genuine packaging gap: command-aliases/ was resolved at install time from the packaged root but excluded from files[], so installed opencode never received its canonical-name aliases. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * P03-04: generate MCP registration + verify one non-serializable binding per ActionId Derive the ActionId->handler binding table from dispatch's real composite-handler loaders (non-serializable function references, opaque branded holder), generate a deterministic MCP registration/discovery manifest from the compiled contract, and gate MCP server startup on verifyBindings so missing/duplicate/stale/non-function bindings fail BEFORE the server registers a single tool (API-004 exit proof). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * P07-07: govern advisories with a ratchet, add friction stop-and-simplify signals, pin tiered test timeouts Promote and retire ratchets (WFQ-015/017/018; dogfood exit criteria 10/14/15). - Advisory governance: src/advisory-registry.ts mirrors shim-registry — an ADVISORY(control: ...) marker declares existence in-source; owner, promotion + removal thresholds, expiry, kill fixture, unfiltered CI path, and issue live in ADVISORY_REGISTRY. verifyAdvisoryRatchet/assertAdvisoryRatchet + a typed AdvisoryRatchetError fail on an unregistered marker, a missing/invalid field, a past expiry, a stale entry, or a kill fixture that no longer fires. src/advisory-kill-probes.ts runs the REAL advisory against a seeded violation + clean control (lint-inv6 spawned via node; benchmark-regression via the real bash+jq script when present, else a portable port bound to the script's regression branch). Governs lint-inv6 + benchmark-regression; check-mutation-gate is reported as advisory theatre (its kill fixture cannot fire under --observe). - Friction signals: src/friction-signal.ts classifies a failed operation as infrastructure vs genuine test-failure and emits a stop-and-simplify signal only on repeated same-(operation,cause) infrastructure failure; a single red test never signals. Signatures cover the real failures this run hit (npm registry unreachable, non-atomic setup_worktree, vitest worker RPC timeout). - Timeout normalization: extend test/setup/vitest-config.test.ts to pin the MCP workspace's explicit testTimeout/hookTimeout and the coherent tiered ladder (unit 5s < process 15s < outcome 30s < mcp 60s) across both projects. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * P07-06: strengthen module boundaries with four ratcheted structural gates Add three test-invoked source-lint census seams under src/architecture and extend the runtime import-cycle detector, each a two-way ratchet (violation + stale-rule) with exit-proof tests (live tree passes; planted violation fails): - layer-boundaries-seam.ts: allowed-dependency layering. Freezes the exact cross-directory import surface of 16 foundation/peripheral layers; FORBIDDEN_IMPORT names both module ends, STALE_LAYER_ALLOWANCE rejects phantom cover. - effect-port-seam.ts: narrow effect ports built on the P04-01 effect-ledger vocabulary. Pins 5 curated modules to their exact effect footprint (finer than the ledger's layer grants); BROAD_EFFECT_CONTEXT + STALE_EFFECT_PORT. - adapter-ownership-seam.ts: direct adapter ownership, generalizing vcs-ownership beyond VCS. Confines the network adapter to its single owner (workflow/feedback.ts); DIRECT_ADAPTER_BYPASS + STALE_ADAPTER_OWNER. - import-cycles.ts: forbidden runtime back-edge registry generalizing the ad-hoc projection->store seam pin; FORBIDDEN_RUNTIME_EDGE + STALE_FORBIDDEN_EDGE over the depcruise graph. New seams consume effect-ledger.ts read-only. Named *-seam.ts so check-module-intent auto-classifies them as source-lint-seam. Manual kill-probes confirmed all discriminating tests go red under a stubbed census. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * P05-02: packaged action + CLI proof with live-denominator coverage ratchet Add a two-part packaged proof (ART-004/005/014): - Pure coverage engine + non-regression ratchet under src/parity/__tests__/packaged-proof.ts, deriving every denominator (actions, presentation aliases, host commands, error families, effect families, cancellation paths) from the LIVE registries (TOOL_REGISTRY / compiled contract / STABLE_ERROR_REGISTRY / EFFECT_OWNERSHIP) rather than a static list, plus a checked-in baseline the ratchet holds the line at. - Compiled-process proof under test/process/packaged-proof.test.ts that spawns the SHIPPED bun binary and drives every action as '<tool> <action> --json', asserting a contract envelope AND the stable CLI exit code (P03-02) for every observation, plus filesystem/process effect probes and a cooperative cancel round-trip. Coverage through the compiled binary: actions 120/120, presentationAliases 2/2, hostCommands 9/9, cancellationPaths 14/14, errorFamilies 2/6 (protocol+handler organically triggerable; authorization/output/presenter/ task pinned against the contract table in the unit test), effectFamilies 2/3 (network not hermetically exercisable). A seeded registered-but-unexercised action drops coverage and trips the ratchet (exit-proof b). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * P07-02: migrate built-in workflows to shared IR + wire live shadow observer Express the five built-in workflows (feature, debug, refactor, oneshot, discovery) in the shared admission IR and begin accumulating live cutover-gate evidence, without flipping enforcement (legacy guard stays authoritative until P07-05). - built-in-workflow-ir.ts: per-edge route conditions (P06-02 compiled edge-condition ASTs) + gate/approval obligations + phase-kind/provenance metadata for all 5 workflows. No import path to any legacy guard module (proved structurally). - legacy-state-translation.ts: the REAL legacy-state -> admission-evidence translation replacing P07-01's scenario proxy. Projects genuine legacy state into the closed fact vocabulary and mints schema-valid, content-addressed (sha256) AdmissionEvidence/Requirement records; adjudicates route AND policy. Scenario-blind: identical state -> identical verdict (anti-tautology). - live-shadow-observer.ts: bounded in-memory sink + error-isolated observer that runs the admission engine beside the authoritative legacy decision and records the LiveShadowAttempt substrate the RESERVED cutover gate consumes. - hsm-transition-guard.ts: extracted notifyShadowObserver helper; extended the shadow seam to the two custom-guard early-return deny paths. Behaviour- preserving and error-isolated. - tools.ts: wired the live observer into the single production transition caller. Corpus disagreement delta: the real state-driven translation surfaces 6 disagreements (down from P07-01's 9) — all genuine P06-01 guard-soundness defects, all safe-direction (legacy allow / admission deny), all explained. The 3 removed were scenario-proxy artifacts. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * P05-05: generate + gate the reachability graph (closure capstone) Assemble a per-action reachability graph from the frozen upstream authorities and gate on complete closure: every public action must have exactly one path from authored ActionId -> schema -> route -> handler -> [effect owner] -> output -> artifact -> packaged fixture. A break or ambiguity (two handlers/owners) at any applicable hop fails closed, naming the action and the hop. The effect-owner hop is conditional (mutating actions only) via a governed provider map validated against the P04-01 effect ledger. Checked-in generated/reachability-graph.json with a drift guard; live tree closes 120/120 public actions. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * P03-05: generate CLI client over the MCP contract + dispatch-closure census Generate a deterministic, byte-stable CLI client surface FROM the compiled contract (P03-03), with a checked-in golden (generated/cli-surface.json) and a drift guard following the proof-fixture pattern. The CLI is a projection of the same contract handler as the MCP wire, not a separate dispatch facade. Structural census (cli-contract-seam.ts) proves 'API actions have no direct CLI-to-dispatch path' via two two-way-ratchet collectors: (1) dispatch-seam containment - only adapters/cli.ts + adapters/mcp.ts import the runtime dispatch value; (2) CLI command classification - every live Commander command is an api-action group / presentation alias / declared host-local command, respecting the host-local classification. Differential fixtures drive the same ToolResult through the real CLI tree and the MCP envelope projection: rendering + stable exit codes agree by construction. resolveExitCode now delegates to the frozen P03-02 exit-code authority (exitCodeForError), a superset of the prior ladder for every handler-reachable code (protocol family now to 1, PRESENTER_ERROR to 3). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * P03-06: Extend and consume shared admission IR Author the shared cross-product admission IR as a single Zod source (no TypeSpec toolchain is available offline) and derive a checked-in, byte-stable JSON Schema artifact from it. The IR expresses admission policies + references, the closed 7-node edge-condition set, evidence requirement models, waiver + approval wire models, and action references, and is closed by construction (strict objects, closed unions, scalar leaves) so it cannot carry a shell command, closure, harness syntax, or Exarchos binding. Exit proofs: - Round-trip: a shared fixture corpus is validated by BOTH the generated JSON Schema (Ajv 2020) and the authored Zod runtime validators, which must agree accept/reject on every fixture; edge conditions add a three-way check against the runtime compileEdgeCondition (P06-02). - Reject dangling references: policy/action/requirement references are resolved, action refs against the live P03-04 ActionId source; dangling or duplicate ids fail. Also adds a builder that lowers to the shared IR (transition tasks 033/047), leaving decision parity to P07-03, and a drift guard over the generated artifact. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * P03-09: add an independent contract-vs-behavior oracle Compare the declared contract against OBSERVED behavior across five axes (incorrect handler, missing authorization, undeclared effect, malformed output, compatibility break), by a route independent of the generation pipeline. Seeded breaks share a byte-identical declaration with their correct twin, so the generation route emits an identical artifact (the drift guards stay green) yet the oracle catches each break by observing behavior. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * P04-02: mint cancellation capability for trusted CLI callers The CLI trusted-caller path wires no runtime capability resolver, so snapshotCallerAuthorization produced an empty capability set for the local-operator identity. handleCancel then built a cancellation-request authorization snapshot with an empty capabilityIds array, which Authorizatio…
1 parent bf3d6df commit 010d84c

623 files changed

Lines changed: 118337 additions & 2926 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.exarchos.yml

Lines changed: 16 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -45,29 +45,25 @@ plugins:
4545
impeccable:
4646
enabled: true
4747

48-
# invariants-catalog — opt-in to the dev-invariants catalog.
49-
# This file IS the explicit opt-in for Exarchos contributors. The loader
50-
# defaults to disabled even inside the Exarchos repo; entries only surface
51-
# at /ideate Phase 0 because this committed file declares the flag here.
52-
# Consumers using Exarchos as a plugin in another repo never see these
53-
# entries unless they add the same key to their own .exarchos.yml.
48+
# invariants-catalog — register the catalogs this repo loads.
5449
#
55-
# The CANONICAL registration is the explicit `catalogs:` entry below: the dev
50+
# Registration in `catalogs:` is the ONE AND ONLY opt-in (DR-31). The dev
5651
# catalog (relocated to `.exarchos/invariants.md` in T19) is registered exactly
57-
# like a user catalog — a `{ path, tier }` object — so it flows through the same
58-
# `resolveCatalogSources` discovery surface every consumer catalog uses.
52+
# like a user catalog would be — a `{ path, tier }` object — so it flows through
53+
# the same `resolveCatalogSources` discovery surface every consumer catalog
54+
# uses. `tier: dev` carries the audience scoping, nothing else.
5955
#
60-
# `devCatalog: enabled` is back-compat SUGAR that ALSO desugars to
61-
# `{ path: .exarchos/invariants.md, tier: dev }`. We KEEP it here intentionally
62-
# (design §4.3) ONLY to satisfy the legacy direct-load gate that the explicit
63-
# registration does not yet drive: `vocabulary-lint` and the `/ideate` Phase-0
64-
# `loadInvariants` path still read the boolean directly. The catalog-sources
65-
# path-dedup collapses the sugar and the explicit registration into ONE dev
66-
# source (proven by RepoConfig_ExplicitDevRegistration_DedupesWithSugar and
67-
# RepoConfig_DesugaredDevSource_MatchesGoldenSnapshot in
68-
# servers/exarchos-mcp/src/architecture/resolve-effective-catalog.test.ts), so
69-
# there is no double-registration. Full retirement of the `devCatalog` boolean
70-
# is a tracked follow-up — NOT this task.
56+
# This repository therefore consumes its own invariants precisely the way a
57+
# downstream consumer does: a local `.exarchos.yml` pointing at a local catalog
58+
# file. There is no repo-only loading mode. Entries surface at /ideate Phase 0
59+
# because THIS registration exists, not because the loader detected anything;
60+
# consumers using Exarchos as a plugin in another repo see nothing here unless
61+
# they register their own catalog in their own `.exarchos.yml`.
62+
#
63+
# The retired `devCatalog: enabled` boolean is GONE (T-43). It survives only as
64+
# a deprecated `.exarchos.yml` alias that the config schema desugars into the
65+
# registration below and reports via `exarchos doctor`; it is not written here
66+
# and no code reads it.
7167
#
7268
# To author new entries through the wizard, run `/exarchos:invariants` (see
7369
# docs/guides/authoring-invariants.md).
@@ -77,6 +73,5 @@ plugins:
7773
# docs/proposals/2026-05-20-invariants-catalog-v2-spec.md §4.0
7874
# docs/designs/2026-05-25-invariants-authoring-wizard.md §4.3
7975
invariants:
80-
devCatalog: enabled
8176
catalogs:
8277
- { path: .exarchos/invariants.md, tier: dev }

.exarchos/invariants.md

Lines changed: 113 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,8 @@ invariants:
5454
- atomic-appender
5555
- stream-lock-manager
5656
summary: >
57-
Concurrency is serialized in two tiers. Tier 1 (in-process): the
57+
Concurrency is serialized in two tiers. This is a CLOSED claim, not a
58+
target (DR-19 / EFF-001, closed 2026-08-04). Tier 1 (in-process): the
5859
StreamLockManager runs concurrent same-stream appends sequentially via a
5960
per-stream Promise-chain mutex. Tier 2 (cross-process): SQLite WAL with
6061
BEGIN IMMEDIATE acquires the write lock up-front, and a per-stream version
@@ -64,7 +65,16 @@ invariants:
6465
backstop, not the conflict detector. Plain appends serialize
6566
transparently; only a genuine OCC mismatch (a stale expectedSequence)
6667
surfaces a conflict, carrying expected/actual directly. No process-level
67-
mutex, no PID lock, no advisory file.
68+
mutex, no PID lock, no advisory file. WITNESS: three genuine OS child
69+
processes drive the production SqliteBackend through the production driver
70+
against one SQLite file, held in the write-lock queue simultaneously and
71+
asserting an interleaving witness (a run that did not actually contend
72+
fails), plus a startup version-gate repair arm. Weakening BEGIN IMMEDIATE
73+
to a deferred BEGIN, disabling the startup repair, or replacing the driver
74+
with a no-op each turn the corresponding test RED — the fixture cannot
75+
pass vacuously. Cross-process linearization is therefore asserted
76+
categorically: a change that cannot keep that fixture green is a
77+
violation, not a caveat.
6878
citations:
6979
- "Mohan et al., *ARIES* (ACM TODS 1992):
7080
https://dl.acm.org/doi/10.1145/128765.128770"
@@ -74,6 +84,8 @@ invariants:
7484
- "SQLite WAL documentation: https://sqlite.org/wal.html"
7585
references:
7686
- servers/exarchos-mcp/src/event-store/atomic-appender.ts
87+
- servers/exarchos-mcp/src/storage/sqlite-backend.ts
88+
- servers/exarchos-mcp/test/process/multi-process-append.test.ts
7789
- docs/architecture/runtime.md#§4
7890

7991
- id: INV-8
@@ -185,25 +197,45 @@ invariants:
185197
enforcement:
186198
mode: audit
187199
audit-prompt: >
188-
Is each agent's authority bounded by construction rather than by
189-
convention? A read-only agent must be unable to mutate the working tree;
190-
a task-isolated agent must be unable to write outside its worktree. Flag
191-
a posture asserted in prose but not enforced at the capability boundary.
200+
Is each agent's authority bounded by the chokepoint that owns the
201+
resource, rather than by convention? STATE authority is enforced in the
202+
dispatch/MCP handler — a read-only agent must be unable to invoke a
203+
mutating action. PROCESS LIFECYCLE and top-level worktree placement are
204+
enforced by the spawn-bounded launcher. SPATIAL write confinement is
205+
NOT launcher-owned: it is a per-harness capability that must be
206+
reported as prevention | detection | advisory | unavailable, and must
207+
never be inferred from the launcher's cwd or worktree ownership. Flag a
208+
posture asserted in prose but not enforced at a capability boundary,
209+
and flag any claim that a task-isolated agent CANNOT write outside its
210+
worktree where the harness's declared spatial posture is advisory or
211+
unavailable.
192212
axis: substrate
193213
cost-of-load: always-load
194214
applies-to:
195215
- agent-spec
196216
- capability-resolver
197217
- handshake
198218
- sub-agent-dispatch
219+
- launcher
199220
summary: >
200221
Every agent declares one of three postures in agent spec YAML: read-only |
201222
task-isolated | shared-mutating. The MCP initialize handshake declares the
202223
runtime half. The capability resolver merges posture with handshake;
203-
mismatches resolve to the handshake (handshake-authoritative). Postures
204-
are unrepresentable-by-construction — a read-only agent cannot mutate the
205-
working tree; a task-isolated agent cannot write outside its assigned
206-
worktree.
224+
mismatches resolve to the handshake (handshake-authoritative). What a
225+
posture makes unrepresentable-by-construction is bounded by the chokepoint
226+
that owns the resource: STATE authority in the dispatch/MCP handler (a
227+
read-only agent cannot invoke a mutating action), PROCESS LIFECYCLE and
228+
top-level worktree placement in the spawn-bounded launcher. SPATIAL write
229+
confinement is deliberately EXCLUDED from the by-construction claim: no
230+
component in the single-machine frame owns the kernel write path, and
231+
harness-created nested worktrees sit outside the launcher's reach, so
232+
filesystem confinement is a DECLARED per-harness capability carrying a
233+
posture of prevention | detection | advisory | unavailable — never
234+
inferred from launcher cwd/worktree ownership. Lifecycle ownership and
235+
spatial isolation are separate audit dimensions; absorbing spatial
236+
confinement into this invariant requires the space-moat fork (an upstream
237+
Bash-covering hook standard or a kernel sandbox), and asserting it before
238+
that lands is an overclaim, not an invariant.
207239
citations:
208240
- "Mark S. Miller, *Robust Composition* (PhD dissertation, JHU 2006):
209241
https://papers.agoric.com/papers/robust-composition/full-text"
@@ -216,6 +248,7 @@ invariants:
216248
references:
217249
- servers/exarchos-mcp/src/capabilities/resolver.ts
218250
- servers/exarchos-mcp/src/agents/generate-agents.ts
251+
- servers/exarchos-mcp/src/launcher/create-worktree.ts
219252
- docs/architecture/runtime.md#§7
220253

221254
- id: INV-12
@@ -398,35 +431,65 @@ invariants:
398431
- docs/architecture/runtime.md#§8
399432

400433
- id: INV-2
401-
dimension: facade-equivalence
434+
dimension: contract-client-equivalence
402435
integrity-class: substrate
403436
phase-affinity: [ review ]
404437
severity:
405438
default: advisory
406439
enforcement:
407-
# mode:audit, not check: a grep for "behavior in an adapter file" is a
408-
# low-precision proxy that cannot prove parity and would false-positive
409-
# on legitimate adapter code. Parity is proven by the parity-harness
410-
# tests; the reviewer judges adapter discipline.
440+
# mode:audit, not check: "is this module a GENERATED client of the
441+
# compiled contract?" is a whole-tree structural question, not a
442+
# line-oriented diff property, and a grep for "behavior in an adapter
443+
# file" would false-positive on legitimate presentation code. The
444+
# MECHANICAL backstops are the dispatch-seam containment census and the
445+
# DR-25 deviation ledger in `contract/cli/cli-contract-seam.ts` — every
446+
# non-projection module importing the runtime `dispatch` value must carry
447+
# a governed, unexpired ledger row, and a row covering nothing fails as
448+
# STALE_DEVIATION. The parity harnesses are a WITNESS, never the proof.
411449
mode: audit
412450
audit-prompt: >
413-
Do the CLI and MCP adapters carry only presentation, with all behavior
414-
in the shared dispatch core? Flag logic added to adapters/cli.ts or
415-
adapters/mcp.ts beyond formatting, and any verb lacking a parity or
416-
registered outputSchema guarantee.
451+
Does this change reach the shared contract handler through the compiled
452+
contract, or does it hand-assemble a call to the runtime `dispatch`
453+
value? Any module outside CONTRACT_PROJECTIONS that imports `dispatch`
454+
must be covered by a governed, unexpired row in CLI_CONTRACT_DEVIATIONS
455+
carrying an owner, a rationale, a retirement condition and an expiry.
456+
Flag a new direct-dispatch path with no ledger row, behavior added to
457+
adapters/cli.ts or adapters/mcp.ts beyond presentation, and any verb
458+
lacking a registered outputSchema. Do NOT accept a passing parity
459+
fixture as evidence that two hand-written surfaces are equal by
460+
construction.
417461
axis: substrate
418462
cost-of-load: always-load
419463
applies-to:
420-
- cli-adapter
464+
- contract-compiler
465+
- cli-client
421466
- mcp-adapter
422467
- dispatch-core
423-
- parity-tests
468+
- deviation-ledger
424469
summary: >
425-
CLI and MCP are both facades over a single functional dispatch core. For
426-
any verb, the same DispatchContext + arguments must produce the same
427-
ToolResult. Adapters carry zero behavior — only presentation. Post-#1266,
428-
every action also registers a Zod outputSchema so parity is schema-checked
429-
in addition to byte-checked.
470+
The MCP wire projection of the compiled contract is the invocation
471+
surface; the CLI is a CLIENT of that same contract, equal to the wire BY
472+
CONSTRUCTION rather than by hand-coordination — not a peer facade kept in
473+
step by fixtures. Behavior lives in the shared dispatch core; a client
474+
carries presentation only (argv parsing, exit codes, stdio framing, error
475+
rendering, carrier translation). Byte- and schema-equivalence across
476+
carriers (the parity harnesses plus each action's registered Zod
477+
outputSchema) is the WITNESS of that construction, never the invariant
478+
itself: a suite of green parity fixtures does not make two hand-written
479+
surfaces equal. The shipped `adapters/cli.ts` meets this framing for
480+
dispatch ADDRESSING: every api-action call site addresses its action by
481+
contract ActionId through the generated client
482+
(`contract/cli/generated-client.ts`, a contract projection) which
483+
verifies the id against the compiled surface before dispatching, so an
484+
action the contract does not compile cannot be addressed and the adapter
485+
imports no runtime `dispatch` value; the Commander tree it keeps is
486+
hand-authored presentation. The DR-25 deviation that previously covered
487+
the adapter's hand-assembled direct dispatch path
488+
(`cli-direct-dispatch`) is RETIRED and CLI_CONTRACT_DEVIATIONS is empty;
489+
the census machinery stays armed, so any future direct route to the
490+
dispatch core must be a contract projection or record a new governed,
491+
owned, expiring deviation — an acknowledged, expiring debt AGAINST this
492+
invariant, never a weakening OF it.
430493
citations:
431494
- "Alistair Cockburn, *Hexagonal Architecture (Ports & Adapters)* (2005):
432495
https://alistair.cockburn.us/hexagonal-architecture/"
@@ -436,6 +499,8 @@ invariants:
436499
https://modelcontextprotocol.io/specification/2025-06-18/server/tools"
437500
references:
438501
- docs/architecture/invariants/references/INV-2-facade-equivalence.md
502+
- servers/exarchos-mcp/src/contract/cli/cli-contract-seam.ts
503+
- servers/exarchos-mcp/src/contract/cli/generated-client.ts
439504
- servers/exarchos-mcp/src/orchestrate/check-invariant-conformance.ts
440505
- docs/designs/archive/2026-05-07-milestone-16-mcp-alignment.md
441506

@@ -512,14 +577,22 @@ invariants:
512577
- skills-renderer
513578
- commands
514579
summary: >
515-
Skills, rules, and workflows must not couple to any single harness. Six
516-
runtimes are first-class (Claude Code, Codex, Copilot, Cursor, OpenCode,
517-
generic). Runtime-specific text is tokenized via {{TOKEN}} placeholders or
518-
guarded via <!-- requires:* --> blocks. Source-of-truth edits go to
519-
skills-src/; skills/<runtime>/** is generated. INV-4 owns the *platform*
520-
axis (6 runtimes); INV-6 owns the orthogonal *workload* axis (workflow
521-
types). The two are complementary substrate properties — substrate
522-
guarantees hold across both axes.
580+
Authored content is emitted ONCE as a standard-conformant artifact
581+
wherever a standard converged — Agent Skills (SKILL.md), AGENTS.md, and
582+
MCP — and each harness reads it natively; the only residual per-harness
583+
variance is the tool prefix, carried by a bare logical name the agent
584+
resolves from its own tool list. Per-runtime fan-out is TECHNICAL DEBT,
585+
not the target architecture: a thin shim survives only where NO standard
586+
exists, and every residual shim carries an owner, the capability reason it
587+
exists, and a retirement condition. Conformance plus shim minimization —
588+
not render-parity across N runtime variants — is the metric, because a
589+
byte-perfect per-harness render proves the artifacts match, not that the
590+
guarantee holds. Source-of-truth edits go to skills-src/; everything under
591+
skills/** is generated build output and is never edited directly.
592+
Runtime-specific text is tokenized via {{TOKEN}} placeholders or guarded
593+
via <!-- requires:* --> blocks. INV-4 owns the *harness* axis; INV-6 owns
594+
the orthogonal *workload* axis (workflow types) and INV-16 the orthogonal
595+
*OS* axis — substrate guarantees hold across all three.
523596
citations:
524597
- "Andrew Hunt & David Thomas, *The Pragmatic Programmer* — DRY / Single
525598
Source of Truth (Addison-Wesley 1999):
@@ -746,8 +819,8 @@ invariants:
746819
unlinking an open file, unlike POSIX); package-manager spawns resolve
747820
their .cmd shim via resolveExecutable; module-relative paths use
748821
fileURLToPath. INV-16 owns the *OS* axis; INV-4 owns the orthogonal
749-
*harness* axis (6 AI runtimes) — both are platform-agnosticity substrate
750-
properties.
822+
*harness* axis (standards-conformance plus thin shims) — both are
823+
platform-agnosticity substrate properties.
751824
citations:
752825
- "Node.js, *Path* (OS-specific separators; fs accepts '/' on Windows):
753826
https://nodejs.org/api/path.html"
@@ -797,9 +870,10 @@ invariants:
797870
registry-enumeration snapshot. The budget and escape-hatch are properties
798871
of the canonical response contract — declared in the registry descriptor,
799872
enforced in the shared core, rendered through a presentation seam — never
800-
special-cased in one facade. This carries the INV-2 reframe (#1608: the
801-
CLI is a presentation client over the MCP contract, equivalence by
802-
construction) and the facade-codegen direction (system-design 05) forward:
873+
special-cased in one facade. This is downstream of the GOVERNING INV-2
874+
(the CLI is a client of the compiled contract, equivalence by
875+
construction — re-approved under DR-26, superseding the #1608 pending
876+
note) and of the facade-codegen direction (system-design 05):
803877
the registered outputSchema must be total over every emittable shape
804878
(baseline + capped + degraded), the precondition that makes facade
805879
equivalence hold by construction. INV-17 is the response-economy

0 commit comments

Comments
 (0)