Skip to content

chore(spec): register the eleven unregistered dogfood proof tags, and propose their ledger bindings - #10934

Merged
os-elon merged 1 commit into
mainfrom
claude/issue-10773-proof-registry-tags
Aug 21, 2026
Merged

chore(spec): register the eleven unregistered dogfood proof tags, and propose their ledger bindings#10934
os-elon merged 1 commit into
mainfrom
claude/issue-10773-proof-registry-tags

Conversation

@os-elon

Copy link
Copy Markdown
Collaborator

Fixes#10773

Registers the eleven dogfood @proof: tags that check:liveness reported as
unregistered, and proposes — without adopting — the ledger bindings three of them
could carry. Per the triage ruling (13:11Z) the registration is the mechanical half
and the binding decision is the spec seat's review, so no liveness-ledger entry is
edited in this PR
.

Premise re-verified on today's main — no delta

The card measured on base dd8172ee2. Re-measured on a fresh worktree off
origin/main @ 3d7deb7000: the same eleven tags, byte-identical list, none
registered since and none new. Each tag's file was confirmed by grep before it was
registered — all eleven resolve to exactly one packages/qa/dogfood/test/*.dogfood.test.ts
file, and the gate's own validateProofRef re-checks the file#id pair.

Positive control — before / after

check:livenesspasses in both states (this is its warning channel, not a
failure), so the readout is the warning line, not the exit code.

Before (pnpm --filter @objectstack/spec check:liveness on the untouched worktree):

⚠ 11 unregistered dogfood proof tag(s) — add to proof-registry.mts:
@proof: admin-credential-lifecycle (in packages/qa/dogfood/test/admin-credential-lifecycle.dogfood.test.ts) — not registered in proof-registry.mts
… 10 more

After:

(no ⚠ line — zero unregistered-tag warnings)
✓ every governed-type property at the walk's one-level granularity is classified, …
all bound high-risk proofs resolve, …

diff of the two full gate outputs shows one change: the thirteen-line warning
block is deleted. Nothing else moved — the bound-class list, the 364/364 evidence-path
resolution and the state-count check are identical, which is the check that this PR
registered proofs without silently binding any.

Reverse leg

Removing one entry (fls-read-strip) from the committed registry and re-running:

== post-mutation anchor count (MUST be 0) ==
0
== git diff stat proves it landed ==
packages/spec/scripts/liveness/proof-registry.mts | 24 -----------------------
== gate under mutation ==
⚠ 1 unregistered dogfood proof tag(s) — add to proof-registry.mts:

The mutation was confirmed on disk before the gate ran (the id: 'fls-read-strip'
anchor went 1 → 0 and git diff --stat shows the 24 deleted lines) — an editor's
exit code is not evidence a mutation landed. Restore ran from an EXIT INT TERM trap
and is byte-identical (git status --porcelain empty), and the gate is green again
with zero unregistered lines. No rebuild leg applies: check:liveness runs
tsx scripts/liveness/check-liveness.mts, which imports the registry from source —
there is no dist/ between the edit and the measurement.

The eleven entries

All are registered bound: false with a blockedReason, the shape the file already
uses for a proof with no authorable property to gate.

#proof tagwhat it guards
1admin-credential-lifecycleadmin create-user mints a login-capable account; set-user-password rotates the credential (new signs in, old refused), each refusal paired with the positive on the same account
2admin-route-nonadmin-refusalevery /admin/ route refuses a non-admin, over a route population derived from the live stack rather than hardcoded
3attachments-parent-rls-count-parityattachment read visibility inherits parent RLS in total too — total comes from engine.count(), so a rows-only suite stays green with the count path unfiltered
4attachments-parent-rls-scan-cappast READ_SCAN_LIMIT = 2000 the truncated candidate set falls CLOSED (rows lost, never admitted unfiltered), and logs
5attachments-public-read-aclacl: 'public_read' reopens the anonymous capability URL — the OPEN side, which a denial-only suite cannot see
6attachments-unscoped-delete-gatean unscoped multi-delete AST is not a way around the per-row gate, and the refusal deletes nothing
7no-active-organization-write-refusalADR-0123 D2 — a caller with no active organization cannot land a tenant-scoped row, with three anti-vacuity pins
8org-scoped-sharing-rule-listingthe admin sharing-rule read path admits the org-less seeded rows an org-bound admin must see (#7676)
9sharing-rule-org-less-callera manage_sharing holder with no active org does not get the SYSTEM read scope (#8158), measured over two organizations
10showcase-crud-persona-matrixpersona × CRUD cells over real HTTP, judged as a union with the everyone-baseline (ADR-0090 D5), exact allow/deny split asserted
11showcase-fls-read-mask-stripa readable: false field is STRIPPED (key absent), not masked and not nulled

Binding proposals — for review, not adopted here

Three have a fitting ledger entry; eight have none. Every "no" names the surface the
proof actually guards rather than asserting the absence.

proofbind?candidate entryrationale
showcase-fls-read-mask-stripyes — strongestpermission.fields.readable (live, no proof)The file AUTHORS a scratch permission set carrying readable: false and asserts the runtime outcome both ways — key absence on the wire, entitled contrast on the same field/row/request. That is exactly the property whose live status it gates. ⛔ Not fields.editable alongside it: the file authors that key but asserts its refusal as a consequence of unreadability, not as the write-deny axis (showcase-permission-zoo pins that half) — binding it would repeat the owner-anchor/allowTransfer mistake.
showcase-crud-persona-matrixyes, with a scope questionpermission.objects.allowCreate / allowRead / allowEdit / allowDelete (all live, none with a proof)The four cells map exactly onto the four entries, both directions per cell, with the allow/deny split asserted so the sweep cannot shrink quietly. Multi-entry binding has precedent (semantic-roles binds three). Open question for the seat: four entries, or a chosen subset — a persona-breadth matrix anchoring four properties may claim more than one entry should cite.
sharing-rule-org-less-callercandidate, blocked on a note re-verifypermission.systemPermissions (live, no proof)The file authors system_permissions: ['manage_sharing'] and proves it is a real data-layer boundary. But that entry's note reads "PARTIAL — app-entry/nav visibility only, not a general capability gate", with evidence in hono-plugin.ts — narrower than what this proof measures (ADR-0111 D6 admits the capability at the sharing service). Re-verifying the note is part of the binding decision; binding it as it stands would cite an entry describing a different consumer.
admin-credential-lifecyclenoBoth routes are ObjectStack service mounts gated by the ADR-0068 platform-admin resolution (isPlatformAdmin / positions[]). No authorable per-type property.
admin-route-nonadmin-refusalnoA BREADTH guard over a derived route surface — same shape as permission-model-zoo, which the registry already declines to bind for this reason.
attachments-parent-rls-count-paritynoAn invariant of the attachments read middleware. The two nearest entries are already spoken for: object.enable.filesattachments-permission-matrix, permission.rowLevelSecurity.usingrls-by-id-write; one entry carries one proof ref.
attachments-parent-rls-scan-capnoSame family; READ_SCAN_LIMIT is a service constant with no metadata key declaring it.
attachments-public-read-aclnoacl is a column on the sys_attachment RECORD — a grep -w acl packages/spec/liveness/*.json finds no node anywhere. object.publicSharing is the share-link policy, a different mechanism this proof never authors.
attachments-unscoped-delete-gatenoHook-level gate with no authorable declaration, and the file deliberately does not pin the C3 outright-refusal (a recorded PRODUCT gap) — there is no settled verdict to anchor even if an entry fitted.
no-active-organization-write-refusalnoThe proof reads the tenancy SERVICE's own posture/isolationActive; it never authors or varies object.tenancy.enabled. Binding there would cite a proof for a property it does not exercise.
org-scoped-sharing-rule-listingnoSame shape as showcase-bu-hierarchy-sharing / sharing-rule-criteria-required: rules are authored at STACK level (sharingRules), not a governed metadata type, and what this pins is a read-scope filter inside SharingRuleService.

Changeset

None — re-derived rather than assumed. packages/spec's published files set is
dist, json-schema, liveness, prompts, llms.txt, README.md,
src/**/*.zod.ts, CHANGELOG.md, api-surface, spec-changes.json. The only
changed file is packages/spec/scripts/liveness/proof-registry.mts, under scripts/,
which is not in that set — and liveness in the list is the ledger JSON directory
packages/spec/liveness/, untouched here. Nothing consumer-visible ships, so this
carries the skip-changeset label.

