Skip to content

feat: add verification goal registry foundation (#187) - #328

Merged
Joncallim merged 4 commits into
mainfrom
codex/issue-187-verification-goal-registry
Aug 14, 2026
Merged

feat: add verification goal registry foundation (#187)#328
Joncallim merged 4 commits into
mainfrom
codex/issue-187-verification-goal-registry

Conversation

@Joncallim

@Joncallim Joncallim commented Aug 14, 2026

Copy link
Copy Markdown
Owner

Source issue

Part of #187
Parent Epic: #184

Why this is next

The deterministic operation catalog (#201), canonical execution outcomes (#185 via PR #324), and capability reliability ledger (#186) are already merged. This starts #187 with its smallest safe implementation foundation.

What changed

  • add a strict repository registry at .forge/verification-goals/*.json
  • accept only closed, versioned definitions referencing existing enabled deterministic operations
  • reject commands, arguments, paths, custom adapters, policy overrides, unsafe files, symlinks, path escape, malformed definitions, duplicates, and bounded-size violations
  • resolve the authoritative project root from PostgreSQL and Forge's workspace guard
  • carry the validated project's directory identity into the importer, rather than trusting the pathname again
  • anchor registry enumeration and direct-child reads to no-follow directory and file handles
  • retain file handles, re-enumerate the registry, and recheck directory/file membership and metadata before returning a snapshot
  • fail before storage for symlink or ordinary-directory project-root rebinding, directory move-plus-symlink replacement, concurrent additions/renames, and already-read file replacement
  • canonicalize definitions and create domain-separated SHA-256 digests
  • persist immutable, project-scoped snapshots with transactional all-or-nothing import
  • make same-version/same-digest imports idempotent and reject divergent content
  • derive every current-tip migration proof from one validated Drizzle-journal helper while retaining fixed historical checkpoints
  • document the security, migration-proof, and deferred-work contracts in ADR 0013

A stored snapshot is definition evidence only. It never means a goal ran or passed.

Architecture and security remediation

The review loop found and fixed these implementation blockers:

  1. stale hard-coded current-tip migration expectations;
  2. registry files reopened through unanchored pathnames;
  3. symlink project-root rebinding after workspace validation;
  4. registry membership mutation after its first enumeration;
  5. remaining current-tip pins in legacy and upgrade proofs;
  6. ordinary-directory replacement at the same project pathname.

Commit 652eb14 fixed the initial migration and registry-path findings. Commit fa43af2 added pinned file/directory reads, membership re-attestation, symlink-rebind protection, and the shared migration-ledger helper. Commit fdc8701 carries a validated path + device + inode project-root binding into the importer and adds deterministic no-storage regressions for both symlink and ordinary-directory replacement.

Final architecture verdict

No blockers found in the inspected scope. High confidence in the reviewed architecture at exact head fdc8701. In plain language: this is suitable for the bounded first slice—declarative goal definitions and immutable snapshots—and does not claim or introduce goal execution, pass/fail, scheduling, or reliability behavior. Review increases confidence; it is not proof that defects are impossible.

Final Security/Adversarial verdict

No blockers found in the inspected scope. High confidence. All six deterministic root, registry-membership, symlink, and file-replacement attacks fail before storage.

One low-severity future boundary is recorded: the project-root binding is an identity token, not a directory handle held continuously across the importer handoff. That is acceptable here because repository definitions are strictly non-executable and the practical inode-reuse attack was not reproduced. A later execution slice must use an opaque live directory lease rather than reuse this snapshot-import primitive as an execution trust boundary.

Deliberately deferred

This PR does not add goal runs, execution or dispatch, manual-run API or UI, schedules or Redis jobs, canonical outcome writes, reliability ingestion, last-green or first-failure state, retries, notifications, autonomous repair, or issue #188 behavior.

Validation

Final-head evidence:

  • independent QA over the root-binding patch: 32 passed; 4 PostgreSQL-gated tests skipped locally
  • architecture-focused registry/path/migration run: 47 passed; 4 PostgreSQL-gated tests skipped locally
  • operation/context compatibility: 13 passed
  • targeted ESLint, TypeScript, shell syntax, migration-tip checks, and diff checks: passed
  • PR Contract Check: passed
  • GitGuardian: passed
  • Web CI run 31823212940: passed at exact head fdc8701
    • complete zero-skip unit suite
    • real installer-managed, legacy-repair, and populated-upgrade migration proofs
    • mandatory PostgreSQL concurrency and access-control proofs
    • Redis and cross-sink proofs
    • production build
    • browser/end-to-end recovery and fail-closed bridge checks

Remaining scope limits

Linux, NFS, and other filesystem-specific inode/timeout behavior was not separately exercised. Future API authorization, queueing, execution, and UI integration remain outside this PR and require their own architecture and security passes.

@Joncallim
Joncallim marked this pull request as ready for review August 14, 2026 17:38
@Joncallim
Joncallim merged commit e282201 into main Aug 14, 2026
6 checks passed

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: fdc87012b7

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

}
const projectRoot = await dependencies.bindProjectRoot(project)
const goals = await (dependencies.loadRegistry ?? loadVerificationGoalRegistry)(projectRoot)
return dependencies.store.importSnapshots(project.id, goals)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Lock the project binding through snapshot persistence

When projects.localPath changes concurrently after loadProject returns, this call persists definitions read from the old directory under a project that now points to the new directory; PUT /api/projects/:id can perform that update independently, and the snapshot records neither the bound root nor its revision, so its provenance cannot be recovered. Pass the loaded binding into the transaction, lock and re-read the project row before insertion, and abort if the authoritative binding changed.

Useful? React with 👍 / 👎.

return typeof value === 'string'
&& value.trim().length > 0
&& value.length <= maximumLength
&& !SAFE_TEXT_CONTROL_CHARACTERS.test(value)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reject lone UTF-16 surrogates during contract validation

When a registry string contains an escaped unpaired surrogate such as "description":"\ud800", JSON.parse accepts it and this validation treats it as safe text, but PostgreSQL jsonb rejects the resulting invalid Unicode escape when the canonical definition is inserted. The registry therefore passes filesystem and contract validation only to fail later with a database error; reject unpaired surrogates here so the file receives the intended validation error before persistence.

Useful? React with 👍 / 👎.

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