Repository navigation
plugin-security: the seed-ownership claim hands every unowned row to the platform admin on each app:seeded — guest-written and public-form rows included — contradicting ADR-0138 D1's "the owner stays unset" #23354
Description
Filing gate ① a product defect with a named landing, finding class (b), a seam between an accepted record and its runtime. reach: a named producer: SecurityPlugin.claimSeedOwnershipOnSettle → claimSeedOwnership runs on every app:seeded, which fires on every boot of an app with seed data. It was measured on a booted stack by #23226's dev (E3, PR #23353), and the seat read the code on origin/main. Filed by domain:services seat 2 (#21118) · zhuangjianguo · session session_01FzQy26KznSa5j2gnf3AzsE. ⛔ Not a claim; grade and lane are triage's.
Reader who acts: triage grades and routes it. The landing is packages/plugins/plugin-security/src/claim-seed-ownership.ts (and, if needed, the seed's settle report), in the C2/C3 epic's plugin-security territory (#15194).
Dedupe: two semantic issue searches on objectstack-ai/objectstack, all states, found no card for this:
- "claimSeedOwnership re-owns unowned rows to platform admin on app:seeded, guest-written row or form-door intake row handed to admin, owner should stay unset" (0 hits);
- "seed ownership claim hands non-seed records to platform admin every boot" (nearest: plugin-security: the seed-ownership claim never runs on a warm boot — seed rows inserted on a later boot (existing admin, in-budget seed) stay ownerless for good #21486 closed, which made the claim run on every
app:seeded; plugin-security: the first sign-up on a freshly seeded database blocks ~45 s while claimSeedOwnership re-owns every seed row through the full write pipeline — app hooks, record-change flows, approvals and notification emails fire for a bookkeeping write #22067, [finding] claimOrgSeedOwnership re-owns seeded rows with an isSystem-only context, so every metadata-bound hook and record-change flow runs for a bookkeeping write (the twin of #22067, whose fix reached only claimSeedOwnership) #23008 and organizations: claimOrgSeedOwnership reads at most 10,000 owner-less ids per object and the once-gate never re-runs it, so rows past 10,000 stay unowned for good #23054 are the claim's own write-path cards).
Governing text (ADR-0138, Accepted, D1)
- Item 3: "The platform stamps nothing on a guest insert and declares no organization-level default owner."
- Item 5: "Empty state: the owner stays unset. When nothing in the scenario sets it, a guest-written row keeps no owner, exactly as the public form doors' intake does today; hooks and flows may set it."
- The supplement ruling 「D1 A′ D2b R」 removed the organization-level default owner.
The seam
- The claim's declared purpose (its own header): "hand seeded business records to the first platform admin". These are rows the seed wrote with
owner_idNULL or the seed identity, before any human existed. - Its predicate (
UNOWNED_PREDICATES) is wider than that purpose. It re-owns every row of every user-authored object with anowner_idcolumn whose owner is NULL or the seed identity, and it cannot tell a seed row from a row written later. - Since plugin-security: the seed-ownership claim never runs on a warm boot — seed rows inserted on a later boot (existing admin, in-budget seed) stay ownerless for good #21486 it runs on every
app:seeded(every boot), not only at the first promotion.
Measured (booted stack, main at bbb9757717)
- A guest-written row and a public-form-door row, both unowned as D1 requires, were each re-owned to the platform admin by one
app:seeded. The claim logged that it handed 2 seeded records to the platform admin. - The stamp's own guest branch (E3, PR feat(security)!: an anonymous caller never owns a record and never sets its owner (ADR-0138 E3) #23353) leaves the owner unset as D1 requires. This claim then overrides it at the next seed settle.
Direction (the seat's lean; the record decides it)
Narrow the claim to the rows the seed wrote: re-own only the records the settling app's seed datasets wrote, and leave every other unowned row as its writer left it. The datasets' identities or the seed's own settle report would be the selector, measured by the fixing seat.
- Amending D1 to call the platform admin the default owner is the option the supplement ruling rejected. It would bend the record to the code, so it is not an option here.
- A per-row provenance marker adds a stored surface with no measured demand.
- Pins:
- a seed row is still claimed;
- a guest-written row and a public-form row keep no owner across boots;
- a row a hook, flow or assignment rule owned keeps its owner.
Related: #23226 (E3; PR #23353 does not depend on this card) · #21486 · #15194 · ADR-0138 D1.
Generated by Claude Code
Activity