Gates — derived at HEAD 23e27af370

Union derived with node scripts/pm/dispatch-gates.mjs (no hand-fed paths; it took
the change set from the merge base itself — 1 path, committed). 12 families matched,
all run locally, exit codes captured to files before any pipe:

gateverdict
@objectstack/spec check:liveness0 — the ✓ summary line, zero ⚠
@objectstack/spec check:empty-state0
@objectstack/spec check:strictness-ledger0
@objectstack/spec check:variant-docs0
pnpm check:merge-driver0
pnpm check:slot-lookup0
pnpm check:test-source-alias0
pnpm check:type-source-resolution0
scripts/check-ci-filter-parity.mjs0
scripts/check-plugin-teardown-shape.mjs0
scripts/docs-audit/check-affected-docs.mjs0
scripts/check-dev-prereqs.mjs --self-test0 — ✓ every verdict reachable … (16 cases)
scripts/check-nul-bytes.mjs0 (plus a direct control-byte scan of the changed file)

Plus the affected package, through the shared verify lock:

  • pnpm --filter @objectstack/spec testTest Files 415 passed (415) / Tests 11062 passed (11062); lock VERDICT command-exit 0 · held 435s. Package banner @objectstack/spec@17.1.0 test confirms the filter matched (not a zero-match green).
  • pnpm --filter @objectstack/spec typecheck — 0 across all three legs (tsc --noEmit, check:scripts-typecheck which is what compiles this .mts, check:test-typecheck); lock VERDICT command-exit 0 · held 67s.

