fix(macp): fail-closed typed gate states — RI-2-002 (#1275) #1293

Merged
jarvis merged 1 commits from fix/ri-050-macp-fail-closed into next 2026-08-18 05:56:39 +00:00
Collaborator

Card RI-2-002 — MACP fail-closed gates (RI-N2, SDLC-D-035, #1275)

Worker card for the alpha 0.0.50 release-integrity workstream, executed by a pi zai/glm-5.3:high worker under fargo, independently verified (gates re-run, sabotage control reproduced) by the dispatching seat before push.

The defect

Four false-success paths in packages/macp (all verified in source at base 8199261):

  1. ci-pipeline gates returned a placeholder — exit_code: 0, output 'CI pipeline gate placeholder', passed: true. An unimplemented CI-provider gate was treated as passing.
  2. Empty-command gates returned passed: true.
  3. runGates() silently SKIPPED empty-command gates entirely — a pass by omission.
  4. Every macp CLI stub (tasks list, submit, gate, events tail) printed "not yet wired" and exited 0.

The fix — typed states, fail closed, explicit simulation

  • GateStatus closed union passed | failed | simulated | waiting | capability_failure as a required discriminator on every GateResult, with the invariant passed === (status === 'passed') — the boolean is now a true view of "really executed and really green".
  • Missing capabilities typed twice: gate status capability_failure with a capability_code from the new MACP_ERROR_CODES (mirroring Forge RI-2-001's FORGE_NO_* vocabulary so the packages speak the same language), and MACPCapabilityError at the CLI surface.
  • manual gate type with no automation → typed waiting (MACP_AUTHORITY_REQUIRED): neither pass nor fail; the aggregate is not passed while any gate waits.
  • Aggregate RunGatesResult.state with precedence capability_failure > simulated > failed > waiting > passed; allPassed === (state === 'passed'). A simulated or waiting result can never satisfy a dependency, gate, or release check. Simulation requires explicit --simulate (or simulate: true); it may complete (exit 0, because the caller asked) but reports aggregate state simulated, never passed.
  • All four false-success paths removed: ci-pipeline placeholder → capability_failure; empty command → capability_failure; silent skip removed (every gate yields a typed result + event); CLI stubs exit 1 with typed MACP_NOT_IMPLEMENTED. macp gate is now genuinely wired to runGates (inline command, JSON gates array, task file with quality_gates, or one-command-per-line file) with --simulate.

Tests — 88 → 109, negative controls are the point

Every negative control asserts BOTH the typed status AND that the aggregate is not passed: empty command, unimplemented ci-pipeline, manual gate, unimplemented CLI capability, simulated-never-passes.

Sabotage control, reproduced independently by the dispatching seat (not trusted from the worker's report): gate-runner.ts reverted to base's false-success behavior → 19 failed / 90 passed — exactly the new/updated fail-closed tests (13 gate-runner RI-N2 controls, 4 rewritten legacy defect tests, 2 CLI wiring tests), zero pre-existing failures. Restored byte-identically (cmp + sha256), 109/109 green again.

Gates (re-run by the dispatching seat, rc-honest)

packages/macp build rc=0 · lint rc=0 · vitest 109/109 rc=0 · root build 25/25 rc=0 · root typecheck 45/45 rc=0. Scope: only packages/macp/**.

Known follow-up (not this card)

Root eslint allowDefaultProject does not cover packages/*/__tests__/*.ts; the legacy __tests__/gate-runner.test.ts was consolidated into src/gate-runner.spec.ts rather than touching the root allowlist. Same gap exists for packages/forge/__tests__/. Recommend a small follow-up card.

## Card RI-2-002 — MACP fail-closed gates (RI-N2, SDLC-D-035, #1275) Worker card for the alpha 0.0.50 release-integrity workstream, executed by a pi `zai/glm-5.3:high` worker under `fargo`, independently verified (gates re-run, sabotage control reproduced) by the dispatching seat before push. ## The defect Four false-success paths in `packages/macp` (all verified in source at base `8199261`): 1. `ci-pipeline` gates returned a placeholder — `exit_code: 0`, output `'CI pipeline gate placeholder'`, `passed: true`. An unimplemented CI-provider gate was treated as passing. 2. Empty-command gates returned `passed: true`. 3. `runGates()` silently SKIPPED empty-command gates entirely — a pass by omission. 4. Every `macp` CLI stub (`tasks list`, `submit`, `gate`, `events tail`) printed "not yet wired" and exited 0. ## The fix — typed states, fail closed, explicit simulation - `GateStatus` closed union `passed | failed | simulated | waiting | capability_failure` as a required discriminator on every `GateResult`, with the invariant `passed === (status === 'passed')` — the boolean is now a true view of "really executed and really green". - Missing capabilities typed twice: gate status `capability_failure` with a `capability_code` from the new `MACP_ERROR_CODES` (mirroring Forge RI-2-001's `FORGE_NO_*` vocabulary so the packages speak the same language), and `MACPCapabilityError` at the CLI surface. - `manual` gate type with no automation → typed `waiting` (`MACP_AUTHORITY_REQUIRED`): neither pass nor fail; the aggregate is not `passed` while any gate waits. - Aggregate `RunGatesResult.state` with precedence `capability_failure > simulated > failed > waiting > passed`; `allPassed === (state === 'passed')`. A `simulated` or `waiting` result can never satisfy a dependency, gate, or release check. Simulation requires explicit `--simulate` (or `simulate: true`); it may *complete* (exit 0, because the caller asked) but reports aggregate state `simulated`, never `passed`. - All four false-success paths removed: ci-pipeline placeholder → `capability_failure`; empty command → `capability_failure`; silent skip removed (every gate yields a typed result + event); CLI stubs exit **1** with typed `MACP_NOT_IMPLEMENTED`. `macp gate` is now genuinely wired to `runGates` (inline command, JSON gates array, task file with `quality_gates`, or one-command-per-line file) with `--simulate`. ## Tests — 88 → 109, negative controls are the point Every negative control asserts BOTH the typed status AND that the aggregate is not `passed`: empty command, unimplemented ci-pipeline, manual gate, unimplemented CLI capability, simulated-never-passes. **Sabotage control, reproduced independently by the dispatching seat** (not trusted from the worker's report): `gate-runner.ts` reverted to base's false-success behavior → **19 failed / 90 passed** — exactly the new/updated fail-closed tests (13 gate-runner RI-N2 controls, 4 rewritten legacy defect tests, 2 CLI wiring tests), zero pre-existing failures. Restored byte-identically (`cmp` + sha256), **109/109 green** again. ## Gates (re-run by the dispatching seat, rc-honest) `packages/macp` build rc=0 · lint rc=0 · vitest **109/109** rc=0 · root build **25/25** rc=0 · root typecheck **45/45** rc=0. Scope: only `packages/macp/**`. ## Known follow-up (not this card) Root eslint `allowDefaultProject` does not cover `packages/*/`**`__tests__/`**`*.ts`; the legacy `__tests__/gate-runner.test.ts` was consolidated into `src/gate-runner.spec.ts` rather than touching the root allowlist. Same gap exists for `packages/forge/__tests__/`. Recommend a small follow-up card.
mos-dt-0 added 1 commit 2026-08-17 21:43:41 +00:00
RI-N2 / SDLC-D-035. Every GateResult now carries a typed status
discriminator (passed|failed|simulated|waiting|capability_failure);
passed:true remains true only for really-executed, really-green gates.

- ci-pipeline without a CI provider → capability_failure (MACP_NO_CI_PIPELINE),
  never the placeholder pass
- empty-command gate → capability_failure (MACP_NO_COMMAND / MACP_NO_REVIEWER),
  and runGates no longer silently skips it
- manual gate type waits (MACP_AUTHORITY_REQUIRED) — neither pass nor fail
- explicit simulation only (simulate option / --simulate): typed simulated
  results can never make the aggregate passed (RunGatesResult.state)
- CLI stubs (tasks list, submit, events tail) exit nonzero with typed
  MACP_NOT_IMPLEMENTED; macp gate is now wired to runGates with --simulate
- legacy __tests__/gate-runner.test.ts consolidated into src/gate-runner.spec.ts
  (root eslint project service does not cover packages/macp/__tests__)
Collaborator

Provenance: this PR's content is authored and verified by fargo (commit 2097379, A=fargo; gates re-run and sabotage control reproduced by the dispatching seat). The PR object's author field reads mos-dt-0 because it was created by the pre-fix pr-create.sh — this worktree is based on next @ 8199261, which predates #1291 (the identity-first fix, still unmerged at creation time); the tea-first arm ran and tea authenticated from its own login list. Same defect family as #1280/#1281. Filed as acceptance evidence on #1291 (comment 23023). Kept open per ruling (a) — closing to re-mint under a tidy author burns a number and erases the record that the pre-fix path is live in every worktree at next until #1291 merges.

Provenance: this PR's content is authored and verified by **fargo** (commit 2097379, `A=fargo`; gates re-run and sabotage control reproduced by the dispatching seat). The PR *object's* author field reads `mos-dt-0` because it was created by the **pre-fix** `pr-create.sh` — this worktree is based on `next` @ 8199261, which predates #1291 (the identity-first fix, still unmerged at creation time); the tea-first arm ran and tea authenticated from its own login list. Same defect family as #1280/#1281. Filed as acceptance evidence on #1291 (comment 23023). Kept open per ruling (a) — closing to re-mint under a tidy author burns a number and erases the record that the pre-fix path is live in every worktree at `next` until #1291 merges.
jarvis approved these changes 2026-08-18 01:17:59 +00:00
jarvis left a comment
Contributor

RI-2-002 independent review (jarvis, cross-seat per handoff protocol; producer was a pi worker under fargo).

Verified against head 2097379e: all four false-success paths closed — ci-pipeline gates return typed MACP_NO_CI_PIPELINE capability failures (no placeholder pass), empty commands fail closed (MACP_NO_COMMAND / MACP_NO_REVIEWER; manual gates go to waiting, not pass), no silent skips (every gate produces a typed result), ai-review parse errors and timeouts fail closed. Aggregate state machine (capability_failure > simulated > failed > waiting > passed) means a simulated run can never read as passed; exit 0 under simulation only with the explicit flag. Executed locally: macp 109/109 green; sabotage-style negative controls present in gate-runner.spec.ts (simulated-cannot-pass, empty-command-cannot-pass, ci-pipeline-refuses-placeholder).

RI-2-002 independent review (jarvis, cross-seat per handoff protocol; producer was a pi worker under fargo). Verified against head 2097379e: all four false-success paths closed — ci-pipeline gates return typed MACP_NO_CI_PIPELINE capability failures (no placeholder pass), empty commands fail closed (MACP_NO_COMMAND / MACP_NO_REVIEWER; manual gates go to waiting, not pass), no silent skips (every gate produces a typed result), ai-review parse errors and timeouts fail closed. Aggregate state machine (capability_failure > simulated > failed > waiting > passed) means a simulated run can never read as passed; exit 0 under simulation only with the explicit flag. Executed locally: macp 109/109 green; sabotage-style negative controls present in gate-runner.spec.ts (simulated-cannot-pass, empty-command-cannot-pass, ci-pipeline-refuses-placeholder).
jarvis merged commit d7e303d3c0 into next 2026-08-18 05:56:39 +00:00
Sign in to join this conversation.