Uh oh!
There was an error while loading. Please reload this page.
feat(lint): ADR-0091 seed pair crosses the runtime publish gate (#8307) - #8390
Conversation
Register runtimeTypes: ['seed'] on the validateSecurityPosture registry entry (packages/lint) so security-grant-expired-at-authoring and security-delegation-missing-reason enforce at the runtime metadata write door for a seed publish, per the #7891 programme's #4001-pattern staging. Re-measured trip-free rather than inherited from #7576/#8308: none of the four shipped stacks (showcase, CRM, todo, blank) author any seed row on sys_user_position / sys_user_permission_set at all (grep across every defineSeed() call site), so the ADR-0091 loop body never executes for the current corpus -- structurally zero, not merely counted zero. The other eleven rule ids this one registry entry also carries (object / permission / book posture, security-role-word, ...) stay undeclared -- that is #8310, still blocked on a strictness rollout and RUNTIME_NEEDS_FULL_SNAPSHOT. Declaring runtimeTypes: ['seed'] on the WHOLE entry rather than splitting it is safe because runtime-gate.ts's baseline/candidate differential holds stack.objects identical across both passes for a seed write, so every other-rule finding fires identically in both passes and cancels in the diff -- proven in validate-security-posture.runtime-surface.test.ts, including a positive control (a trippy object context that WOULD leak if the isolation failed). Extends validate-security-posture.runtime-surface.test.ts with the pin recording the crossing (real runRuntimeAuthoringRules calls, not the pre-crossing mirror) and a changeset. Fixes#8307 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Euoy6wyfzgiWtgCg4s6JK2
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
📓 Docs Drift CheckThis PR changes 1 package(s): 3 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
⛔ 1 release-owned page(s) also reference the affected code. These are read-only:
|
Uh oh!
There was an error while loading. Please reload this page.
Fixes#8307
Programme slice of #7891 (staging per #4001 pattern, #7220/PR #7479 precedent — the strictness-rollout direction is ruled, not re-decidable in this slice).
What changed
packages/lint/src/authoring-rules.ts— thevalidateSecurityPostureregistry entry now declaressurfaces: ['cli', 'runtime-publish']withruntimeTypes: ['seed'](previouslyCLI_ONLYwith asurfaceReason). This puts the ADR-0091 seed pair —security-grant-expired-at-authoring,security-delegation-missing-reason— on the runtime metadata publish door (Studio, REST/meta, MCP/AI authors) for aseedwrite, per #7576's own staging note that this pair was the one slice ready to cross.Why declaring it on the WHOLE registry entry is safe
This entry is the WHOLE
validateSecurityPosturefunction (all 13 rule ids) — there is no per-rule-id split inauthoring-rules.ts.runtime-gate.ts's baseline/candidate differential is what makesruntimeTypes: ['seed']safe to declare here without leaking the other 11 rule ids' verdicts: aseedwrite's candidate stack carriesobjectsIDENTICAL to the baseline (onlydatadiffers), so every finding this function derives fromstack.objects/stack.permissions/stack.positions/stack.apps/stack.booksis produced byte-identically in both passes and cancels in the diff. Only the ADR-0091 pair'sstack.data[]reads can differ between the two passes. This is proven — not just asserted in a comment — by a new positive-control test: aseedwrite against a context object that WOULD tripsecurity-owd-unsetandsecurity-role-wordif the isolation failed, asserting zero leaked findings.object/permission/bookremain undeclared — that is #8310, still blocked (a strictness rollout onobject,RUNTIME_NEEDS_FULL_SNAPSHOTonpermission/book).security-role-wordis deliberately not in this slice either (#7220 constraint — it judges six collections and wiring only the snapshot-safe two would split one rule id across the wall).Re-measured, not inherited (hard constraint from the dispatch)
The dispatch required re-running the "trip-free on the shipped corpus" claim rather than trusting #7576/#8308's numbers. I grepped every
defineSeed(...)call site across all four shipped stacks (showcase, CRM, todo, theblankcreate-objectstack template):examples/app-showcase/src/data/seed/index.tsseeds Account, Contact, Project, Task, Category, BusinessUnit, Team, Product, ProjectMembership, FieldZoo, Invoice, InvoiceLine, ExpenseReport, ExpenseLine, Inquiry, Preference, Announcement.examples/app-crm/src/data/index.tsseeds Account, Contact, Opportunity, Lead, Activity.examples/app-todo/src/data/index.tsseeds Task.packages/create-objectstack/src/templates/blankhas no seed data at all.None of the four apps authors a
sys_user_positionorsys_user_permission_setseed row — grep forvalid_until,delegated_from, and either object name across all three source trees returns zero hits. Showcase's own approval demo (src/security/seed-approval-demo.ts) explicitly does this at runtime instead, precisely because "users can't be seeded (they sign up) and position assignments are runtime admin actions." So the ADR-0091 loop body (GRANT_SEED_OBJECTS.has(seedObject)) never executes for any of the four shipped apps — this is a structural zero for the current corpus, not a measured-and-hoped-stays-zero one. No STOP condition was hit.Tests
packages/lint/src/validate-security-posture.runtime-surface.test.tsextended with the pin recording the crossing:surfaces: ['cli', 'runtime-publish']/runtimeTypes: ['seed'];runRuntimeAuthoringRules({ type: 'seed', ... })(previously only the pre-crossing mirror could be asked);object/permission/book/position/appstill reach no rule (unaffected — [3 of #7891] Flip the registration:runtimeTypesgainsobject+permission/book— whole rule families cross the publish door #8310's residue).Local gates (
node scripts/pm/dispatch-gates.mjs <changed paths>)Re-derived against the actual committed diff (
git diff --name-only 2d8dba312 HEAD) — same file set as before commit, no drift:check:spec-parsed-alias— OKcheck:cross-package-test-inputs— OK (self-test + real run)check:changeset-gate-self-tests(empty-changeset / adr-0087-registration / changeset-no-major self-tests) — OKcheck:objectui-changeset,check:objectui-pin-freshself-tests — OKnode scripts/check-changeset-no-major.mjs— "This diff introduces nomajorbump"node scripts/check-adr-0087-registration.mjs— "this PR adds no declared-breaking changeset" (correct: this is additive enforcement, not a spec key removal/rename)check:query-options-erasure— ratchet holds, no new unswept sites;check:type-check-coverage— unaffected,@objectstack/linttypechecks cleancheck:nul-bytes— OK, no raw control bytescheck:api-surfaceis untouched, as expected — nopackages/specsource in this diff.Changeset
.changeset/security-posture-seed-pair-runtime-publish.md,"@objectstack/lint": minor— following the direct precedent of.changeset/visibility-predicate-family-runtime-publish.md(#7220's own family crossing the runtime-publish wall, alsominor) rather than the dispatch prompt's default "patch unless conventions say otherwise": this is a real behavior change (a class of runtime writes that used to succeed will now be refused), and the established convention for "a rule crosses ontoruntime-publish" in this repo isminor, notpatch.Scope note
File surface matches the dispatch exactly: the
validateSecurityPostureregistration inpackages/lint, the runtime-surface pin extension, and the changeset. Noobject/permission/bookwiring, nosecurity-role-word. Did not touch #8273's (error-code-ledger + service-settings) or #8057's (objectql engine + kernel schema) files.Generated by Claude Code