Skip to content

feat: implement project verification goals and proof runs (#187) - #331

Draft
Joncallim wants to merge 65 commits into
mainfrom
architecture/issue-187-goal-run-policy-final
Draft

feat: implement project verification goals and proof runs (#187)#331
Joncallim wants to merge 65 commits into
mainfrom
architecture/issue-187-goal-run-policy-final

Conversation

@Joncallim

@Joncallim Joncallim commented Aug 17, 2026

Copy link
Copy Markdown
Owner

Source Issue

Issue: #187

Agent Run

Runtime: manual
Run ID: n/a
Delivery mode: combined

Summary

Architecture evidence: docs/architecture/issue-187-verification-goal-run-policy.md plus the checked-in orthogonal review / normative amendment trail through Round 14.

Implementation evidence: this is a combined delivery PR. It now ships the complete manual-execution vertical slice for project verification goals, end to end:

  • Strict executable goal schema v2 with shared v1/v2 registry parsing; v1 manifests remain byte/meaning compatible.
  • Code-owned operation eligibility and execution-profile boundary: only exact Operation Catalog references on the reviewed allowlist are executable.
  • Manifest v2 with execution bindings; immutable v2 snapshot persistence; protected registry execution bindings (migration 0034).
  • First-class goal-run persistence: execution schema (migration 0035) and protected runtime routines (migration 0036) covering admission, lease claim/renewal, child operations, evidence snapshots, terminalization, and recovery fencing.
  • Project verification policy with a default-disabled head, seeded on first use and revalidated live at admission.
  • The safe manual runner itself: the route admits through forge_admit_verification_goal_run_v1 (the application login has no direct INSERT on run tables, so no bypassing write exists), then executes synchronously. The runner claims and renews the business lease, re-verifies the pinned executable identities, records the repository and environment evidence snapshots, runs the exact bound operations in canonical ordinal order through the root-anchored launcher, and terminalizes to a deterministic pass/fail/inconclusive outcome through forge_terminalize_verification_goal_run_v2, which requires the evidence set for a passed result.
  • Queue-delivery and scheduler database authority: schedule-binding reconcile and per-goal slot-claim routines, with the privilege reconcile script and installer migration proof updated to match.

Remaining delivery scope: the ticking scheduler worker that drives the schedule-slot routines; goal history (last known-good / first observed failing) and the #185/#186 outcome/reliability integration; and closure E2E evidence. The PR remains draft while these are incomplete.

Acceptance Criteria Validation

  • A project can define and validate at least one repository-backed verification goal. — existing v1 remains byte/meaning compatible; executable schema v2 validates, imports, snapshots, and activates through manifest-v2 protected registry bindings in migration 0034.
  • A goal can run on demand and persist structured pass/fail/inconclusive evidence. — the manual runner admits through the protected routine, executes the bound operations under the root-anchored launcher, records repository/environment evidence snapshots, and terminalizes atomically; a passed outcome is impossible without the required evidence set.
  • Unsafe or unsupported command definitions fail closed before execution. — v1 rejects commands/inputs; executable v2 accepts only reviewed allowlist references with matching capability. Admission throws before any DB write for non-allowlisted or capability-mismatched operations, the runner re-resolves the binding and refuses stored-policy digest drift, and the launcher re-verifies the pinned executable immediately before exec and never classifies a spawn-level failure as a successful exit.
  • Scheduled runs do not overlap or create duplicate evidence for the same goal/ref. — migration 0036 ships the protected slot-claim and binding-reconcile authority that enforces one active slot per goal/ref at the database level; the scheduler worker that invokes it remains in the follow-up scope above.
  • Forge records the last known-good state and the first observed failing state when determinable. — history implementation remains in the follow-up scope above.
  • Goal runs emit canonical outcomes compatible with [FEATURE] Normalize execution outcomes and stop reasons #185 and reliability evidence compatible with [FEATURE] Add capability reliability ledger #186. — the run contracts define the canonical outcome vocabulary; subject/reliability integration remains in the follow-up scope above.
  • Goal failure does not trigger an unreviewed code repair. — the architecture and executable contracts expose no repair/autonomy action and accept no arbitrary command/model field.
  • Disabled goals do not run. — the route rejects disabled and non-manual goals, and the admission routine revalidates the live policy head, registry head, and entry/snapshot identity under the canonical lock order before any run row exists.
  • Tests cover schema validation, command policy, deduplication, pass/fail history, and redaction. — schema, command policy, admission idempotent replay, launcher classification, runner lease/evidence handling, and route SQLSTATE mapping are covered by 49 new focused tests; history and redaction coverage lands with those remaining slices.

Tests / Verification

  • verification-goal-launcher.test.ts (14 tests): exit-code classification; spawn-level failures (ENOENT, STDIO_MAXBUFFER) surface as spawnError and never as exit 0; killed children classify as timeout; executable replacement before launch fails closed as executable_changed; root dev/ino mismatch fails as root_changed; abort classifies as cancelled; the safe launch environment strips NODE_OPTIONS/NODE_PATH and every GIT_* redirection even when passed through overrides. Writing these tests exposed a real defect: an abort landing in the handle-close window briefly left the launch rejection unobserved (a process-level unhandledRejection under strict policy). The launcher now pins a marker handler at promise creation so the rejection is always adopted while callers still receive it normally.
  • verification-goal-runner.test.ts (7 tests): happy path with evidence snapshots and lease-token-fenced child finalize; legacy operations policy key rejected as operation_contract_changed; spawn error → operation_infrastructure_failed; digest drift → operation_contract_changed; lease loss → lease_lost with no terminalize attempt; launcher error → root_changed; dirty repository → repository_dirty.
  • verification-goal-admission.test.ts (5 tests): the protected routine call shape with no direct INSERT; the live policy head id and sequence passed as real parameters (bigint never crosses the JSON boundary); canonicalOperationOrdinals in the resolved policy; idempotent replay; non-allowlisted or capability-mismatched operations throw before any DB call.
  • verification-goal-runtime-migration.test.ts (3 tests): migration 0036's hardened routine signatures (9-arg finalize with lease token, 6-arg slot claim), the queued-only claim guard, the lease-token fence, the passed-evidence guard, and the child_begun event, all mirrored in the privilege reconcile script.
  • verification-goal-runs-route.test.ts (20 tests): POST/GET happy paths; 401/503/400/404/409 guards; disabled and non-manual goal rejection; closed SQLSTATE mapping (P1871/P1872/P1873/P1876 → 409, P1874 → 429); generic 500 carries a correlation id and leaks no secret material.
  • Full suite: 2165 passed / 64 skipped across 162 files; tsc --noEmit clean; ESLint clean on all changed files.

Risks / Follow-up

This is intentionally a large combined delivery PR because #187's architecture and implementation share one security/evidence contract. Internal commits remain small and reviewable: architecture rounds first, then schema/registry slices, then the runtime slices, then the hardening and test slices. Architect first is an ordering/evidence rule, not a phase-specific PR rule.

The PR head branch was aligned to the delivery branch tip (codex/issue-187-goal-definition-v2). No content was lost: the three fix commits unique to the old head are contained in, or strictly superseded by, the delivery branch.

V1 proof execution remains read-only and does not enable arbitrary shell/test/build commands, host repository writes, auto-repair, autonomy changes, Sentinel actions, branch/commit/PR/merge automation, or live MCP grant issuance. Stronger same-UID/host confinement remains a separately stated residual boundary and is not silently claimed by this PR.

@Joncallim Joncallim changed the title docs: architect verification goal run policy (#187) feat: implement project verification goals and proof runs (#187) Aug 18, 2026
…(issue #187)

Implements the PR #331 architecture for issue #187 verification goals:

- Add migration 0035 with protected policy, run, event, evidence, and
  schedule tables plus shared-ledger dual execution subjects.
- Add TypeScript contracts for policy, run lifecycle, system limits,
  Git safety profile, and operation execution profiles.
- Add trusted executable registry, filesystem authority loader, repository
  profile computer, and root-anchored command launcher.
- Add manual run admission, synchronous runner, and POST/GET API route.
- Update privilege reconciliation, CI ACL inventory, and installer checks
  for the nine new protected tables and protected routines.
- Add schema-content tests and update existing schema/ingress tests.

This is a partial implementation. Remaining architecture slices:
- Redis-delivered worker queue claim/ACK instead of synchronous API run.
- TrustedProjectRootLease retention across the full runner lifecycle.
- History/aggregate evidence reader and reliability-v2 child ingest.
- Bounded scheduler with schedule bindings/heads/slots.
- Queue expiry/recovery, capacity enforcement, and policy settings API.
- Comprehensive adversarial/Git/root/lease tests and operator docs.

Validation: npm run test (2116 passed, 64 skipped), npm run lint,
npx tsc --noEmit, npm run build.
…eduler authority (issue #187 Slice C/E)

Migration 0036 adds the protected business routines the worker, dispatcher,
scheduler, and API depend on: capacity-checked idempotent admission, lease
claim/renew with a recovery fence, child-operation finalization with the
closed v2 outcome mapping, repository/environment snapshot recorders, queue
expiry, the Redis dispatch marker, the prefix-validating atomic terminalizer,
and schedule binding/slot authority keyed per (project, goal).

It also drops the unreleased 0035 terminalizer that accepted a
caller-supplied outcome without prefix validation, and the manual runner now
finalizes children and runs exclusively through the protected routines.

Fresh-install validation against PostgreSQL 16 (installer-equivalent chain
through 0036 plus the privilege reconciler) surfaced and fixed:

- 0035: missing temporary REFERENCES grants, a missing composite identity
  unique index, run events referencing later snapshot surfaces, and the
  shared-ledger expansion running under the wrong role;
- 0036: extract() call grammar, the operation_runs CHECK role boundary,
  reconcile FOUND capture, verifier-required mapping, admission/claim guards,
  and capacity-blocked schedule slot retention;
- the legacy normalizer/proof contamination counts for the 49-table
  latest position.

Privilege reconciliation now verifies the exact runtime-routine owner/ACL
boundary, and migrate-through-0036 joins the managed-migration proofs.
- claim routine accepts queued rows only; expired running rows belong to
  the recovery fence, never to a competing worker
- finalize child operation now carries the claim lease token so proof
  writes are fenced to the current lease holder
- idempotent manual replay resolves before capacity accounting so a
  retried identical request returns its run even at a capacity limit
- begin child appends the child_begun event
- terminalizer requires repository and environment evidence for passed
- reconcile script tracks the 9-arg finalize and 6-arg slot-claim
  signatures
- add the immutable launcher contract digest for run environment evidence
- re-verify the pinned executable identity (device, inode, content digest)
  immediately before launch; a replaced executable disables execution
  instead of launching the new file
- classify spawn-level failures (ENOENT, EACCES, STDIO_MAXBUFFER) as
  spawnError, never as exit 0; killed children classify as timedOut
- pin the launch promise as observed at creation so an abort during the
  handle-close window cannot trip process-level unhandled-rejection policy
- buildSafeLaunchEnvironment strips NODE_OPTIONS/NODE_PATH and all GIT_*
  path redirections even when passed through overrides
…sue #187)

- admission now calls forge_admit_verification_goal_run_v1, the single
  authoritative write path; the direct INSERT (for which the application
  login has no privilege) is removed
- the live policy head revision id and sequence travel as real routine
  parameters; the JSON resolved policy serializes the sequence as text so
  a bigint never crosses the JSON boundary
- code-owned eligibility is enforced at admission: an operation outside
  the reviewed allowlist/profile throws before any run row is created
- the route resolves the live policy head (or seeds the default) and
  maps the closed SQLSTATE set: P1871/P1872/P1873/P1876 to 409 and
  P1874 to 429
- trusted executable resolution anchors to the project root as well as
  the worker cwd
…h (issue #187)

- keep the business lease renewed on the reviewed 10-second cadence; any
  renewal failure or non-owner response disables further proof writes and
  a lost lease is never re-claimed by the runner
- execute the exact bound operations in canonical ordinal order via the
  reviewed eligibility binding; verifier/classifier/profile drift against
  the stored digests terminalizes as operation_contract_changed
- record the repository and environment evidence snapshots before a
  passed terminalization, including the launcher contract digest, trusted
  executable identities, and the git safety profile digest
- finalize child operations with the claim lease token
- compute the repository profile through the retained project-root
  descriptor (/dev/fd) with the safe launch environment and deterministic
  metadata fingerprint; expose headOid on the profile
- map launcher failures to specific terminal codes instead of a generic
  internal error
- launcher: exit classification, spawnError for ENOENT/STDIO_MAXBUFFER
  (never exit 0), killed-as-timeout, executable change, root dev/ino
  mismatch, abort, and safe-environment stripping under overrides
- runner: happy path with evidence snapshots and lease-fenced finalize,
  legacy policy key, spawnError, digest drift, lease loss, launcher
  error, and dirty repository
- admission: protected routine call shape, real bigint params, idempotent
  replay, and allowlist/capability enforcement before any DB call
- migration: text assertions on the hardened routine signatures, claim
  guard, lease-token fencing, passed-evidence guard, and child_begun
  event, mirrored in the reconcile script
- route: POST/GET happy paths, 401/503/400/404/409 guards, SQLSTATE
  mapping to 409/429, and generic 500 without secret leakage
@Joncallim
Joncallim force-pushed the architecture/issue-187-goal-run-policy-final branch from 388e9d7 to 5d1aa85 Compare August 19, 2026 16:56
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.

1 participant