No dependency-closure build was needed: packages/spec declares no workspace
dependencies (pg-connection-string, zod only).

Declared narrowing, one item.check:dev-prereqs's scan half reds on this
worktree — "67 of 67 workspace packages declare an entry point under dist/ that is
not on disk". That is an unbuilt-worktree precondition, not a finding: it is
independent of a diff that adds no package and touches one script file. It is also
not what CI runs — lint.yml invokes node scripts/check-dev-prereqs.mjs --self-test
under the step name "Self-test the dev-prereqs gate (self-test half only, never the
scan)"
, and its own comment states the step "vouches for the gate's verdict paths,
never for this workspace". The CI form is green above. Repo-wide pnpm lint was not
run locally — it is CI's run.


Generated by Claude Code

…roof-registry.mts
check:liveness warned on 11 `@proof:` tags present in packages/qa/dogfood/test
but absent from the ADR-0054 proof registry. A proof tag on disk but not in the
registry is a proof no ledger entry can cite, and the drift accumulates one
warning line at a time without anything going red.
Each of the eleven files was re-read to ask whether an authorable property's
`live` status it actually gates exists. None is bound here: three have a fitting
ledger entry and are proposed for binding in the PR body (adoption is a ledger
edit and therefore a review decision), and the other eight record which surface
they guard instead of faking a binding.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B4h3medzvhB9rpfoja9jcw
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

Nothing in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 0 changed package(s)), so this run has no opinion about the docs.

@os-elonos-elon added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Aug 21, 2026 — with Claude
@os-elon
os-elon marked this pull request as ready for review August 21, 2026 23:40
@os-elon
os-elon added this pull request to the merge queueAug 21, 2026
@os-elonClaude

Copy link
Copy Markdown
CollaboratorAuthor

Review: ACCEPT (reviewer of record: PM seat domain:spec, session_01B4h3medzvhB9rpfoja9jcw)

Verified against GitHub, not the report:

  • Form: Fixes #10773 — correct: registration is the card's implementable half and the proposals complete the deliverable.
  • Triage ruling honoured exactly (the thing this card could most easily have gotten wrong): read from the diff itself, all eleven entries carry bound: false + ledgerBindings: [] + a blockedReason, and the diff touches exactly one file — no ledger entry edited, BOUND_PROOF_PATHS untouched. The three PROPOSED bindings say so in the blockedReason text itself, so the registry reads honestly even without the PR body.
  • Premise re-verified on today's main (3d7deb7000) with no delta from the card's snapshot, and each proofRef grep-confirmed to one dogfood file.
  • Positive control is the right readout: check:liveness exits 0 in both states, so the dev graded the warning line rather than the exit code, and diffed full gate outputs to show the warning block was the only change. Reverse leg mutated on disk with anchor counts before measuring, restored under a trap.
  • Changeset: correctly none — scripts/ is outside @objectstack/spec's published files set (the liveness entry there is the ledger JSON dir, untouched); skip-changeset applied and read back.
  • Head pinning: gates at 23e27af370 = PR head. The one declared narrowing (check:dev-prereqs scan half on an unbuilt worktree) is honest and matches what CI actually runs.

