docs(verify): record the #12981 determination that the audience-gate seed is not a durability swallow - #13884
Conversation
…seed is not a durability swallow `scripts/measure-durability-swallow-family.mjs` reports `harness.ts::inviteForAudienceGate` as tier-1 DARK on three mechanical conjuncts and leaves the fourth -- "and the caller still reports success" -- to a person. This records that answer in the file, the way `packages/runtime/src/domains/keys.ts` does, so the next census reading is legible instead of re-derived. The answer is NO: the helper returns `Promise<void>` and has exactly one caller, whose next statement performs the operation the row is a precondition for -- a lost row makes `signUp` throw with the audience gate's real status and body. Comment-only; no behaviour changes. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016ZC5rNQj3WEet5HAmmAkMs
…bracket fragments Two type/format fragments in the annotation prose were spelled with angle brackets. The GitHub body sanitizer eats short tag-shaped fragments, even inside backticks, so an annotation meant to be quoted verbatim into the card would arrive corrupted. Spelled out instead, with no loss of precision. Comment-only; no behaviour changes. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016ZC5rNQj3WEet5HAmmAkMs
📓 Docs Drift CheckThis PR changes 1 package(s): ⛔ 1 release-owned page(s) name something this change touched. These are read-only:
What this run could not see
Coarse fallback — 2 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin db5498252feef666c382ca7b01911dd6644e665c && git checkout db5498252feef666c382ca7b01911dd6644e665c
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 9b21037d4198cd14105abd354b019e9323b4fe57 a4603a4c48ea9ac90578b22db6bd8afa786ac209 && git checkout -B drift-repro 9b21037d4198cd14105abd354b019e9323b4fe57 && git merge --no-ff a4603a4c48ea9ac90578b22db6bd8afa786ac209
node scripts/docs-audit/affected-docs.mjs --json 9b21037d4198cd14105abd354b019e9323b4fe57
|
Part of #12981 — batch 8. A determination, not a repair.
Batches 1–7 repaired every DARK site that was a repair. This batch answers the one question left, per site:
Two different answers, both backed by a reading of what the caller actually sees.
packages/verify/src/harness.ts :: inviteForAudienceGate— NOT a claim-to-persist ⇒ annotatedRecorded in the file, in the shape
packages/runtime/src/domains/keys.tsestablished. The whole diff is that annotation — comment-only, no behaviour change.Caller-visible evidence, not an adjective:
Promiseofvoid(harness.ts:694) — no return value, no counter, no report out of which any caller could read a landed row.harness.ts:727(signUp) is the only call site in the repo. Its very next statement POSTs/auth/sign-up/email— the operation the row is a precondition for. Under the defaultinvite_onlyposture a missing invitation makes that POST refuse andsignUpthrowsverify signUp failed: ${res.status} ${await res.text()}, carrying the audience gate's real status and body. Underopen/email_domainsign-up succeeds on its own merits and the row was never needed. Those are the only two branches — which is what the seam's own "the gate answers either way" means, now stated so it can be checked.org_verify_audience_gate/usr_verify_audience_gateappear at exactly two sites in the repo, both of them the write in this function. Nothing reads them back.sys_invitationobject.@objectstack/verifyboots in-process against in-memory SQLite; the file header: "This is a verification harness — it never touches a real database or production data." There is no deployed plane that could go on looking healthy.⇒ AGENTS.md's third legal answer. The annotation also records why a
FAILURE_PROPAGATION_SITESentry is not the right declaration here: that one asserts every path out of the catch delivers, and this catch returns normally — the delivery is the caller's next call. (It would also go red as STALE today, exactly askeys.ts's annotation records, sinceDURABILITY_CRITICAL_CALLEEShas noinsert.)packages/plugins/plugin-sharing/src/share-link-service.ts :: resolveToken— IS a claim-to-persist ⇒ handed back, not repairedThis reverses batch 6's "probably not / usage telemetry" reading. Per the dispatch's hard boundary, nothing in this file is touched — the tier call belongs to its own repair batch.
Caller-visible evidence:
use_countandlast_used_atare declaredreadonly: trueonsys_share_link, andpackages/objectql/src/readonly-strict-errors.ts:130records that{ context: { isSystem: true } }"exempts statically 'readonly' fields" — soSYSTEM_CTX(share-link-service.ts:44) is exactly what makes this update legal rather than a silent no-op strip.share-link-routes.ts:276-286,runtime/src/domains/share-links.ts:176-186).objects/sys-share-link.object.ts:60putsuse_countandlast_used_atin the shippedactive_linksgrid; the object header says admins inspect via the audit grid and rows are "preserved for audit". The field declarations state the invariant, shipped in four locales:use_count— "Incremented by resolveToken on every successful resolution";last_used_at— "Stamped by resolveToken; used by the dashboard to highlight active links".resolveTokenreturns its result), and there is a write.share-link-service.test.ts:280-284: a stamp that ticks wrongly is "a bad signal for anyone auditing a leaked link." Measured:use_countis never read for enforcement anywhere — there is no max-uses gate that would fail closed and surface the loss.Census — measured before and after, both readings byte-identical
Instrument:
scripts/measure-durability-swallow-family.mjs, self-test green before every reading (4 positive controls at their declared tier, 3 negative yield none, 2 regression controls stay clear, 56 member sites total).Both readings taken with the instrument's self-test green, and the after-reading re-taken on the final commit
a4603a4c4.keys.tsdemonstrates the same thing — it has carried its determination for rounds and is still listed. The annotation moves a site off the human worklist, not off the instrument's count. Measured, not assumed: the base tree was restored under a trap and re-run, and the two census outputsdiffto zero.Gates
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstackfrom this worktree, on the actual diff (1 path). Both output sections read whole. 19 families — 9 pnpm, 10 direct node; no.mdxin the diff, so no re-derivation was owed. Exit codes captured before any pipe; every verdict quoted from the gate's own line.check:cross-package-test-inputs·check:doc-authoring·check:logger-receiver-detach·check:page-declaration-shape·check:published-files·check:slot-lookup·check:test-source-alias·check:type-source-resolution·check-ci-filter-parity·check-comment-mask-adoption·check-cross-package-test-inputs·check-keyed-text-bounds·check-plugin-teardown-shape·check-shard-attestation·check-undeclared-dep-imports·docs-audit/check-affected-docs·docs-audit/check-drift-comment. Pluscheck:nul-bytes("scanned 7608 text file(s) … no raw ASCII control bytes") for the edit itself.pnpm lint— WHOLE REPO, run in full, no narrowing to declare:eslint . --no-inline-config,command-exit 0, re-run on the final commita4603a4c4(60s under the shared verify lock).node scripts/check-test-completeness.mjs— exit 3 = PREREQUISITE NOT MET. It grades a savedturbo run testlog; CI tees a real one.pnpm check:durability-log-levelon this family — measured both directions, byte-identical: "✓ durability-degradation log levels: 29 durability-critical catch seam(s), all loud, rethrowing or propagating to the caller (4 propagating, declared); 2 counter-guarded summary report(s) outside a catch" before and after. Its green is never "level approved", in either direction — and it sees no seam ininviteForAudienceGateat all, since its vocabulary carries noinsert.pnpm check:dual-build-cjs-loads— not run locally; it demands a full workspace build, and the diff is comment-only. CI runs it.Typecheck — a real reading, not a zero-match exit 0.
@objectstack/verifydoes declaretypecheck: tsc --noEmit. The first run reported 19TS2307 Cannot find module '@objectstack/…'— a pure build prerequisite. Cleared exactly the closure it named (turbo run build --filter='@objectstack/verify^...' --concurrency=2, 32 tasks successful) and re-ran ona4603a4c4: 0 errors, with--listFilesconfirmingharness.tsreally is in the program (1 hit).Tests. The two suites that actually exercise
signUp→inviteForAudienceGate—src/erasure-transaction-authorization.test.tsandsrc/harness.app-default-profile.test.ts— run green: 2 files, 10 tests passed.Before/after was measured, not asserted. The annotation was committed first (
b1aa36a35; final heada4603a4c4) so the restore leg had a real reference. The mutation script carriestrap … EXIT INT TERMwith absolute paths; mutation and restore were both confirmed on disk by blob hash, never by an editor's exit code — PRE5dca167b(asserted equal to the HEAD blob), POSTc2c448f7(asserted equal to the base blob and different from PRE; an empty hash treated as FAILURE), marker[#12981]1 → 0 → 1. Restore proved by state:git diff HEAD0 lines,git status --porcelain0 entries, worktree hash back at the HEAD blob.Ratchet ledgers.
scripts/durability-degradation.baseline.json— not in the diff, still empty, zero rows added.engine-double-contract.pinned.json— not in the diff, and unmoved.scripts/slot-lookup-baseline.json— not in the diff;check:slot-lookupreports "106 unswept site(s) in 25 file(s), none new… baseline key set verified against add6a1b: no files added."Changeset
skip-changeset, applied to this PR as an additive POST and read back. The diff is one comment-only hunk in one file: it publishes nothing, changes no behaviour, and adds no user-visible change for a release note to carry.Remaining DARK after this batch
Session: https://claude.ai/code/session_016ZC5rNQj3WEet5HAmmAkMs
Generated by Claude Code
Generated by Claude Code