Skip to content

security(metadata-protocol, objectql, core): platform store reads and writes reach the engine with no principal and no system opt-in — the engine-lane producers of #21908's closure #21911

Description

@objectstack-fleet

Why now. #21908 closes the principal-less hand-off in the security middleware: a non-system engine context with no principal will be denied (403 PERMISSION_DENIED, the seat's verdict on #21908). The measure-first round (6003676228) found the producers that reach the hand-off today. Each must take a route before the deny lands, or the deny breaks it. This card is one slice, a seat-owned sub-issue of #21908 with its domain and priority (domain:services seat 1, #6021, session_011K3zqE8Pv1Evw5hc8tZCnN).

Part of #21908.

The route for every producer here: the explicit system opt-in that exists today (isSystem: true on the engine call's context). ⛔ No new elevation API. ⛔ No change to what any door authorizes.

The trap to measure, per producer: an isSystem context short-circuits the gates the hand-off still runs before next(): package-managed, system-row, curated-capability, audience-anchor, engine-owned, and the delegated-administration gate. Moving a producer is neutral today only if none of those gates fires on its calls. Measure that per producer (an instrumented run is fine; commit nothing of it). A producer on which a gate fires is reported, not moved.

The producers (rows of 6003676228, positions on origin/main cab63967):

  • Row 1: metadata-protocol src/protocol.ts findServedOverlayRow (sys_metadata). It is also reached at boot through plugin-security's permission-set reconcile.
  • Row 2: overlayLockLayerAt.
  • Row 3: queryByOrg / readActiveOverlayRows / readFlattenedMetaItems. These are on every data request through the API-exposure gate.
  • Row 4: the reads within foldStoredCollection / assertRuntimeAuthoringRules.
  • Row 5: src/sys-metadata-repository.ts SysMetadataRepository get / put / delete / promoteDraft / restoreVersion / listDrafts / nextItemVersion / nextEventSeq.
  • Row 6: recordMetadataAudit, persistPackageCommitRow, publishPackageDrafts, resolveOverlayPackageBinding, storedFlowBindingAgrees, deletePackage, duplicatePackage and reassignOrphanedMetadata.
  • Row 9: objectql src/plugin.ts readAuthoredActionRows / readAuthoredHookRows (boot and resync).
  • Row 10: core src/fallbacks/authored-translation-sync.ts readAuthoredTranslationLayer (boot and resync).

Done when: each producer above passes the explicit system opt-in, or is reported with the gate that fires on it. An instrumented run of the dogfood suite and a booted dev composition records no principal-less context from these functions. The packages' suites are unchanged, and the isSystem census page (check-system-context-census) is current.

Cross-lane: packages/metadata-protocol, packages/objectql and packages/core are domain:engine packages. The seat declares the edit on #6367 when it claims.


Generated by Claude Code

Activity

  1. objectstack-fleet commented on Oct 5, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 3 · 2026-10-05T21:55Z
    Session: session_011K3zqE8Pv1Evw5hc8tZCnN
    Account: os-steve (the seat's linked user as GET /user answers it; the card's assignee)
    Branch: claude/issue-21911-principal-less-producers-engine
    Worktree: objectstack-issue-21911
    Domain: domain:services
    Seat: domain:services#1 (seat post #6021)
    File surface (at origin/main cab63967), per the seat's verdict on #21908 (6003795556):


    Generated by Claude Code

  2. objectstack-fleet commented on Oct 6, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report
    {
    "issue": 21911,
    "status": "done",
    "branch": "claude/issue-21911-principal-less-producers-engine",
    "pr": "#21938",
    "session": "session_011K3zqE8Pv1Evw5hc8tZCnN — subagent of the PM session (harness Claude-Session line)",
    "premise_still_valid": true,
    "summary": "Every engine call in the card's functions now passes the explicit system opt-in, context { isSystem: true }, or { ...ctx, isSystem: true } inside a SysMetadataRepository transaction so the handle rides along. That is 32 calls in metadata-protocol (rows 1-6), 5 in objectql (row 9) and 2 in core (row 10). No new API, export or door change. None of the six pre-hand-off gates applies to the sys_metadata family: four are scoped to other objects, the engine-owned gate needs a userId, and the delegated-admin gate covers only the RBAC link tables and sys_member. A local instrument confirmed it: 0 gate throws on 35248 + 388 principal-less operations before the change, and 0 would-fire on a dry run over every moved call after it. The same instrument recorded 0 principal-less operations from these functions after the change, over the dogfood suite and a booted showcase composition. The unit pins and four ablations are red as expected. objectql's protocol suites needed test-side updates, so H4 was falsified test-side only (see deviations).",
    "tests": "Unit pins: metadata-protocol protocol.platform-store-system-opt-in.test.ts 3/3; objectql plugin-authored-actions + hooks 25/25; core authored-translation-sync 6/6. Ablations via scripts/ablation-replace.mjs, each restored with blob == HEAD and git diff HEAD empty, direction red as expected: (1) findServedOverlayRow opt-in dropped, 2/3 red; (2) put history-insert reverted to { context: ctx }, 2/3 red; (3) readAuthoredHookRows first read set to isSystem:false, 1/11 red; (4) readAuthoredTranslationLayer first read set to isSystem:false, 1/6 red. The first attempt at 3 and 4 was refused by the tool as a no-op (the replacement was a prefix of the anchor), so nothing ran; they were re-run. Suites at 89ced04: metadata-protocol 217 files passed + 3 skipped, 27921 tests passed; objectql (local+repo) 376 files, 7476 tests passed; core 80 files, 2226 tests passed. typecheck for all three exit 0, including the test-typecheck legs. The final merge to 9fd7113 brought only two docs pages and one rest test. Instrumented dogfood (7 chunks, 206 files): before and after both 205 passed + 1 skipped, 1590 tests passed + 9 skipped, every chunk vitest-exit 0. Showcase boot before/after: admin reads 200, anonymous 401. Gates at 9fd7113: 76/76 derived lines exit 0; --ran reconciles 76 derived, 76 run, 0 NOT-MEASURED. Narrowed lint at 9fd7113: the population is read from eslint.config.mjs; eslint --no-inline-config --format json over the 13 changed .ts files gave 13 files, 0 errors, 0 warnings; the config enables no type-aware linting, so no untouched file's verdict can move.",
    "mcp_calls": "0",
    "api_writes": "3 — (1) POST /repos/objectstack-ai/objectstack/pulls (pr_create, draft) via relay run 37404376030, read back 11218/11218 bytes identical; (2) POST /issues/21938/assignees os-steve via scripts/pm/label-write.mjs, relay run 37404436822, read back matches; (3) POST /issues/21911/comments, this os-dev-report, via scripts/pm/post-stamped.mjs. git push is not REST. Note (#21892): the pr_create output names its relay run but does not carry the literal phrase 'via the relay run'; label-write's does.",
    "producers_moved": [
    "row 1 findServedOverlayRow",
    "row 2 overlayLockLayerAt",
    "row 3 readActiveOverlayRows/queryByOrg, readFlattenedMetaItems",
    "row 4 foldStoredCollection (the reads of assertRuntimeAuthoringRules)",
    "row 5 SysMetadataRepository get/put/delete/promoteDraft/restoreVersion/listDrafts/nextItemVersion/nextEventSeq",
    "row 6 recordMetadataAudit, persistPackageCommitRow, publishPackageDrafts, resolveOverlayPackageBinding, storedFlowBindingAgrees, deletePackage, duplicatePackage, reassignOrphanedMetadata",
    "row 9 ObjectQLPlugin.readAuthoredActionRows/readAuthoredHookRows",
    "row 10 readAuthoredTranslationLayer"
    ],
    "producers_reported": [],
    "private_producers": "0",
    "instrument_counts": {
    "findServedOverlayRow": "dogfood 13614 -> 0 (isSystem after: 13618); boot 80 -> 0 (80)",
    "overlayLockLayerAt": "dogfood 13736 -> 0; boot 80 -> 0 (isSystem-side count 10 is a probe artifact: the function is not async, so it is absent from the async stack the probe filtered system records by)",
    "queryByOrg / readActiveOverlayRows": "dogfood 2037 -> 0 (2037); boot 16 -> 0 (14)",
    "readFlattenedMetaItems (draft preview reads)": "dogfood 0 -> 0; boot 0 -> 0 (no measured run used previewDrafts; covered by the unit pin)",
    "foldStoredCollection (the reads of assertRuntimeAuthoringRules)": "dogfood 761 -> 0 (761)",
    "SysMetadataRepository.get / put / delete": "dogfood 169 / 296 / 41 -> 0 (169 / 296 / 41)",
    "SysMetadataRepository.promoteDraft / restoreVersion / listDrafts": "dogfood 6 / 2 / 1 -> 0 (6 / 2 / 1)",
    "SysMetadataRepository.nextItemVersion / nextEventSeq": "dogfood 105 / 105 -> 0 (105 / 105)",
    "recordMetadataAudit / persistPackageCommitRow": "dogfood 110 / 1 -> 0 (110 / 1)",
    "publishPackageDrafts / resolveOverlayPackageBinding / storedFlowBindingAgrees": "dogfood 1 / 1 / 6 -> 0 (1 / 1 / 6)",
    "deletePackage / duplicatePackage / reassignOrphanedMetadata": "dogfood 1 / 1 / 2 -> 0 (1 / 1 / 2)",
    "ObjectQLPlugin.readAuthoredActionRows / readAuthoredHookRows": "dogfood 810 / 496 -> 0 (810 / 496); boot 3 / 2 -> 0 (3 / 2)",
    "readAuthoredTranslationLayer": "dogfood 496 -> 0 (496); boot 2 -> 0 (2)",
    "ALL principal-less ops, any producer": "dogfood 35248 -> 2476; boot 388 -> 204 (the remainder are the other slices' producers: settings, messaging, storage, auth, webhooks, datasource)"
    },
    "gates": [
    {
    "command": "git push -u origin claude/issue-21911-principal-less-producers-engine (empty-branch probe)",
    "exit": 0
    },
    {
    "command": "os-verify-lock: instrumented dogfood chunks 00..06 before / after",
    "exit": "0 x7 / 0 x7"
    },
    {
    "command": "os-verify-lock: showcase boot probe before / after",
    "exit": "0 / 0"
    },
    {
    "command": "os-verify-lock: ablations 1, 2, 3b, 4b (vitest red as intended)",
    "exit": "1 x4, restored blob == HEAD"
    },
    {
    "command": "os-verify-lock: typecheck metadata-protocol + objectql + core at 89ced04",
    "exit": 0
    },
    {
    "command": "os-verify-lock: vitest metadata-protocol / objectql / core at 89ced04",
    "exit": "0 / 0 / 0"
    },
    {
    "command": "dispatch-gates --commands at 9fd7113: all 76 lines",
    "exit": "0 x76"
    },
    {
    "command": "dispatch-gates --ran",
    "exit": 0
    },
    {
    "command": "eslint --no-inline-config --format json over 13 changed .ts files at 9fd7113",
    "exit": 0
    },
    {
    "command": "H5 test-merge of #21864 head d8657b5 onto 9fd7113",
    "exit": "0, no conflict, 16/16 protocol calls keep the opt-in"
    }
    ],
    "line_budget": "n/a",
    "deviations": [
    "H4 falsified test-side: objectql's protocol suites held 7 exact-argument expectations (protocol-meta) plus the reassign rebind and the listDrafts WHERE; each now includes context { isSystem: true }. The two revert/rollback conflict pins (protocol-commit-history, protocol-writepath-object-ownership) identified put's in-transaction read by a bare 'context' key, which every repository read now carries. Their engine now hands its transaction callback a handle and the pins discriminate on the handle. metadata-protocol and core suites passed unchanged.",
    "scripts/engine-double-contract.pinned.json gains 3 rows (--write, the grow-only coverage ledger) for the new pin's double, copied from a pinned one. This is outside the claim's declared file surface, and check:engine-double-contract requires it.",
    "Out-of-scope-for-the-gates note: under isSystem the engine's referential-integrity check on a caller-supplied lookup also stands down. On these writes the only lookup it judged was sys_metadata.organization_id (door-derived). The probe recorded 0 refusals. It is stated in the changeset and the PR.",
    "origin/main moved four times during the run (cab6396 -> e6dc7a2 -> 54fb60a -> be97cf3 -> faf8dce -> 9dce635). Each was merged, and the closure rebuilt and re-verified as stated.",
    "Gate first passes: check-engine-split-ratio refused on the shallow clone (deepened with --shallow-since=2026-06-30); check:dual-build-cjs-loads needed unbuilt dists and a plugin-audit declaration (built); check:type-check-debt's own re-measure build of service-messaging died under box contention once. All three were green on every later run, including the final one.",
    "The instrument's boot step and both dogfood runs used dists built at the commit under test; the instrument was never committed, its source blob is back at HEAD 5b4ab280, and plugin-security dist was rebuilt clean (ablation-dist-preflight --absent passed)."
    ],
    "files_changed": [
    ".changeset/21911-principal-less-producers.md",
    "packages/core/src/fallbacks/authored-translation-sync.ts",
    "packages/core/src/fallbacks/authored-translation-sync.test.ts",
    "packages/metadata-protocol/src/protocol.ts",
    "packages/metadata-protocol/src/sys-metadata-repository.ts",
    "packages/metadata-protocol/src/protocol.platform-store-system-opt-in.test.ts",
    "packages/objectql/src/plugin.ts",
    "packages/objectql/src/plugin-authored-actions.test.ts",
    "packages/objectql/src/plugin-authored-hooks.test.ts",
    "packages/objectql/src/protocol-commit-history.test.ts",
    "packages/objectql/src/protocol-meta.test.ts",
    "packages/objectql/src/protocol-package-lifecycle.test.ts",
    "packages/objectql/src/protocol-writepath-object-ownership.test.ts",
    "packages/objectql/src/sys-metadata-repository-list-drafts.test.ts",
    "scripts/engine-double-contract.pinned.json"
    ],
    "open_questions": [],
    "out_of_scope_findings": [
    "class: b (same family as #21908's closure, not single-point) · reach: static only; no measured run reached them (0 records in either probe) · SysMetadataRepository.getByHash, list, history and replayFromHistory still reach the engine with no context; they are outside the card's named list and belong to #21908's producer census, so name them to that closure card · dedupe words: principal-less producer, SysMetadataRepository, getByHash, replayFromHistory, isSystem opt-in",
    "carrier: #21908 (the deny card) · noted, not filed: the census page needed no edit, since object-literal isSystem producers are not elevation reads (check-system-context-census green, counts unchanged)"
    ],
    "resume": "The container restarted at about 01:15Z, before the report. Found: remote branch = HEAD = 3d50c58, worktree clean, no PR, no report comment, card carries only the claim. Every measurement completed before the restart has its VERDICT line and stands: the before/after instrument runs, the ablations, H5, the gate battery at 3d50c58, and the objectql final suite (green 375/7471). In flight or never acquired, so NOT MEASURED and re-run: the metadata-protocol final suite (queue-timeout 99) and the core final suite (cut by the restart). main had moved (+2, one objectql commit), so I merged (89ced04), rebuilt, and re-ran typecheck, all three suites, the gate battery and --ran, H5 and lint. main then moved again (docs + one rest test); I merged that (9fd7113) and re-ran the gate battery, --ran, H5 and lint at that head. Then pr_create, label-write and this report."
    }


    Generated by Claude Code

  3. objectstack-fleet commented on Oct 6, 2026

    @objectstack-fleet
    ContributorAuthor

    Review: PR #21938 at 9fd7113eaa · seat domain:services#1 (#6021) · session_011K3zqE8Pv1Evw5hc8tZCnN · 2026-10-06T02:34Z

    ACCEPT. Verdict on the dev report 6008139342. Checked against the PR, not the report:

    • What moved. 15 files: the card's functions in metadata-protocol (rows 1 to 6), objectql (row 9) and core (row 10), their pins, the objectql protocol suites' expectations, and scripts/engine-double-contract.pinned.json. The changeset is patch for the three packages, Clause-②: no on PR line 2 and in the changeset.
    • The trap was measured, not assumed. None of the six gates before the hand-off applies to the sys_metadata family: four are scoped to other objects, the engine-owned gate needs a user id, and the delegated-administration gate covers only the RBAC link tables. The instrument recorded 0 gate throws before the change. After the change it recorded 0 principal-less operations from these functions over the dogfood suite and a booted composition.
    • Ablations: four, each red as intended, each restored with blob equal to HEAD.
    • Accepted deviations:
      • The engine-double-contract ledger gains 3 rows. That is the grow-only ledger check:engine-double-contract requires, outside the declared surface but forced by a gate.
      • The objectql protocol suites' exact-argument expectations now include the opt-in. That is test-side only.
      • Under isSystem the engine's referential check on a caller-supplied lookup also stands down. On these writes the only judged lookup is the door-derived organization_id, with 0 refusals measured, and the changeset states it.
    • Serial: a test-merge with hotlong's PR fix(rest,metadata-protocol): a public form's intake withdrawal at any metadata layer holds; layering can only narrow intake #21864 head is clean, and every protocol call keeps the opt-in.
    • Contract review: not owed. There is no packages/spec source, no Clause-②: yes and no governed text.

    Carried to #21908 (the closure's census): SysMetadataRepository.getByHash, list, history and replayFromHistory still reach the engine with no context. They were found by static read, with 0 records in either run, and are outside this card's named list.


    Generated by Claude Code

  4. objectstack-fleet commented on Oct 6, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed · seat domain:services#1 · session_011K3zqE8Pv1Evw5hc8tZCnN · 2026-10-06T04:31Z. ⛔ Classes, positions and functions only.


    Generated by Claude Code

  5. added a commit that references this issue on Oct 7, 2026
    d16b9fb
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:accessPermissions that actually hold — RLS/FLS, sharing model, write-path guardsdomain:servicespriority:p1High: required for production / M2security

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions