Skip to content

Run leases: bind effect-host certification to the admitted instance; test expiry on the fenced commit paths (#253) - #257

Merged
kikashy merged 3 commits into
mainfrom
run-leases-batch-253
Oct 29, 2026
Merged

kikashy merged 3 commits into
mainfrom
run-leases-batch-253

Conversation

@kikashy

@kikashy kikashy commented Oct 29, 2026

Copy link
Copy Markdown
Contributor

Closes #253. Milestone v0.12.0. This is the pre-release batch from the security re-read of #251; it covers R1, R2, N1 and N2 from that review record.

What changed

R1 (MEDIUM): expiry on the fenced commit paths. Both lease kits (runStoreLeaseConformance, Go CheckRunStoreLeaseConformance) now require commitHistoryFenced and commitContextSnapshotFenced to refuse an expired fence, both at equal time and 1 ms after. Tests: a store that skips the expiry check on either path now fails the kit at lease.history.expired.equal-time / lease.snapshot.expired.equal-time (TS "the kits test expiry on every fenced commit path"; Go TestLeaseKitsTestExpiryOnEveryFencedCommitPath).

R2 (MEDIUM): certification is bound to the admitted instance.

  • The effect-host kit makes its authority fail. A host that performs while the authority errors fails with host.authority-unavailable, because unavailable ownership evidence closes dispatch.
  • certifyEffectHost / CertifyEffectHost call the factory once. The host is built over an authority that answers from the kit until the checks pass and from the real authority after that. The exact instance the kit exercised is the one admitted:
    • TypeScript returns a frozen wrapper that refuses, performing nothing, once the instance's dispatch is not the one the kit checked.
    • Go returns a *CertifiedEffectHost whose identity is the evidence; a copy of the value is refused.
  • ReferenceEffectHost's dispatch and effectFencing are sealed on the instance. A subclass, or a Go type embedding it, carries no evidence.
  • admitUnattended / AdmitUnattended accept only the package's reference host or a certified host, and only when that host enforces against the run's own store. A no-op authority, another store, or a certification over another store is refused.
  • Tests: TS "certification is bound to the instance it checked"; Go TestLeaseCertificationIsBoundToTheInstanceItChecked (authority-unavailable, factory-called-once, identity-not-equality, authority-is-the-store). Each covers a bypass from the record: a different factory product, a swapped dispatch (TS), an equal-but-different value (Go), a no-op or foreign authority, and evidence on another instance or a subclass.

N1 (LOW). Go keeps no registry of hosts, so admitted reference and certified hosts are collected. Test: TestLeaseAdmittedHostsAreCollectable (100 of 100 collected after GC).

N2 (LOW). The Go agent adapter's pre-effect checkpoint save now follows the new exported DurableReadingTool, so a source act is saved before it runs. Test: TestASourceActIsSavedBeforeItRuns (go/adapter/agent; light).

Docs: docs/design/storage.md (certifying an effect host, the kit's expiry cases). No schema, record or moved Desk file changed, so the parity table is unchanged.

Mutation rows (origin: agent#253)

B1–B9, all caught by storage/lease.test.ts itself (caught: selected, not the whole-suite fallback). Twins applied by hand in a scratch copy, all caught:

  • Go twins of B1–B4, B8 and B9.
  • Go-only safeguards: identity (self), "made by NewReferenceEffectHost", the N1 registry and N2.
  • B5–B7 are TypeScript-only, because a Go method cannot be swapped.

L17 from #223 is re-anchored to the new admission check. All 18 L rows are still caught.

Checks

Candidate 75f884c67905296dfb350ca36ef1864ce34186e3 (branch run-leases-batch-253, clean tree). Command: jpdev check --changed origin/main, run with Go 1.26.10 (the manifest's version) and Node 22. Parity was run against a shallow Desk checkout at desk_pin.

== 23 passed, 0 failed, 0 skipped, 0 not run ==
python-tests OK; mutation-rows 546 rows, 0 problems; build; packed-smoke; reference 13 pages
unit-tests 3887 passed | 32 skipped; certify vercel / codex / deepagents passed; differential
go-vet, gofmt, go-generate, go-test, go-race (832 s), harness-check (202 legs, 94 agent checks)
parity: every difference is named

The first full run failed only reference (event-reference line numbers moved); the second commit regenerates that page. The exact-head gates and the push-to-main mutation table belong to CI.

Left as is

LOW 6 (Go lone-surrogate identifiers, pre-existing), LOW 7 (already documented) and LOW 8 (mutation-check tooling) are unchanged, as the issue scopes.

🤖 Generated with Claude Code

kikashy and others added 2 commits October 29, 2026 11:23
…expiry on the fenced commit paths (#253)

The v0.12.0 pre-release batch from the security re-read of #251.

- R1: both lease kits now refuse a store whose commitHistoryFenced or
  commitContextSnapshotFenced accepts an expired fence, at equal time
  and after.
- R2: the effect-host kit makes its authority fail, and a host that
  performs then fails it. certifyEffectHost / CertifyEffectHost call
  the factory once, over an authority that answers from the kit until
  the checks pass and from the real authority after, and admit that
  exact instance: a frozen TypeScript wrapper that refuses once the
  instance's dispatch is not the one checked, a Go *CertifiedEffectHost
  whose identity is the evidence. The reference host's dispatch is
  sealed on the instance. Unattended admission accepts only these, and
  only when their authority is the run's own store.
- N1: Go keeps no registry of hosts; admitted hosts are collectable.
- N2: the Go agent adapter's pre-effect save follows DurableReadingTool.

Nine mutation rows (agent#253) and ten Go twins, all caught; L17
re-anchored. Storage docs updated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: kikashy <35789537+kikashy@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: kikashy <35789537+kikashy@users.noreply.github.com>
…pied reference host (#253)

From the narrow re-check of #257. TypeScript certification reads the host's dispatch once, runs the kit through that function and admits only it, so a getter or a Proxy trap cannot show the kit one function and admission another; the reference host's authority, admitted set and queue are #private fields. In Go only the pointer NewReferenceEffectHost made is evidence (a self pointer), not a shallow copy that would share its key map without the lock. Rows C1-C3 added; L4, L9 and B4 re-anchored.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: kikashy <35789537+kikashy@users.noreply.github.com>
@kikashy

kikashy commented Oct 29, 2026

Copy link
Copy Markdown
Contributor Author

Verdict: merge once the pending check job passes. R1, R2, N1 and N2 are fixed as claimed, and #251's guarantees hold. 0 HIGH, 1 MEDIUM, 3 LOW, all for the pre-release batch.

Review record: agent PR #257 (issue #253), the lease batch after the #251 re-read

Candidate and toolchains

A Claude Opus review over a Claude Opus build; not a cross-vendor claim. This is a narrow re-check of the batch that answers my #251 re-read. Nothing was posted, pushed or edited outside the room.

Method

  • Re-ran my existing probes, which are not committed: <scratch>/work/review251.probe.test.ts and <scratch>/work/review251_probe_test.go.
  • Added new probes for this check: P22–P23 in TypeScript and G12–G14 in Go. They go through the real certifyEffectHost / CertifyEffectHost, admitUnattended / AdmitUnattended, both kits, and the session and writer paths.
  • Commands:
    • TypeScript: npx vitest run storage/review251.probe.test.ts from packages/harness.
    • Go: go test -count=1 -run TestReview -v ./context/, plus -race on the TestReview257 tests and G1–G3.
    • Mutation rows: mutation-check.mjs --row "lease batch B" --row "lease fence L17 ".

Probes

# Scope Probe Result
P1–P3 / G1–G3 4 Source act and plain tool under a fenced view: admission, expiry after the claim, takeover with an effect in flight Hold in both languages: 1 admission; after expiry the host refuses expired and nothing runs; the takeover gets HostOutcomeUnknown with exactly 1 physical dispatch
P8 / G7 1 (R1) The non-enforcing store kinds against the new kits, including history and snapshot ports that skip expiry All refused. The R1 kinds fail lease.history.expired.equal-time and lease.snapshot.expired.equal-time in both kits. The earlier kinds are caught as before. The only pass is a TS durable declaration with per-instance epochs, which is declared out of scope
P22 / G12 1 (R2) Every R2 bypass from the re-read All refused. A host that performs when the authority errors fails host.authority-unavailable (both). A dispatch replaced after certification is refused at admission and at the wrapper, and nothing runs (TS). A reference host over a no-op authority or another store is refused; over the run's store it is admitted (both). Certified over one store, it is refused with another (both)
P22 / G12 2 Wrappers and copies: a delegating wrapper, a Proxy and Object.create over a certified or reference host (TS); a copy of a *CertifiedEffectHost and a type that embeds it with its own Dispatch, by value and by pointer (Go) All refused
P22 / G12 2 Re-certify the same instance after mutating it Refused in both languages (host.authority-unavailable). In TS the first certificate is refused once the dispatch changes
P22 / G13 2 Concurrent certification and admission. TS: an inner host leaked during certification, and two certifications in parallel. Go: 8 goroutines × 50 certify-and-admit, plus dispatch through one shared certified host, under -race The leaked inner host is refused both during and after certification. Every proper certification is admitted. Go: 400 of 400 admitted, 400 dispatches performed, no data race
P22 2 A getter, or a Proxy get trap, that hands the kit an enforcing dispatch and hands capture and identity checks a non-enforcing one Certified, admitted, and it performs a stale fence (TS). Finding 1
P22 2 The reference host's authority or admitted set replaced after admission (TS-private only) Still admitted. With a no-op authority it performs a stale fence; with an empty set it performs an admitted identity again. Finding 1
P22 / G12 2 A host that refuses on authority errors but performs after three in a row (a circuit breaker) Certified and admitted in both languages; the 4th dispatch performs while the store is unreachable. Finding 3
G12 2 A host whose own state switches it to performing after certification Admitted, and performs a stale fence. Certification tests behavior at one time; a time bomb or kit-detecting host is beyond any behavioral kit (noted, not a finding)
G12 / G14 3 (N1) Is anything kept alive, and did admission change? Weak pointers: 1000 of 1000 admitted reference hosts and 200 of 200 certified hosts were collected. A shallow struct copy of a reference host is now admitted. It shares the original's admitted-key map but not its lock, and -race reports a data race in admit when the two dispatch concurrently. No double perform was seen in 200 trials. Finding 2
P23 5 Subclasses of the sealed reference host A subclass's dispatch override never runs (the sealed own property shadows it), and a subclass that declares an effectFencing field throws TypeError at construction. Subclasses are not evidence, so this is not a safety issue; an API note
P10 5 The 113 boundary inputs 111 agree, with no untyped throw; unchanged from #251
N2 1 The Go adapter's pre-effect save Read: go/adapter/agent/invoke.go uses runcontext.DurableReadingTool; the batch adds a test. Not probed

Mutants

  • TypeScript rows: all 10 chosen were caught by their selected tests in lease.test.ts, never by the whole-suite fallback. They are B1–B9 and the re-anchored L17.
  • Go twins: not re-run.
  • My store kinds (P8 / G7): all refused by both kits, except the declared per-instance-epoch case.

Findings

# Finding Sev Evidence What to do
1 The TypeScript binding of certification to the admitted host is incomplete. (a) certifyEffectHost captures host.dispatch once, but the kit then calls host.dispatch(...) through fresh property reads. A getter or Proxy trap can show the kit an enforcing function and show the capture and every later identity check a non-enforcing one. That host is certified, admitted, and performs on a stale fence. The docs' "refuses once the instance's dispatch is not the one the kit checked" is therefore false for accessors. (b) The reference host seals dispatch, but authority and admitted are writable at runtime (TS private only). Admission reads the authority recorded at construction, while dispatch uses the property, so swapping either after admission keeps the evidence and stops enforcement or replays an admitted identity. Both need deliberate code; both fixes are cheap MEDIUM P22 (a) Run the kit through the captured function (dispatch.call(host, …)) and never read host.dispatch again. (b) Use ES #private fields, or seal authority and admitted as dispatch is, or have dispatch use the authority recorded at construction. Add the getter, the Proxy and the field swaps as tests
2 Go: removing the registry (N1) changed admission. A *ReferenceEffectHost is now evidence by type, so a shallow copy of a package-made host is admitted, where before only the constructor's pointer was. The copy shares the original's admitted-key map but not its mutex, so concurrent dispatch through both races in admit, and the same identity could be admitted twice. go vet's copylocks would flag such a copy LOW G12, G14 (-race) Give the reference host the same identity check as CertifiedEffectHost (a self pointer)
3 The host kit fails the authority once. A host that degrades to performing after a few consecutive authority errors passes, is certified and admitted, and performs while the store is unreachable (both languages). No finite test is complete, but this circuit-breaker shape is a plausible design LOW P22, G12 Fail the authority several times in a row, including after successes, and state in the docs that a host must never degrade to performing
4 Sealing dispatch on the instance silently ignores a subclass's dispatch override, and a subclass with an effectFencing field throws at construction LOW P23 One sentence in the docs, or make the class final by convention

From #251, finding 6 (Go accepts a lone-surrogate identifier) and finding 8 (mutation-check tooling) are unchanged and outside this PR.

What was not established

Maintainer disposition (2026-10-30)

  • TypeScript MEDIUM (the kit read dispatch fresh; authority and admitted set were replaceable after admission): fixed in this PR at f9bd581. Certification captures dispatch once, runs the kit through it and admits only it. The reference host's fields are #private. Tests cover a changing getter, a Proxy, and a replaced authority or admitted set. Rows C1–C3 are caught.
  • Go LOW regression (a shallow copy of a reference host was admitted and raced): fixed in this PR. Only the constructed pointer counts as evidence. TestLeaseACopiedReferenceHostIsNotEvidence passes under -race, and fails with the guard removed.
  • Remaining LOWs (the kit fails the authority only once; subclass dispatch override and the effectFencing field): filed as Run leases: fail the authority repeatedly in the host kit, and define subclassing of the reference host #258 for v0.13.0.
  • CI is green at f9bd581.

@kikashy
kikashy merged commit 6ed3dc4 into main Oct 29, 2026
8 checks passed
@kikashy
kikashy deleted the run-leases-batch-253 branch October 29, 2026 17:09
@github-actions

Copy link
Copy Markdown
Contributor

🎉 Thank you for your contribution to Judgment Pack!

If you’d like another task, choose a path that interests you:

kikashy added a commit that referenced this pull request Oct 29, 2026
Conflict resolved: mutation-rows.json keeps main's agent#253 rows, then the
agent#252 rows; generated files regenerated with their generators.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: kikashy <35789537+kikashy@users.noreply.github.com>
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.

Run leases: bind effect-host certification to the admitted instance and test expiry on the fenced commit paths (#251 review)

1 participant