Skip to content

Fence unsettled runs by recorded provider subject across auth locators (model + production replay) - #388

Open
0thernet wants to merge 13 commits into
mainfrom
claude/fv-subject-keyed-fence
Open

0thernet wants to merge 13 commits into
mainfrom
claude/fv-subject-keyed-fence

Conversation

@0thernet

Copy link
Copy Markdown
Member

The confirmed-write fence keyed unsettled runs by auth locator ID, so the same provider account reached through two auth records could run a duplicate write. New journals now record the auth record's provider subject, and the fence also refuses while an unsettled run of the same provider target, operation, canonical input, and duplicate-risk source recorded the same subject under a different locator — in the journal scan and again in a recheck after the run's own claim is on record.

Claims moved

Claim Layer Evidence
intent-fence-subject-across-locators (new) quint fence.qnt models two locators that may each record one subject or none, checks fenceSafety over 5,000 simulated samples of up to 12 steps and Apalache to length 8, adds the stepSubjectBlind mutant, and replays through the production fence cores and the file-backed state layer

Evidence and how the evidence fails when broken

  • Quint mutant. stepSubjectBlind drops the cross-locator subject check; both quint run and Apalache find the fenceSafety violation it allows.
  • Replay. scripts/verification-fence-replay.test.ts replays every seeded trace through the production fence cores, then replays a greedy seven-trace cover — one that reaches every action result, including the new claim -> withheld and scan -> withheld — through the real file-backed state layer. A hash-keyed fence and a reconciler that releases on a caller's claim each diverge from the model traces on named states.
  • Backwards compatibility. Journals written before authSubject existed parse unchanged and fence per locator only; parseAuthSubject normalizes and rejects malformed values. The fence can only refuse more.
  • Tests. src/confirmed-write-intent-fence.test.ts gained per-locator subject cases (one test each) and strict journal subject parsing pins; src/run-journal.test.ts and src/read-path-preparation.test.ts cover the new field and the identity-preflight scope note.

Defects found

  • The lane's own file-backed replay under-covered the new outcomes: withheld never appeared in a five-trace greedy cover. The cover is now sized at the measured minimum of seven traces (FILE_BACKED_TRACES = 7); eight fails because the greedy stops once coverage is complete.
  • No production defect: the model and replay found the missing fence itself, which is the change.

Package budget

src/run-journal.ts, src/runtime.ts, and src/confirmed-write-*.ts ship, so the packed and unpacked measurements were rerun and scripts/package-budget.ts updated on this branch's earlier commits; the merge keeps main's measurement where the shipped set is unchanged by the merge itself.

CI cost

The Required verification job runs the enlarged fence.qnt with Apalache at length 8 (unchanged) and 5,000 simulation samples of up to 12 steps (was 3,000/10); the replay file adds the per-locator subject cases and two more file-backed cover traces, each a full bound-state-helper run.

Not covered

  • Across locators the account realm is the recorded provider subject, which the operator may type: two locators recording one subject are fenced as one account even when they are not, so the cross-locator fence can only refuse more.
  • Runs with no recorded subject — including every journal written before this field existed — fence per locator only; a succeeded run under another locator neither fences nor replays.
  • Local validation on this host was partial under heavy load: typecheck, the claims render/check, the seeded-trace replay, and the cover test pass; the file-backed cover traces and the intent-fence suite's helper-heavy tests are delegated to the Required CI run on this head.

🤖 Generated with Claude Code

0thernet and others added 7 commits September 24, 2026 13:37
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… identity preflight

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ournal subject parsing

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The subject-fence outcomes claim -> withheld and scan -> withheld need a
seven-trace greedy cover; five traces left them unseen.
@vercel

vercel Bot commented Sep 25, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
ghostget Building Building Preview Sep 25, 2026 4:54am UTC

Request Review

… bound Apalache for shared CPUs

- A locator whose auth record names no provider subject cannot preview an
  R2/R3 action at all, so a no-subject run journal exists only in records
  written before the field did; the legacy-journal case already covers
  the claim's no-subject semantics.
- Remeasure the package: 23,475,086 unpacked bytes on this branch across
  the unchanged 596-file inventory; the Linux package job measured the
  same. Restores the reviewed allowance.
- With QUINT_CONCURRENCY two checker jobs share the runner's four vCPUs,
  so a fence.qnt Apalache check that finished in about ten minutes alone
  can need roughly twice that; raise the per-check bound to 20 minutes.
  The bound still fails a genuinely stuck check.
The hash-keyed mutant world names its auth locators fence-second-0 and
fence-main-1, with a generation suffix. The refusal classifier matched
[a-z-]+, so the mutant world's subject-fence refusal read as an
unexpected reason instead of a refusal, and the divergence check failed
before reaching the trace assertions.
…ed-fence

# Conflicts:
#	docs/assurance.md
#	kb/plans/formal-verification-assurance.md
#	scripts/npm-release-workflow.test.ts
#	scripts/package-budget.ts
…dget

The file-backed replay diverged: after finish(r3, succeeded) production's
repair sweep marks the elected source superseded — its recovery releases
and the run is durably settled — while the model kept reconciled empty.
Settling a successor through reconcile does the same. Both steps now
reconcile the elected source, matching supersedeSettledDuplicateSources:
dispatched, settled, never failed.

The enlarged model also outgrew its budgets: six Apalache checks plus the
replay approached the step's 20 minutes on the shared 4-vCPU runner, and
the weight from run 36028981199 predates the subject dimension. The step
gets 30 minutes inside a 35-minute job, the fence weight rises to 1150 s
so the packer isolates it, and the file-backed cover cap becomes a
ceiling — it bounds replay cost rather than pinning the greedy
minimizer's exact count.

This branch was successfully deployed

1 active (outdated) deployment
Preview — d29fc37f Deployed Sep 25, 2026 by vercel[bot]
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