Skip to content

feat(lint): runtime-gate snapshot carries permissions/books; permission/book stack-key wiring (#8309) - #8491

Merged
qq9340100 merged 1 commit into
mainfrom
claude/issue-8309-permission-book-snapshot-wiring
Aug 13, 2026
Merged

feat(lint): runtime-gate snapshot carries permissions/books; permission/book stack-key wiring (#8309)#8491
qq9340100 merged 1 commit into
mainfrom
claude/issue-8309-permission-book-snapshot-wiring

Conversation

@qq9340100

Copy link
Copy Markdown
Collaborator

Fixes#8309

Programme slice [2 of #7891] (the strictness-rollout direction is ruled there, not re-decidable in this slice). Direct precedent: PR #8390 ([1 of #7891], merged today on this same runtime-gate surface — its scope note deliberately left this card's wiring undone).

What changed

Both halves the re-priced card asks for, bounded to what the three cross-collection rules need:

1. Snapshot enrichment (packages/lint/src/runtime-gate.ts): RuntimeStackContext gains permissions and books beside objects. Which collections was MEASURED, not guessed: the three cross-collection rules read exactly stack.objects × stack.permissions (security-master-detail-ungranted, security-private-no-readscope) and stack.books × stack.permissions (security-book-audience-unknown-set). positions/apps are read only by security-role-word — the excluded family (#7220 constraint; #8310's call) — so they are deliberately NOT carried, and the test suite pins that boundary.

The baseline/candidate differential now carries every context collection in BOTH passes (so sibling-derived findings cancel), and the replace-not-erase rule that protected objects writes is generalized to every context collection — an updated permission set or book judges a universe with one copy of itself, not two. The construction is extracted into an exported buildRuntimeWriteSnapshots, so the runtime-surface tests exercise the gate's real construction instead of the hand-kept mirror this file previously maintained (the mirror is retired).

2. The wiring: TYPE_TO_STACK_KEY gains permissionpermissions and bookbooks, ahead of their registration — the same order seed landed in (#7576#8307). The entries are inert until a rule declares those types in runtimeTypes, because dispatch filters by declared type before consulting the table; the registration flip itself is deliberately NOT here (#8310 remains open; no runtimeTypes change, no security-role-word wiring in this diff).

3. Call-site plumbing in packages/metadata-protocol (declared-allowed by the dispatch, listed explicitly): evaluateRuntimeAuthoringGate accepts optional permissions/books and forwards them into the gate context; assertRuntimeAuthoringRules in protocol.ts gathers both from the live registry per publish, through the same best-effort singular-then-plural listItems pattern objects always used (each collection guarded independently so a registry that can answer one question still answers the others). Files touched there: src/runtime-authoring-gate.ts, src/protocol.ts — nothing else in that package.

The #4463 D2 cost question — per-write, not cached (measured)

The snapshot is built per write: one filter + one spread over the written type's collection, sibling collections passed by reference; the registry read is one map walk + array copy of item references per collection. It runs only on an active-state publish (D1 — never a draft autosave), and the shipped corpus is 30 objects / 10 permission sets / 1 book, so the cost is microseconds per publish. A cache would buy nothing and would need cross-org invalidation the gate has no seam for. The choice and the numbers are recorded in buildRuntimeWriteSnapshots's docblock.

Per-write vs whole-stack agreement — the PM mechanism assumption, pinned

The measured defect (PR #7886): 38 phantom security-master-detail-ungranted findings per-write vs 4 whole-stack. validate-security-posture.runtime-surface.test.ts now pins the agreement AND keeps the defect executable as an in-tree reverse verification:

  • a permission-set write against the full context adds nothing where the whole-stack run finds nothing — both the set that grants the detail and the one that does not;
  • the same write against an objects-only context (the pre-[2 of #7891] permission/book wiring: TYPE_TO_STACK_KEY entries + the cross-collection snapshot the three comparison rules need #8309 snapshot) still reproduces the phantom finding — so deleting the enrichment turns the agreement pin red in the predicted direction (MORE findings than whole-stack, not fewer);
  • the book case both ways: a resolving audience.permissionSet is clean with the full context and phantom-flagged without it, while a genuinely dangling audience is still caught with it;
  • a permission-set UPDATE replaces its stored self (no duplicate-name double set);
  • runtime-gate.test.ts pins the construction directly: every context collection identical across both passes for a non-context write (the isolation property PR feat(lint): ADR-0091 seed pair crosses the runtime publish gate (#8307) #8390's seed proof rests on, extended to all three collections), replace semantics for a context-collection write, null for unmapped types, and safe behavior with no context at all.

No verdict changes at the door until #8310 flips registration: permission/book writes still reach no rule (pinned), and for every currently-gated type the sibling collections cancel in the differential.

Prose kept honest

authoring-rules.ts's RUNTIME_NEEDS_FULL_SNAPSHOT docblock, the validateSecurityPosture entry comment, and validateRlsPredicateEnforceability's surfaceReason all claimed the snapshot carries objects only — updated to record what #8309 changed and that the remaining gap for permission/book is the declaration alone.

Tests

pnpm --filter @objectstack/lint build → exit 0
pnpm --filter @objectstack/lint test -- --maxWorkers=2
Test Files 72 passed (72)
Tests 2009 passed (2009) (was 1978 before this card — +31)
pnpm --filter @objectstack/lint typecheck → clean (tsc --noEmit)
pnpm --filter @objectstack/metadata-protocol test -- --maxWorkers=2
Test Files 80 passed (80)
Tests 1170 passed (1170)
pnpm --filter @objectstack/metadata-protocol typecheck
→ the package declares no typecheck script (it sits in the DEBT ledger, #4311) — stated, not skipped silently

Local gates

Re-derived against the actual committed diff with node scripts/pm/dispatch-gates.mjs — it surfaced two families beyond the dispatch prompt's list, both via the packages/metadata-protocol paths, and both were run: check:durability-log-level, check:filter-alias-parity. Full local pass: check:cross-package-test-inputs (12 self-test cases + real run OK), check:spec-parsed-alias (OK), check:changeset-gate-self-tests (empty-changeset / adr-0087 / changeset-no-major self-tests OK), check:nul-bytes (7612 files, no raw control bytes), check:durability-log-level (25 seams loud/propagating; 65 read seams clean), check:filter-alias-parity (4 spellings identical both sides), check:query-options-erasure (240 sites, at the ceiling, no new files), check:type-check-coverage (OK, ledger unchanged), check:objectui-changeset self-test (OK), node scripts/check-changeset-no-major.mjs ("no major bump"). One red, pre-existing and unrelated: check:objectui-pin-fresh reports the .objectui-sha pin stale — it is equally red on unmodified origin/main (verified side by side), this diff never touches the pin, and refreshing it is release-adjacent work owned elsewhere. No packages/spec path is in the diff, so the spec artifact families are untouched.

Changeset

.changeset/permission-book-snapshot-wiring.md@objectstack/lint: minor, @objectstack/metadata-protocol: minor (precedent: PR #8390's minor for the same surface; the metadata-protocol half widens a public function's accepted inputs).

Scope note

File surface matches the dispatch exactly: packages/lint/src (runtime-gate.ts, authoring-rules.ts prose, the two test files, both barrels) plus the two declared-allowed metadata-protocol call-site files, plus the changeset. No runtimeTypes flip, no partial security-role-word wiring, no content/docs/releases/**, none of the files other in-flight cards own.


Generated by Claude Code

…/books, and permission/book map to stack keys (#8309)
Slice 2 of #7891. RuntimeStackContext gains permissions and books; the
baseline/candidate differential carries every context collection in both
passes with replace-not-erase semantics generalized from objects; the
construction is extracted and exported as buildRuntimeWriteSnapshots so
tests drive the real thing instead of a mirror. TYPE_TO_STACK_KEY maps
permission -> permissions and book -> books ahead of their runtimeTypes
registration (#8310 remains open; no registration flip here). The
metadata-protocol gate call site gathers both collections from the live
registry per publish, the same best-effort pattern objects always used.
Kills the measured RUNTIME_NEEDS_FULL_SNAPSHOT defect for the three
cross-collection rules: 38 phantom security-master-detail-ungranted
findings per-write vs 4 whole-stack (PR #7886). Agreement between the
per-write and whole-stack verdicts is pinned, with the pre-#8309 phantom
shape kept executable as in-tree reverse verification.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012MNV7ZSCjNfA38eDCjsXQL
@vercel

vercelBot commented Aug 13, 2026

Copy link
Copy Markdown

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

1 Skipped Deployment
ProjectDeploymentActionsUpdated (UTC)
objectstackIgnoredIgnoredAug 13, 2026 3:54pm

Request Review

@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 2 package(s): @objectstack/lint, @objectstack/metadata-protocol.

6 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/automation/hook-bodies.mdx(via @objectstack/lint)
  • content/docs/concepts/metadata-lifecycle.mdx(via @objectstack/metadata-protocol)
  • content/docs/deployment/validating-metadata.mdx(via packages/lint)
  • content/docs/kernel/services-checklist.mdx(via @objectstack/metadata-protocol)
  • content/docs/permissions/authorization.mdx(via @objectstack/lint)
  • content/docs/protocol/kernel/http-protocol.mdx(via @objectstack/metadata-protocol)

2 release-owned page(s) also reference the affected code. These are read-only:

  • content/docs/releases/v17.mdx(via @objectstack/lint)
  • content/docs/releases/v9.mdx(via @objectstack/metadata-protocol)

content/docs/releases/ is RELEASE-OWNED (AGENTS.md "Documentation Guardrails"): release
notes are written centrally at release time, and a code PR that edits them is the exact PR
that guardrail exists to stop. They are still audited — read-only. If one of them is actually
wrong, file an issue or open a dedicated docs-only PR; do not edit it here.

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

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

Labels

documentationImprovements or additions to documentationsize/lteststooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[2 of #7891] permission/book wiring: TYPE_TO_STACK_KEY entries + the cross-collection snapshot the three comparison rules need

2 participants

@qq9340100@claude