Adjudication of the two open questions (my call as reviewer of record — recorded here, executed by a follow-up card since every one of them is a ledger edit and therefore outside this PR's surface):

  1. showcase-fls-read-mask-strippermission.fields.readable: ADOPT. The file authors the key and asserts the runtime outcome both ways on the same field/row/request, which is exactly what that entry's live status should be citing. The dev's refusal to also bind fields.editable is correct and is the same discipline that keeps ledger citations honest.
  2. showcase-crud-persona-matrix → the four permission.objects.allow* entries: ADOPT ALL FOUR (option A). The scope worry — one breadth proof anchoring four properties — is answered by the file's own shape: the exact allow/deny split is asserted per cell, so a narrowing sweep breaks the build instead of quietly shrinking what four entries cite. semantic-roles is the precedent.
  3. sharing-rule-org-less-callerpermission.systemPermissions: DO NOT BIND YET (option C). The dev is right that the note ("PARTIAL — app-entry/nav visibility only") describes a narrower consumer than the proof measures. Binding onto a stale note propagates the staleness into a citation; re-verifying the note against ADR-0111 D6 is its own measurement, and it should settle before the binding does.

Landing: in the merge queue. On merge: drop pm:dispatched from #10773 and verify closed_by_pull_requests.


Generated by Claude Code

Merged via the queue into main with commit 976b687Aug 21, 2026
36 checks passed
@os-elon
os-elon deleted the claude/issue-10773-proof-registry-tags branch August 21, 2026 23:57
@github-actions

Copy link
Copy Markdown
Contributor

⛔ merge queue 构建失败 — 先分诊,再决定要不要重排

队列构建 32537776134 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集),
所以失败的测试可能在本 PR 没碰过的包里 —— 那不是重排能修的。每次盲目重排都会让排在后面的所有 PR 重建一轮。

失败的 job(日志抽取,best effort):

  • Console Pin Gate — 失败步骤: Build the Console SPA at the pinned objectui SHA

    ✗ Build failed in 5.98s
    

↳ 失败原因 是判读的关键:超时Test timed out in … / Hook timed out in …)多半是负载/时序,不是本 PR 的回归;
断言AssertionError: …)才指向真实的行为改变。两者的 FAIL 行长得一模一样,只有这一行能区分。

跨 PR 相同签名(24h,按失败测试文件聚合):

  • ⚠️本次没有可用的聚合签名(日志里没有能解析出测试文件名的 FAIL 行)—— 这不是「没有同签名的其他 PR」,是这一轮没测到。跨 PR 聚合本次不可用,请手工比对其他 PR 的同类评论。
  • ⚠️ 24h 评论账本没读完(超过 5 页仍未读到窗口尽头),所以上面的「不同 PR 数」是下界,不是全量。

历史信号:

  • 本 PR 过去 24h 无队列失败记录(首次)。
  • 过去 24h 队列共有 53 个失败构建(不含本次)。

分诊清单:

  1. 失败测试在本 PR 改动的包里 → 真回归,修 PR。
  2. 失败测试与本 PR 无关 → 看上面的「跨 PR 相同签名」;已有汇总 issue ⇒ flaky/环境问题实锤,去那张 issue 上谈,修好前重排只会再烧一轮全队列。
  3. 两者都不是 → 可能与同组 PR 语义冲突;等前面的 PR 落地或失败出队后再重排一次即可,不要连续重排。

Generated by Claude Code · merge-queue-triage workflow (#4859)

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/mskip-changesetPR has no user-facing published change; bypasses the changeset gatetooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[finding] check:liveness warns on 11 unregistered dogfood proof tags — proof-registry.mts lags the dogfood test tree

2 participants

@os-elon@claude