Commit 010d84c
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
- .exarchos
- .github/workflows
- scripts
- audit
- test-fixtures
- servers/exarchos-mcp
- src
- __tests__
- tasks
- workflow
- adapters
- architecture
- __fixtures__
- __snapshots__
- artifacts
- capabilities
- channel
- commands
- config
- contract
- bindings
- cli
- generated
- compiler
- generated
- ir
- generated
- oracle
- reachability
- generated
- core
- ctk
- __fixtures__
- dispatch
- evals/benchmarks/seeded-defects
- fixtures
- event-store
- __tests__
- extensions
- install
- orchestrate
- doctor
- checks
- init
- writers
- invariants
- onboard
- pure
- worktree
- parity
- __tests__
- projections
- quality
- release
- runbooks
- runtime
- storage
- tasks
- test-helpers
- utils
- vcs
- views
- workflow
- __fixtures__
- admission
- retirement
- test
- integration
- governance
- public-root
- suite-invariants
- packaged
- process
- skills-src
- debug
- references
- delegate
- references
- ideate
- references
- oneshot
- plan
- refactor
- references
- review/references
- shepherd
- references
- synthesize/references
- skills
- claude
- delegate
- references
- ideate
- references
- refactor
- references
- codex
- delegate
- references
- ideate
- references
- refactor
- references
- copilot
- delegate
- references
- ideate
- references
- refactor
- references
- cursor
- delegate
- references
- ideate
- references
- refactor
- references
- generic
- delegate
- references
- ideate
- references
- refactor
- references
- opencode
- delegate
- references
- ideate
- references
- refactor
- references
- standard
- debug
- references
- oneshot
- plan
- review/references
- shepherd
- references
- synthesize/references
- src
- operations
- test
- fixtures
- migration
- __fixtures__
- batch-baselines
- __snapshots__
- process
- setup
Some content is hidden
Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
45 | 45 | | |
46 | 46 | | |
47 | 47 | | |
48 | | - | |
49 | | - | |
50 | | - | |
51 | | - | |
52 | | - | |
53 | | - | |
| 48 | + | |
54 | 49 | | |
55 | | - | |
| 50 | + | |
56 | 51 | | |
57 | | - | |
58 | | - | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
59 | 55 | | |
60 | | - | |
61 | | - | |
62 | | - | |
63 | | - | |
64 | | - | |
65 | | - | |
66 | | - | |
67 | | - | |
68 | | - | |
69 | | - | |
70 | | - | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
71 | 67 | | |
72 | 68 | | |
73 | 69 | | |
| |||
77 | 73 | | |
78 | 74 | | |
79 | 75 | | |
80 | | - | |
81 | 76 | | |
82 | 77 | | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
54 | 54 | | |
55 | 55 | | |
56 | 56 | | |
57 | | - | |
| 57 | + | |
| 58 | + | |
58 | 59 | | |
59 | 60 | | |
60 | 61 | | |
| |||
64 | 65 | | |
65 | 66 | | |
66 | 67 | | |
67 | | - | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
68 | 78 | | |
69 | 79 | | |
70 | 80 | | |
| |||
74 | 84 | | |
75 | 85 | | |
76 | 86 | | |
| 87 | + | |
| 88 | + | |
77 | 89 | | |
78 | 90 | | |
79 | 91 | | |
| |||
185 | 197 | | |
186 | 198 | | |
187 | 199 | | |
188 | | - | |
189 | | - | |
190 | | - | |
191 | | - | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
| 211 | + | |
192 | 212 | | |
193 | 213 | | |
194 | 214 | | |
195 | 215 | | |
196 | 216 | | |
197 | 217 | | |
198 | 218 | | |
| 219 | + | |
199 | 220 | | |
200 | 221 | | |
201 | 222 | | |
202 | 223 | | |
203 | | - | |
204 | | - | |
205 | | - | |
206 | | - | |
| 224 | + | |
| 225 | + | |
| 226 | + | |
| 227 | + | |
| 228 | + | |
| 229 | + | |
| 230 | + | |
| 231 | + | |
| 232 | + | |
| 233 | + | |
| 234 | + | |
| 235 | + | |
| 236 | + | |
| 237 | + | |
| 238 | + | |
207 | 239 | | |
208 | 240 | | |
209 | 241 | | |
| |||
216 | 248 | | |
217 | 249 | | |
218 | 250 | | |
| 251 | + | |
219 | 252 | | |
220 | 253 | | |
221 | 254 | | |
| |||
398 | 431 | | |
399 | 432 | | |
400 | 433 | | |
401 | | - | |
| 434 | + | |
402 | 435 | | |
403 | 436 | | |
404 | 437 | | |
405 | 438 | | |
406 | 439 | | |
407 | | - | |
408 | | - | |
409 | | - | |
410 | | - | |
| 440 | + | |
| 441 | + | |
| 442 | + | |
| 443 | + | |
| 444 | + | |
| 445 | + | |
| 446 | + | |
| 447 | + | |
| 448 | + | |
411 | 449 | | |
412 | 450 | | |
413 | | - | |
414 | | - | |
415 | | - | |
416 | | - | |
| 451 | + | |
| 452 | + | |
| 453 | + | |
| 454 | + | |
| 455 | + | |
| 456 | + | |
| 457 | + | |
| 458 | + | |
| 459 | + | |
| 460 | + | |
417 | 461 | | |
418 | 462 | | |
419 | 463 | | |
420 | | - | |
| 464 | + | |
| 465 | + | |
421 | 466 | | |
422 | 467 | | |
423 | | - | |
| 468 | + | |
424 | 469 | | |
425 | | - | |
426 | | - | |
427 | | - | |
428 | | - | |
429 | | - | |
| 470 | + | |
| 471 | + | |
| 472 | + | |
| 473 | + | |
| 474 | + | |
| 475 | + | |
| 476 | + | |
| 477 | + | |
| 478 | + | |
| 479 | + | |
| 480 | + | |
| 481 | + | |
| 482 | + | |
| 483 | + | |
| 484 | + | |
| 485 | + | |
| 486 | + | |
| 487 | + | |
| 488 | + | |
| 489 | + | |
| 490 | + | |
| 491 | + | |
| 492 | + | |
430 | 493 | | |
431 | 494 | | |
432 | 495 | | |
| |||
436 | 499 | | |
437 | 500 | | |
438 | 501 | | |
| 502 | + | |
| 503 | + | |
439 | 504 | | |
440 | 505 | | |
441 | 506 | | |
| |||
512 | 577 | | |
513 | 578 | | |
514 | 579 | | |
515 | | - | |
516 | | - | |
517 | | - | |
518 | | - | |
519 | | - | |
520 | | - | |
521 | | - | |
522 | | - | |
| 580 | + | |
| 581 | + | |
| 582 | + | |
| 583 | + | |
| 584 | + | |
| 585 | + | |
| 586 | + | |
| 587 | + | |
| 588 | + | |
| 589 | + | |
| 590 | + | |
| 591 | + | |
| 592 | + | |
| 593 | + | |
| 594 | + | |
| 595 | + | |
523 | 596 | | |
524 | 597 | | |
525 | 598 | | |
| |||
746 | 819 | | |
747 | 820 | | |
748 | 821 | | |
749 | | - | |
750 | | - | |
| 822 | + | |
| 823 | + | |
751 | 824 | | |
752 | 825 | | |
753 | 826 | | |
| |||
797 | 870 | | |
798 | 871 | | |
799 | 872 | | |
800 | | - | |
801 | | - | |
802 | | - | |
| 873 | + | |
| 874 | + | |
| 875 | + | |
| 876 | + | |
803 | 877 | | |
804 | 878 | | |
805 | 879 | | |
| |||
0 commit comments