You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The DERIVED half of bootstrapSystemCapabilities is still per-item, and batching it is a ruling, not a refactor — it trades away #8751's platformStampedInOrg signal or reverses part of #8552 #11520
The residue of #11451. That card batched the CURATED half (one $in carrying the #8470 predicate) and equality-gated the reconcile for both halves. The derived half's existence read is untouched, and this is the write-up of why, so the next attempt starts from the analysis rather than repeating it.
Unscoped and cross-organization by construction (SYSTEM_CTX carries no tenantId), so under #4363's ORDER BY id ASC tie-breaker this is the lowest-id row for the name, installation-wide. Call it X. Everything the half then does is a function of X:
skippedAuthored counts the passes where X is not ours;
platformStampedInOrg counts the subset where X wears the platform stamp while sitting inside an organization;
only on that branch is the NULL-organization bucket read at all, and only then can unseededDerived and its warning fire.
Why it cannot simply be batched
Unnarrowed — { name: { $in: … } } — the result set is one row per organization per name, bounded only by the number of organizations, against a page capped at one row per name. It truncates, and a truncated page reads as "absent", which inserts. (That cap is a defect in its own right for the callers already using it: #11518.)
Narrowed to the platform bucket — { name: { $in: … }, organization_id: null } — is bounded and cheap, and answers a different question. Let B be the bucket occupant (at most one, by the declared key (COALESCE(organization_id, '__global__'), name)). B and X differ whenever an organization's row has a lower id, and the two divergences both land on ruled ground:
Both are pinned: bootstrap-system-capabilities.test.ts asserts platformStampedInOrg at the fixture for (1), and the #8552 posture at the fixture for (2).
Bucket-scoped batched read, accepting the two divergences above. Cheapest; needs a maintainer ruling on both, because each undoes a decision that was made explicitly.
Leave it. Note the derived set is EMPTY on a stock installation — the platform's own permission sets grant only names already in KNOWN_CAPABILITIES, and package-declared names are excluded via materializedCapabilityNames. It grows only for an app whose defaultPermissionSets grant capability strings that nothing declares.
Recommendation: 1, folded into #11518 rather than done twice, and only if #11518 is being repaired anyway. Option 3 is defensible on its own until an installation is measured paying for it — the growth axis is real but the stock cost is zero, and options 1 and 2 both spend a shared file's complexity or a ruling to buy it.
Not measured
The slope of this axis has never been measured. The hosted bootstrap-curve.mjs rig lives in objectstack-ai/cloud and its axes are permission sets / positions / objects. What is established in-repo is the round-trip count, pinned in bootstrap-seed-round-trips.test.ts — where the derived half's residue is stated as 1 + derived reads rather than hidden.
The residue of #11451. That card batched the CURATED half (one
$incarrying the #8470 predicate) and equality-gated the reconcile for both halves. The derived half's existence read is untouched, and this is the write-up of why, so the next attempt starts from the analysis rather than repeating it.What the derived half's read actually is
packages/plugins/plugin-security/src/bootstrap-system-capabilities.ts:Unscoped and cross-organization by construction (
SYSTEM_CTXcarries notenantId), so under #4363'sORDER BY id ASCtie-breaker this is the lowest-idrow for the name, installation-wide. Call itX. Everything the half then does is a function ofX:derivedRowIsOurs = X.managed_by === 'platform' && X.organization_id == null([finding] The derived capability seeder rewrites a platform-marked row INSIDE an organization and still leaves the platform bucket unseeded — silently, on the UPDATE branch #8536 did not scope #8751);skippedAuthoredcounts the passes whereXis not ours;platformStampedInOrgcounts the subset whereXwears the platform stamp while sitting inside an organization;unseededDerivedand its warning fire.Why it cannot simply be batched
Unnarrowed —
{ name: { $in: … } }— the result set is one row per organization per name, bounded only by the number of organizations, against a page capped at one row per name. It truncates, and a truncated page reads as "absent", which inserts. (That cap is a defect in its own right for the callers already using it: #11518.)Narrowed to the platform bucket —
{ name: { $in: … }, organization_id: null }— is bounded and cheap, and answers a different question. LetBbe the bucket occupant (at most one, by the declared key(COALESCE(organization_id, '__global__'), name)).BandXdiffer whenever an organization's row has a lowerid, and the two divergences both land on ruled ground:Bis ours,Xis an organization's row. Today: skip branch —skippedAuthored++,platformStampedInOrg++ifXcarries the platform stamp, then a bucket read sees our placeholder andcontinues. Under a bucket-scoped read: the update branch. TheUPDATEitself is now a no-op (the placeholder already holdshumanize(name), andbootstrapSystemCapabilitiesstill readssys_capabilityone name at a time — and it, not the declared seeder, is the loop whose size is the union of everysystemPermissionsstring #11451 gated the write on inequality) — so the whole observable difference is that [finding] The derived capability seeder rewrites a platform-marked row INSIDE an organization and still leaves the platform bucket unseeded — silently, on the UPDATE branch #8536 did not scope #8751's anomaly signal stops firing whenever our own bucket row also exists, which is precisely the case its doc says it is counted for: "counted even when the platform bucket IS properly occupied and nothing is missing".absentand inserts. That is the [Decision] When a curated capability name is already held in the platform bucket, should the seeder keep declining — or adopt, backfill, or refuse the collision at authoring time? #8552 ruling reversed by a read shape.Both are pinned:
bootstrap-system-capabilities.test.tsassertsplatformStampedInOrgat the fixture for (1), and the #8552 posture at the fixture for (2).The options, so they are not re-derived
buildExistingByName's UNSCOPED page cap truncates once a name can have more than one row — and a truncated page reads as "absent", which inserts #11518): read{ name: { $in: … } }with a generous cap; if the page comes back full, treat it as "could not answer" and degrade to per-item, reusing the machineryreadNamePagealready has for a driver without$in. Bounded, behaviour-preserving — a complete page holds every row for every name in the chunk, so the first row per name ISX. Costs new machinery in a file four callers now share, and overlapsbuildExistingByName's UNSCOPED page cap truncates once a name can have more than one row — and a truncated page reads as "absent", which inserts #11518.KNOWN_CAPABILITIES, and package-declared names are excluded viamaterializedCapabilityNames. It grows only for an app whosedefaultPermissionSetsgrant capability strings that nothing declares.Recommendation: 1, folded into #11518 rather than done twice, and only if #11518 is being repaired anyway. Option 3 is defensible on its own until an installation is measured paying for it — the growth axis is real but the stock cost is zero, and options 1 and 2 both spend a shared file's complexity or a ruling to buy it.
Not measured
The slope of this axis has never been measured. The hosted
bootstrap-curve.mjsrig lives inobjectstack-ai/cloudand its axes are permission sets / positions / objects. What is established in-repo is the round-trip count, pinned inbootstrap-seed-round-trips.test.ts— where the derived half's residue is stated as1 + derivedreads rather than hidden.Refs #11451 (parent) · #11518 (the page cap) · #8470 · #8751 · #8552 · #5876 · #10946 · #11096
Blocked-by: #11518
Generated by Claude Code