diff --git a/docs/09-reviews/2026-09-02-m5-2-real-effects-live-attempt.md b/docs/09-reviews/2026-09-02-m5-2-real-effects-live-attempt.md index e1eb946..6a7c50a 100644 --- a/docs/09-reviews/2026-09-02-m5-2-real-effects-live-attempt.md +++ b/docs/09-reviews/2026-09-02-m5-2-real-effects-live-attempt.md @@ -77,10 +77,31 @@ session identifiers. Then correct the observed boundary and run once. The requested failure manifest is now implemented for the next run. It is captured from the live coordinator database before cleanup and then passed through an exact public projector. The output is restricted to a schema -version, source marker, derived stage, nine bounded counts, and—only for an +version, source marker, derived stage, ten bounded counts, and—only for an ambiguous recovery—one fixed-enum reason code. Any extra key, unknown reason, string, impossible count, or stage/count mismatch suppresses the projection. The historical aggregates above remain operator observations from attempt 11; they are not retroactively upgraded into machine evidence. The next live run is the first one that can produce the new machine-verifiable partial projection. + +## Attempt 12 result + +The first run with the merged projection used commit `688c226`, the same +process-scoped proxy, Codex CLI 0.145.0, certificate verification, real Git +worktrees, and child verifier. It again reached the reviewer admitted turn and +failed closed, but now emitted machine-derived evidence: + +- stage: `reviewer-admitted-turn-partial`; +- tasks 5, dispatches 1, turn intents 3, tool actions 5; +- lifecycle publications 1 and Git evidence records 1; +- zero finalizations, satisfactions, and cursor commits; +- reason: `codex-native-turn-completed-observation-only`; +- cleanup: 5/5 roles deleted and absence-confirmed, coordinator removed, zero + journals, and the empty caller artifacts directory removed. + +The fifth action proves that the reviewer selected both admitted tools, while +the completed-only persisted turn cannot by itself prove whether the second +callback result was durably completed. The next diagnostic adds only one +aggregate—completed tool actions—to distinguish callback rejection from an +adapter-finalization failure. It does not expose tool arguments or outputs. diff --git a/docs/09-reviews/2026-09-02-mainline-checkpoint.md b/docs/09-reviews/2026-09-02-mainline-checkpoint.md index d9fb6ba..67883a8 100644 --- a/docs/09-reviews/2026-09-02-mainline-checkpoint.md +++ b/docs/09-reviews/2026-09-02-mainline-checkpoint.md @@ -59,13 +59,22 @@ was then removed. This is a failed partial attempt, not a product pass. See the [attempt record](2026-09-02-m5-2-real-effects-live-attempt.md). The follow-up diagnostic slice now captures a strict pre-cleanup SQLite -projection on scenario failure. It exposes only a fixed stage enum, nine +projection on scenario failure. It exposes only a fixed stage enum, ten bounded aggregate counts, and an allowlisted ambiguous-reconciliation reason; raw prompts, receipts, paths, thread IDs, and journal contents are not included. Schema drift, excess counts, inconsistent stages, and unsafe reason strings are rejected by the CLI projector. Failure-stage, tamper, privacy, and exact cleanup tests pass. +Attempt 12 on merged commit `688c226` exercised that projection. It reported +`reviewer-admitted-turn-partial` and +`codex-native-turn-completed-observation-only`, with 5 tasks, 1 dispatch, 3 +turn intents, 5 selected tool actions, 1 lifecycle publication, and 1 Git +evidence record. Exact cleanup passed. Because selected-action count alone does +not distinguish a rejected second callback from post-callback adapter failure, +the projection now also includes the bounded completed-action aggregate before +the next run. + ## Ordered next gates 1. Run the merged real-effects Codex path once with the new partial-stage diff --git a/src/validation/coordinator-driven-no-plan-scenario.mjs b/src/validation/coordinator-driven-no-plan-scenario.mjs index 40f7a0b..d0b0d4b 100644 --- a/src/validation/coordinator-driven-no-plan-scenario.mjs +++ b/src/validation/coordinator-driven-no-plan-scenario.mjs @@ -54,6 +54,8 @@ const FAILURE_PROGRESS_COUNT_QUERIES = Object.freeze({ dispatches: "SELECT COUNT(*) AS count FROM event_pump_dispatches", turnIntents: "SELECT COUNT(*) AS count FROM turn_execution_intents", toolActions: "SELECT COUNT(*) AS count FROM turn_tool_actions", + completedToolActions: + "SELECT COUNT(*) AS count FROM turn_tool_actions WHERE result_status = 'completed'", lifecyclePublications: "SELECT COUNT(*) AS count FROM lifecycle_action_publications", gitEvidenceRecords: "SELECT COUNT(*) AS count FROM git_evidence_records", dependencyFinalizations: diff --git a/src/validation/m5-2-event-pump-codex-gate.mjs b/src/validation/m5-2-event-pump-codex-gate.mjs index e5a176a..cf6fdb0 100644 --- a/src/validation/m5-2-event-pump-codex-gate.mjs +++ b/src/validation/m5-2-event-pump-codex-gate.mjs @@ -169,6 +169,7 @@ const FAILURE_PROGRESS_COUNT_LIMITS = Object.freeze({ dispatches: 5, turnIntents: 16, toolActions: 32, + completedToolActions: 32, lifecyclePublications: 4, gitEvidenceRecords: 4, dependencyFinalizations: 1, @@ -197,6 +198,7 @@ export function projectM52EventPumpFailureProgress(value) { if (!Number.isSafeInteger(count) || count < 0 || count > limit) return null; counts[key] = count; } + if (counts.completedToolActions > counts.toolActions) return null; if (value.stage !== deriveCoordinatorDrivenFailureStage(counts)) return null; let reconciliation = null; if (value.reconciliation !== null) { diff --git a/test/coordinator-driven-no-plan-scenario.test.mjs b/test/coordinator-driven-no-plan-scenario.test.mjs index d5bdef5..90b07b9 100644 --- a/test/coordinator-driven-no-plan-scenario.test.mjs +++ b/test/coordinator-driven-no-plan-scenario.test.mjs @@ -362,6 +362,7 @@ test("failed trusted finalization starts no dependent business turn", async (t) dispatches: 5, turnIntents: 8, toolActions: 11, + completedToolActions: 11, lifecyclePublications: 4, gitEvidenceRecords: 3, dependencyFinalizations: 0, @@ -554,6 +555,7 @@ test("a bounded shutdown after role bootstrap cleans every created role and run dispatches: 0, turnIntents: 0, toolActions: 0, + completedToolActions: 0, lifecyclePublications: 0, gitEvidenceRecords: 0, dependencyFinalizations: 0, diff --git a/test/m5-2-event-pump-codex-gate.test.mjs b/test/m5-2-event-pump-codex-gate.test.mjs index 4af3f39..67f67c4 100644 --- a/test/m5-2-event-pump-codex-gate.test.mjs +++ b/test/m5-2-event-pump-codex-gate.test.mjs @@ -425,6 +425,7 @@ test("failure progress projection exposes only bounded SQLite-derived stage data dispatches: 1, turnIntents: 3, toolActions: 4, + completedToolActions: 3, lifecyclePublications: 1, gitEvidenceRecords: 1, dependencyFinalizations: 0, @@ -442,6 +443,7 @@ test("failure progress projection exposes only bounded SQLite-derived stage data (value) => { value.rawThreadId = "raw-thread-id"; }, (value) => { value.counts.prompt = 1; }, (value) => { value.counts.turnIntents = 17; }, + (value) => { value.counts.completedToolActions = value.counts.toolActions + 1; }, (value) => { value.stage = "review-published"; }, (value) => { value.reconciliation.reasonCode = "/private/raw/path"; }, (value) => { value.reconciliation.reasonCode = "codex-native-turn-secret-id"; },