Skip to content

bootstrapSystemCapabilities still reads sys_capability one name at a time — and it, not the declared seeder, is the loop whose size is the union of every systemPermissions string #11451

Description

@os-sam

Found while implementing #11096 + #11097 (batching the declared-capability seed and the env overlay reconciler). This is the seeder next door, and it was not in either card's scope.

The shape

packages/plugins/plugin-security/src/bootstrap-system-capabilities.ts:

  • the definition set is built in memory first — curated KNOWN_CAPABILITIES (line ~326), then every systemPermissions[] string across every seeded permission set that is not already curated or already materialized (lines ~332-338);
  • then one loop over that set (line ~347) issues a per-item existence read: const existing = await tryFind(ql, 'sys_capability', lookup, 1) (line ~356);
  • and the curated half's reconcile is an UPDATE (line ~497) that fires whether or not label/description differ from what is stored.

Same shape as the two loops #10946 measured at slope 4.0000 / R² = 1.000000, and the same shape #11096 has just removed from bootstrapDeclaredCapabilities.

Why this is worth its own card

⚠️The cost argument on #11096 actually describes THIS loop.#11096 said the capability count "is the union of every capability every package declares, which is typically larger than either axis #10946 measured" — but bootstrapDeclaredCapabilities iterates stack.capabilities, i.e. only the EXPLICIT defineCapability declarations. The union-of-systemPermissions set is the one built here, at lines 332-338. So the larger of the two capability loops is the one still unbatched.

Both run on every kernel:ready, back to back (security-plugin.ts ~3209 then ~3218).

Why it is not a mechanical copy of #11096

The two halves do not share a lookup key, which is exactly the exclusion that makes buildExistingByName non-reusable as-is here:

constlookup=isDerived
? {name: def.name}
: {name: def.name,managed_by: 'platform',organization_id: null};

buildExistingByName (seed-name-lookup.ts) answers "does a row exist for this NAME", optionally scoped to one organization. The curated half asks a narrower question — the platform's own organization-less row for this name — and that narrowing is load-bearing: #8470 put it there precisely because an unqualified find(..., limit: 1) has no ORDER BY and could reconcile an arbitrary organization's row, leaving the platform's own row unseeded.

So batching this needs a real design step, one of:

  1. two batched reads (one per half) with the curated half post-filtering on managed_by/organization_id in memory — cheap, but it reads rows the current query never returns;
  2. extending seed-name-lookup.ts to carry an extra equality predicate alongside the $in — a shared file that three seeders now depend on, so widening it is not a drive-by;
  3. leaving the derived half per-item and batching only the curated half, whose size is fixed by KNOWN_CAPABILITIES — which optimizes the half that does not grow.

Option 1 or 2 look right; the choice should be made deliberately, not inside another card's PR.

Not measured

The slope of this axis has not been measured. The hosted bootstrap-curve.mjs rig lives in objectstack-ai/cloud and its axes are permission sets / positions / objects. What is established here is the code shape, by reading, plus the round-trip counts #11096 pinned for the sibling seeder in bootstrap-seed-round-trips.test.ts — the natural home for a pin on this one too.

Refs #11096 · #10946 (the precedent and the buildExistingByName machinery) · #8470 (why the curated lookup is narrowed) · objectstack-ai/cloud#1555 (the 20s request budget)

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions