Skip to content

feat: add canonical execution outcomes - #324

Merged
Joncallim merged 6 commits into
mainfrom
agent/normalize-execution-outcomes
Aug 7, 2026
Merged

feat: add canonical execution outcomes#324
Joncallim merged 6 commits into
mainfrom
agent/normalize-execution-outcomes

Conversation

@Joncallim

Copy link
Copy Markdown
Owner

Summary

  • add a versioned, idempotent execution_outcomes ledger and migration
  • normalize admission blocks and terminal work-package results into bounded, redacted outcomes
  • document the contract in ADR 0010 and add focused regression coverage

Why

Forge previously mixed transport state, semantic results, and free-text errors, making execution evidence unsafe to compare. This introduces the shared outcome boundary required by #185.

Validation

  • cd web && npm test -- --run __tests__/execution-outcomes.test.ts __tests__/work-package-handoff-db.test.ts
  • git diff --check

Known baseline limits

  • npx tsc --noEmit remains blocked by pre-existing malformed or missing implicit type packages.
  • Drizzle snapshots stop at migration 0026 while the journal already reaches 0028, so generating a new snapshot triggers unrelated interactive prompts. Migration 0029 and the journal entry follow the repositorys existing migration convention.

claude added 5 commits August 7, 2026 02:18
…tions

The installer-managed migration CI gates hardcoded the migration ledger's
"latest" state as exactly 29 rows ending at migration 0028's timestamp. That
coincidentally matched main, but this PR adds migration 0029, so the ledger
is now 30 rows and those hardcoded assertions reject a fully-successful
migration.

Also update repair-epic-172-legacy-release.ts: its checkpoint classifier
only recognized ledgers of exactly 27/28/29 rows (positions 0026/0027/0028).
Once later migrations exist, a fully-migrated database no longer matches any
of those, so the idempotent installer re-run wrongly refuses the epic-172
legacy-release repair. The 0028 checkpoint now accepts ledgers of 29 rows or
more, validating only that the first 29 entries are the known-good
0000-0028 ledger; later migrations are outside this script's scope and are
governed by their own migration files and the CI journal-parity gate.
Two more hardcoded main-era assumptions break once migration 0029 exists,
both downstream of the gate fixed in the previous commit so CI never
reached them yet:

- migration-0027-expansion-assertions.sql pinned the post-0028 ledger to
  count 29 / max timestamp 1784274000000. apply-epic-172-s5-recovery-
  migration.sh actually migrates to the true latest (db:migrate, unpinned),
  so with 0029 present this needs to become 30 / 1785820800000.
- The closed application-ACL inventory check in web-ci.yml enumerates every
  public table by name; execution_outcomes wasn't in any of its three
  arrays, so the ordinary-app boundary proof would reject it as an
  unaccounted-for table. Add it as its own operation_ledger_tables entry
  with the SELECT/INSERT/UPDATE privileges the upsert-only worker path
  actually uses (db/worker/execution-outcomes.ts has no DELETE).
This test reads scripts/ci/sql/migration-0027-expansion-assertions.sql as
text and asserts it still contains specific hardcoded evidence strings,
including the pre-0029 migration count. It wasn't updated when that SQL
file's asserted count changed from 29 to 30 in the previous commit, so the
zero-skip unit suite failed even though the SQL file itself was correct.
…path

Orthogonal review finding: the three upsertExecutionOutcome call sites
(admission block, run completion, run failure) were unguarded, unlike
every other non-critical write in this file (publishTaskEventBestEffort,
recordTaskLogBestEffort). A ledger write failure would abort completion
before the work package is marked complete/awaiting-review, or abort
failure recording before run:failed/artifact:created are published and
before the failure is logged -- even though the underlying artifact/status
writes had already durably succeeded. The ledger is idempotent on
(taskId, attemptKey), so a missed write is safe to retry or backfill
later; it should never block state that's already been persisted.

Also fixes a stale CI-script echo string ("...at the 29-row ledger") left
over from an earlier fix in this PR; the proof it labels runs at the fixed
0028 checkpoint regardless of how many later migrations exist.
…block kinds

Orthogonal review finding: the outcome-ledger writes in
persistWorkPackageHandoffBlock were only mocked, never asserted, so the new
kind -> stopReasonCode mapping (broker -> admission_denied, reserved_role
-> policy_blocked, filesystem_grant -> missing_capability) and the
terminalBlock -> retryable inversion were unverified by CI. Add one
targeted assertion per existing test that already exercises each kind.

The other two upsertExecutionOutcome call sites, in executeReadyWorkPackage's
completion and failure branches, are not covered here: that function is
currently unreachable in both production and this test file because
isWorkPackageExecutionEnabled() is hardcoded to always return false, so no
test can exercise it without restructuring the module's mocking boundary.
@Joncallim
Joncallim marked this pull request as ready for review August 7, 2026 07:14
@Joncallim
Joncallim merged commit af56911 into main Aug 7, 2026
3 checks passed
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants