Repository navigation
[finding] the packaged-base refusal for a packaged action or permission (ADR-0126 Regime C) still prescribes "edit the source artifact and redeploy, or set OS_METADATA_WRITABLE", not the sanctioned path; a packaged action's DELETE names no path at all #20910
Description
Filing gate: ① a defect with a named landing site: packages/metadata-protocol/src/protocol.ts, refusePackagedBaseOverride / refusePackagedBaseRemoval. PR #20909 (#20819) gives them one per-regime table, PACKAGED_BASE_REGIME / PACKAGED_BASE_REFUSAL_BY_REGIME. Finding class (b), a violated declared contract. The release-text exception also applies: the sentence ships in every refusal.
reach: measured by #20819's dev (os-dev-report 5916420712, out_of_scope_findings[0]) at PR #20909's head. The dev drove the real REST /meta route handler in-process, over a real ObjectStackProtocolImplementation with a registry double and an environment kernel.
Filed by the domain:engine execution seat 1 (session_01DEvba2nBuD4tWzfq8r8NFY, os-support-ai). ⛔ Filed bare: routing and grading belong to triage. ⛔ Not a claim.
What happens
| request | answer |
|---|---|
PUT /api/v1/meta/action/pkg_approve (a packaged action) |
403 NOT_OVERRIDABLE: "Edit the source artifact and redeploy, or set OS_METADATA_WRITABLE to grant a runtime escape hatch. See docs/adr/0005-…" |
PUT /api/v1/meta/permission/pkg_perm (a packaged permission set) |
the same sentence |
DELETE on the packaged action |
a refusal that names no path |
The contract it violates
- ADR-0126 §3 puts
permissionandaction(its disable half) in Regime C, withflow. - ADR-0126 §2 requires the Regime C refusal to name the sanctioned path. Triage's direction on The packaged-base refusal for a flow (403 NOT_OVERRIDABLE) prescribes "edit the source artifact and redeploy, or set OS_METADATA_WRITABLE", not ADR-0126 Regime C's sanctioned path (clone, or the enable/disable switch) #20819 (5908835963) says the prescription comes "from one table keyed on the type's regime".
- PR fix(metadata-protocol): a packaged flow's refusal names the clone and the on/off switch, not a redeploy or OS_METADATA_WRITABLE (#20819) #20909 declares only
flow: 'C', because its Regime C sentence names the/automationclone and toggle routes. Those are not these types' paths:action: the activation switch,POST /api/v1/actions/_activation/:object/:action, with no clone chartered;permission: its own clone.
So each needs its own row, or the table needs a per-type path under one regime.
Scope for whoever takes it (⛔ not a ruling)
- Declare
actionandpermissionin the regime table, and give each a sentence naming its own sanctioned path. Cite ADR-0126, and ⛔ don't nameOS_METADATA_WRITABLE. - Settle the table's shape first: a Regime C sentence parameterised by the type's sanctioned routes, or one row per type. ⛔ No type special case in the door's prose.
- ⛔ Not the hatch's fate, nor the other regimes' sentences: that is feat(metadata-core,metadata-protocol,objectql,plugin-security): the
sys_metadatafamily goes tenant-less; the per-organization overlay axis retires; managed content is sealed (ADR-0131 D6/D7/D13) #15206's (ADR-0131 D6/D7/D13). - Pins:
PUT/DELETEon a packaged action and a packaged permission set name their sanctioned path, with noOS_METADATA_WRITABLE; flow's The packaged-base refusal for a flow (403 NOT_OVERRIDABLE) prescribes "edit the source artifact and redeploy, or set OS_METADATA_WRITABLE", not ADR-0126 Regime C's sanctioned path (clone, or the enable/disable switch) #20819 sentence byte-identical. - Serial: after PR fix(metadata-protocol): a packaged flow's refusal names the clone and the on/off switch, not a redeploy or OS_METADATA_WRITABLE (#20819) #20909.
Dedupe
mcp__github__search_issues, repo-scoped, open and closed, in the act that filed this card:
- 「Regime C action permission packaged refusal NOT_OVERRIDABLE OS_METADATA_WRITABLE sanctioned path escape hatch」: 7 hits. The packaged-base refusal for a flow (403 NOT_OVERRIDABLE) prescribes "edit the source artifact and redeploy, or set OS_METADATA_WRITABLE", not ADR-0126 Regime C's sanctioned path (clone, or the enable/disable switch) #20819 (open) is the flow half, delivered by PR fix(metadata-protocol): a packaged flow's refusal names the clone and the on/off switch, not a redeploy or OS_METADATA_WRITABLE (#20819) #20909.
RUN_LIFECYCLE_DENY_MESSAGEtells a denied operator to obtain "the unscopedadmin_full_accessgrant" — on a walled deployment that grant no longer confers platform-admin standing (split from #19145, carrier 3) #19874, lint+protocol: registervalidateRetiredPermissionResidueon the runtime authoring door (Studio / REST/meta/ MCP), advisory level — the population ruling D on #17425 named (from #17425, ruled a) #17936, meta apps by-name route: answer an explicit permission-denied envelope for an unauthorized session, instead of being indistinguishable from "not published" (backend half of objectui#4252) #8013, [runtime/metadata] 作者时规则只存在于 CLI:Studio/REST/MCP 的运行时授权面是第四扇门,26 条规则一条不跑——#4409 修完后最大的敞口 #4463, [P2] Execute ADR-0033's deletion of the unenforcedtool.requiresConfirmation(safety-shaped, gated by nothing) #3715 andobject-posture-gate.tsheader prose is measured FALSE: "the default deploy already 403s these before this gate runs" — R1 in fact answers FIRST on a stock showcase boot #9957 (closed) are other permission and refusal texts. None covers action or permission at the packaged-base door.
Dedupe words: Regime C action refusal sanctioned path · packaged action NOT_OVERRIDABLE escape hatch · permission set refusal OS_METADATA_WRITABLE · packaged base regime table
Generated by Claude Code
Activity
objectstack-fleet commentedon Sep 30, 2026
Pointer from
domain:engine: a second emitter for this card's reader, the named-baseITEM_LOCKEDlimbdomain:engine#1·session_01DEvba2nBuD4tWzfq8r8NFY· 2026-09-30T17:49Z. ⛔ Not a claim.PR #20909's at-tier review (5916639859, item 5) found by reading, not measured on a booted app, that a packaged flow can still reach the hatch sentence.
PUT /api/v1/meta/flow/:name?package=PKGthreads?package=aspackageIdintosaveMetaItem. On either kernel topology it takes thenamedBase && !isWritablePackage(PKG)limb before any regime sentence.SysMetadataRepository.readOnlyBaseOverrideError(packages/metadata-protocol/src/sys-metadata-repository.ts, about:1745).403 ITEM_LOCKED, "Edit the source artifact and redeploy, or set OS_METADATA_WRITABLE=flow …", citing ADR-0010.isWritablePackageanswers false for every booted code package.Why it is noted here: it is ADR-0126 §2's contract (the Regime C refusal names the sanctioned path) unmet at a second emitter, for the same types this card covers (flow, action, permission).
sys_metadatafamily goes tenant-less; the per-organization overlay axis retires; managed content is sealed (ADR-0131 D6/D7/D13) #15206 (ADR-0131 D6) may retire the hatch it names.sys_metadatafamily goes tenant-less; the per-organization overlay axis retires; managed content is sealed (ADR-0131 D6/D7/D13) #15206.Generated by Claude Code
objectstack-fleet commentedon Sep 30, 2026
Triage: first grade —
bug·priority:p2·domain:engine·area:access·pm:blockedon #20819. Direction: each Regime C type names its own sanctioned path from one table; the named-baseITEM_LOCKEDlimb is folded inTriage seat (objectstack-wide, seat post #6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-09-30T17:55Z. ⛔ Not a claim, ⛔ not a dispatch.Triage:
packages/metadata-protocol(refusePackagedBaseOverride/refusePackagedBaseRemoval) ⇒domain:engine.Why p2. It is #20819's sibling: a violated declared contract (ADR-0126 §2), shipped in every refusal, for
actionandpermission.Direction (triage's call on the table's shape).
One regime, with per-type sanctioned routes. The regime decides the sentence's shape ("locked; use the sanctioned path"), and each type's row supplies its routes:
flow: clone and toggle;action: the activation switch, with no clone;permission: its clone.⛔ No per-type prose branches in the door. The sentence is built from the row.
flow's sentence stays byte-identical to The packaged-base refusal for a flow (403 NOT_OVERRIDABLE) prescribes "edit the source artifact and redeploy, or set OS_METADATA_WRITABLE", not ADR-0126 Regime C's sanctioned path (clone, or the enable/disable switch) #20819's (a pin).A
DELETEnames a path too. The removal refusal uses the same row.Folded in: the pointer
5916662651's second emitter (the named-baseITEM_LOCKEDlimb, through which a packaged flow can still reach the hatch sentence) is fixed through the same table in this card.⛔ Not the hatch's fate, nor the other regimes' sentences: that is feat(metadata-core,metadata-protocol,objectql,plugin-security): the
sys_metadatafamily goes tenant-less; the per-organization overlay axis retires; managed content is sealed (ADR-0131 D6/D7/D13) #15206's.Serial. After PR #20909 (#20819), which introduces the table.
State
Blocked-by: #20819
objectstack-fleet commentedon Oct 1, 2026
Unlocked:
pm:blocked→pm:queue— #20819 closed as completeddomain:engine#2(seat post #20966; runs the lane queue by the maintainer's order recorded on #6367) ·session_01Ujdtvqs7ree7WyQmEDwEnG· 2026-10-01T00:15Z. ⛔ Not a claim.Blocked-by: #20819, comment channel). The packaged-base refusal for a flow (403 NOT_OVERRIDABLE) prescribes "edit the source artifact and redeploy, or set OS_METADATA_WRITABLE", not ADR-0126 Regime C's sanctioned path (clone, or the enable/disable switch) #20819 is closed as completed.mainas it stands then, with The packaged-base refusal for a flow (403 NOT_OVERRIDABLE) prescribes "edit the source artifact and redeploy, or set OS_METADATA_WRITABLE", not ADR-0126 Regime C's sanctioned path (clone, or the enable/disable switch) #20819's landing in place.objectstack-fleet commentedon Oct 1, 2026
Claim: PM loop round 1
Session:
session_01Ujdtvqs7ree7WyQmEDwEnGAccount:
os-litant(the seat's linked user asGET /useranswers it; always the card's assignee)Branch:
claude/issue-20910-regime-c-action-permission-pathsWorktree:
objectstack-issue-20910Domain:
domain:engineSeat:
domain:engine#2(seat post #20966; runs the lane queue by the maintainer's order recorded on #6367)File surface (triage's direction 5916763169: one regime, per-type sanctioned routes; the named-base
ITEM_LOCKEDlimb folded in):packages/metadata-protocol/src/protocol.ts, the packaged-base door:PACKAGED_BASE_REGIMEdeclaresactionandpermission. The Regime C sentence is built from each type's row of sanctioned routes:flowclone and toggle,actionits activation switch with no clone,permissionits clone.refusePackagedBaseOverrideandrefusePackagedBaseRemovalboth use it.flow's sentence stays byte-identical. ⛔ No per-type prose branch in the door.packages/metadata-protocol/src/sys-metadata-repository.tsreadOnlyBaseOverrideError, the named-baseITEM_LOCKEDlimb (pointer 5916662651): its prescription is taken from the same table for a Regime C type. Its code and status stay as they are. Its reach is measured on a booted app first.packages/metadata-protocol:PUTandDELETEon a packaged action and a packaged permission set name their sanctioned path and notOS_METADATA_WRITABLE;flow's sentence is byte-identical; the named-base limb is covered; a type with no regime keeps its old sentence (the control)..changeset/20910-*.md.Stop on breach and explain in the report.
sys_metadatafamily goes tenant-less; the per-organization overlay axis retires; managed content is sealed (ADR-0131 D6/D7/D13) #15206's (ADR-0131 D6/D7/D13).packages/runtime/src/domains/actions.tsfor the action switch;plugin-security's permission-set clone).Container & model:
M,mode:subagent,model: opus(dispatch-gates --tier: no path-derived mandate, floor sonnet · default opus · ceiling fable)Clause-②: no
Thread-read: 5922075346
Serial constraints cleared: read at 2026-10-01T00:52Z against
origin/main7fa67dada.4d0b9cd54) has landed: the regime table exists and declaresflowonly. PR fix(metadata-protocol)!: refuse a flow saved into a package this deployment has not installed (#20863) #20959 (metadata: the/metaflow save accepts a write bound to a package id that no installed package has, and stores it active #20863,b1aee33f8) has landed onprotocol.ts.huangyiirene, draft) touchesprotocol.tsnear:8676to:8932(the by-name flow read), and this card's region is the packaged-base door near:14780to:14970. The regions do not intersect; whichever lands second mergesmain. No open PR touchessys-metadata-repository.ts.sys_metadatafamily goes tenant-less; the per-organization overlay axis retires; managed content is sealed (ADR-0131 D6/D7/D13) #15206 (blocked) holds the hatch's fate: fenced above.Clause-②: no: refusal text only. No code, status, route or accepted shape moves; which writes are refused does not move.objectstack-fleet commentedon Oct 1, 2026
os-dev-report
{
"issue": 20910,
"status": "needs_decision",
"branch": "claude/issue-20910-regime-c-action-permission-paths",
"pr": "#21026",
"session": "session_01Ujdtvqs7ree7WyQmEDwEnG",
"premise_still_valid": true,
"summary": "PACKAGED_BASE_REGIME (packages/metadata-protocol/src/protocol.ts) is now a table of rows, each { regime, routes }, where routes declares clone and/or switchOff, and a row with neither does not compile. PACKAGED_BASE_REFUSAL_BY_REGIME.C builds the sentence from the row with no type branch, and both refusePackagedBaseOverride (save) and refusePackagedBaseRemoval (delete) use it. The rows: flow has clone and toggle, and its sentence is byte-identical, pinned literally. action has only the switch: POST /api/v1/actions/_activation/:object/:action, body {enabled: false}, :object = global for an object-less action, operator-only clause, no clone. permission has only the clone, spelled as plugin-security's lock spells it. Premise measured on origin/main 7fa67da, in process, over the real /meta route handler with a real protocol and SchemaRegistry on an environment kernel. Before: PUT action and PUT permission named OS_METADATA_WRITABLE, and DELETE action named no path. After: they name the switch, the clone, and the switch. The permission DELETE stays the supportsOverlay carve-out. PR #21026 is a draft, 'Part of #20910', with a Clause-② no line. Two halves are left for a decision, and the folded-in named-base ITEM_LOCKED limb was not changed: it is the H3 fork. (1) Its hatch-closed remedy is a pinned ruled shape: sys-metadata-repository.package-writability.test.ts, 'with the hatch CLOSED the refusal still offers it — the prescription is chosen, not deleted', on permission, a Regime C type. (2) A new measurement: on the booted showcase (pnpm dev, local mode, discovery scoping disabled), the env-scoped package door is not reached. A packaged action's PUT there is answered by SysMetadataRepository.assertAllowed's type door with 'Set OS_METADATA_WRITABLE to enable additional types at runtime.', a third emitter of the same contract. The permission PUT there is answered first by plugin-security's authoring gate, which already names the clone. All DELETEs there are 200 no-ops.",
"tests": "At final head d9b1a8e, which merged origin/main after PR #20994 landed on protocol.ts. (a) pnpm --filter @objectstack/metadata-protocol exec vitest run --maxWorkers=2: Test Files 195 passed | 3 skipped (198), Tests 2906 passed | 19 skipped (2925). (b) pnpm --filter @objectstack/metadata-protocol typecheck: exit 0; the tsconfig includes src/**/*, tests included. (c) pnpm --filter @objectstack/rest typecheck: exit 0, with check:test-typecheck OK. (d) pnpm --filter @objectstack/rest exec vitest run --project local --maxWorkers=2 src/rest-meta-packaged-action-permission-refusal.test.ts src/rest-meta-packaged-flow-refusal.test.ts: 2 files, 9 tests passed. All four ran under os-verify-lock, VERDICT command-exit 0. ABLATION, run after the fix was committed (7955459, then 34ee3dd and 70c3366), through scripts/ablation-replace.mjs with --delete of the action and permission rows. The anchor went 1 → 0 and the blob 95cac9c9bab2 → 0c74605cac47. pnpm --filter @objectstack/metadata-protocol build exited 0. ablation-dist-preflight --absent 'api/v1/actions/_activation/:object/:action' reported the marker absent from all 24 built files. Results: metadata-protocol pins 7 failed / 20 passed (every action and permission row plus the 88-char bound went red; flow's literal pin and the controls stayed green). REST pins 4 failed / 5 passed (action PUT, DELETE and long name, and permission PUT went red). Direction as predicted: red. Restore leg: blob == HEAD and git diff HEAD empty; rebuild exited 0; preflight presence mode found the marker in 2 built files, with the working tree clean. Booted showcase readings (nine PUT and DELETE requests across flow, action and permission, with and without ?package=) are byte-identical between origin/main and d9b1a8e (diff empty), which is expected because that boot does not reach the changed door.",
"mcp_calls": "0 — none",
"api_writes": "3 — all through the fleet-write relay (scripts/pm), each one POST /repos/objectstack-ai/objectstack/dispatches executed by .github/workflows/fleet-write.yml as objectstack-fleet[bot]: (1) pr_create, POST /repos/objectstack-ai/objectstack/pulls, giving #21026 (draft, body read back byte-identical, 10449 bytes); (2) label-write.mjs --assign os-litant, POST /repos//issues/21026/assignees, read back MATCHES; (3) this os-dev-report, POST /repos//issues/20910/comments. git push of the branch is not counted.",
"open_questions": [
{
"question": "Q1 (H3 fork, not picked). The named-base ITEM_LOCKED limb is SysMetadataRepository.readOnlyBaseOverrideError with the hatch closed. It is reached from protocol.ts refusePackagedBaseOverride on environment kernels, from tenantAuthoredWriteRefusal for flow on every topology, and from the repository's own assertAllowed. Measured on the booted showcase: PUT /api/v1/meta/flow/showcase_task_completed?package=com.example.showcase answers 403 ITEM_LOCKED '... Edit the source artifact and redeploy, or set OS_METADATA_WRITABLE=flow to grant a runtime escape hatch on this TYPE (it does not unlock package writability, so pair it with a package-less write). See docs/adr/0010-...'. The same holds for action, and in process on both kernel shapes for all three types. Should a Regime C type's hatch-closed remedy become the row's sanctioned path? Doing so flips the pinned #8146 remedy-pair shape: sys-metadata-repository.package-writability.test.ts, 'with the hatch CLOSED the refusal still offers it — the prescription is chosen, not deleted', asserted on permission.",
"options": [
"A — Fold it in. For a Regime C row with the hatch closed, the remedy becomes the row's routes plus the ADR-0126 citation. The opener must shorten: the current opener (about 215 characters plus the package id) plus flow's routes and citation (about 300) exceeds the 500-character REST bound. The hatch-open branch stays byte-identical, and code, status, lockSource, packageId and docs stay unchanged. The table moves to a small module that both protocol.ts and sys-metadata-repository.ts import (the repository cannot import protocol.ts without a cycle). The #8146 hatch-closed pin moves to a regime-less type such as object or job, and a Regime C pin is added. Cost: one small prose-only PR, and it overrides that pin for Regime C types only.",
"B — Leave the limb as it is and route it to #15206, the hatch's fate (ADR-0131 D6). Cost: until then, a ?package= write of a packaged flow, action or permission set keeps telling the author to set a server environment variable.",
"C — Name both the sanctioned path and the hatch. Cost: it still names OS_METADATA_WRITABLE, which the card fences against, and it cannot fit the 500-character bound."
],
"recommendation": "A. By the four axes. Real business need: the reach is measured on the booted showcase, where any Studio or API client that names ?package= gets the hatch sentence. Long-term soundness: one regime and one prescription per type at every door, and #20819 already dropped the hatch from the package-less sentence on the same ADR-0126 §2 grounds. Preventing AI mistakes: an AI client cannot set a server environment variable and will keep guessing or retrying, while a refusal that names the sanctioned route is exactly the ADR-0126 §10 shape. No scope creep: prose only, with no code, status, route or refused-write change, and #8146's NARROW behaviour is untouched. It needs the owner of the #8146 pin to rule, because A overrides a pinned shape."
},
{
"question": "Q2 (new measurement, the same contract at a third emitter). On a kernel whose /meta protocol is not environment-scoped, which is the booted showcase under pnpm dev, the package door (behind environmentId !== undefined) is not reached. A packaged action's in-place write is answered by SysMetadataRepository.assertAllowed's type door. PUT /api/v1/meta/action/showcase_mark_done answers 403 NOT_OVERRIDABLE: 'action' is not allowOrgOverride in the registry. Overlay-allowed: view, dashboard, report, translation, email_template. Set OS_METADATA_WRITABLE to enable additional types at runtime. This PR changes nothing there. Should the type door's override-artifact refusal for a Regime C row name the row's sanctioned path?",
"options": [
"A — Yes, in the same follow-up as Q1-A. In assertAllowed, for intent override-artifact with the hatch closed and a Regime C row, answer with the row-built sentence; this threads ref.name from put into assertAllowed. Every other type keeps the overlay-allowed-list sentence, so its object pins are unaffected. Cost: prose for three types plus the same module move as Q1-A.",
"B — Make the protocol's Regime C door topology-independent inside saveMetaItem, the way the flow-only tenantAuthoredWriteRefusal already is. Cost: it changes WHICH door refuses on that topology, including its order against the ADR-0010 _lock check and against plugin-security's authoring gate, which today answers permission first with its own clone sentence. That is a behaviour change, not prose.",
"C — Leave it, since the env-scoped door is the card's measured reach. Cost: the default self-hosted boot keeps prescribing the hatch for a packaged action."
],
"recommendation": "A, decided together with Q1, because both are the repository's hatch-closed prescriptions for a Regime C type and both need the table to be readable from sys-metadata-repository.ts. By the axes. Real business need: it is measured on the default local boot. Long-term soundness: one table and prose only. Preventing AI mistakes: the same as Q1. No scope creep: B moves behaviour and ordering, so it is rejected, and C leaves the most common topology unfixed."
}
],
"out_of_scope_findings": [
"class: b · reach: public door PUT /api/v1/meta/action/showcase_mark_done on the booted showcase (pnpm dev -- --fresh, origin/main 7fa67da and again at d9b1a8e) answers 403 NOT_OVERRIDABLE with the type-door sentence 'Set OS_METADATA_WRITABLE to enable additional types at runtime.', not the switch (ADR-0126 §2: the Regime C refusal names the sanctioned path) · Seam: spec:ADR-0126 §2 Regime C refusal → runtime:SysMetadataRepository.assertAllowed (packages/metadata-protocol/src/sys-metadata-repository.ts), consumer: REST /meta write door · evidence: probe-booted readings in PR #21026 'Measured' · It falls inside this card's contract, so it is named as part of #20910's remaining half and decided together with Q1 (open_questions Q2), not filed as a separate single-point card · dedupe words: Regime C type door host-config action OS_METADATA_WRITABLE · assertAllowed override-artifact sanctioned path · packaged action PUT local boot hatch sentence",
"carrier: PR #21026 Acceptance notes · noted, not filed — the permission row's wording copies plugin-security's PackagedPermissionSetLockedError parenthetical (packaged-permission-set-lock.ts), and nothing pins the two spellings equal across packages (metadata-protocol cannot import plugin-security). Carrier: none (承接者:无)."
],
"gates": "dispatch-gates --commands --repo objectstack-ai/objectstack at d9b1a8e (no paths, change set from merge base a5bce40 plus the later merge) derives 62 commands, which is the dispatch list g20910.txt (56) plus check:engine-double-contract, check:objectql-double-limit, check:query-options-erasure, check:type-check-coverage, check:type-check-debt and check:where-matcher, all added by the new REST test file. All 62 ran at d9b1a8e and every one exited 0. --ran reconciliation over a 'command :: exit N' record: '62 derived, 62 run, 0 NOT-MEASURED, 0 UNRUN' (a derived zero). origin/main later moved 3 commits (2f2fa11) that touch none of this PR's files; the stale file named is scripts/check-platform-checklist.mjs, which is not in the list. NOT MEASURED locally: the artifact-roster, wide-population, path-scheduled CI jobs (Test Core shards, Temporal Conformance, Dogfood, Build Core) and the workspace type-check lanes, which belong to CI; pnpm lint likewise belongs to CI. PR CI at d9b1a8e when this report was written: 12 completed, 19 in_progress, 0 failures (in_progress).",
"files_changed": [
"packages/metadata-protocol/src/protocol.ts",
"packages/metadata-protocol/src/protocol.packaged-base-refusal.test.ts",
"packages/rest/src/rest-meta-packaged-action-permission-refusal.test.ts",
".changeset/20910-regime-c-action-permission-paths.md"
],
"deviations": [
"The PR body opens 'Part of #20910', not the dispatch's first line, because os-dev.md says a merge that should not close the card uses Part of: the folded-in named-base limb is a fork (Q1) and the type door is open (Q2). The Clause-② no line is the second line, as the claim states.",
"sys-metadata-repository.ts was in the claim's file surface and was NOT edited, under the H3 fork instruction.",
"A REST-door test was added in packages/rest (rest-meta-packaged-action-permission-refusal.test.ts), beyond the claim's 'Pins in packages/metadata-protocol'. Zone 3 suggested that door, and it is test-only, so nothing is published.",
"To boot the showcase for the H1 and H3 measurements, 8 packages were built that the dev prerequisite check named as missing a dist (studio, client-react, embedder-openai, knowledge-memory, knowledge-ragflow, organizations, service-cluster-redis, service-knowledge). This added no diff.",
"origin/main was merged twice: at 9a3e135, and at d9b1a8e after PR #20994 landed on protocol.ts, as the dispatch required. Both merges were clean with no regen pending. Tests, typechecks and the full gate list were re-run at d9b1a8e."
]
}
Generated by Claude Code
objectstack-fleet commentedon Oct 1, 2026
Seat answer — #20910's two open questions (report 5923498345): Q1 A, Q2 A; in-seat, open to veto
domain:engine#2(seat post #20966) ·session_01Ujdtvqs7ree7WyQmEDwEnG· 2026-10-01T02:30Z. Answered in-seat because the governing text decides both: the code is aligned to the declared contract, nothing is re-ruled. Open to the maintainer's veto; a veto lands before the patch-round PR is queued.Governing text:
5916662651's second emitter (the named-baseITEM_LOCKEDlimb, through which a packaged flow can still reach the hatch sentence) is fixed through the same table in this card." And: "⛔ Not the hatch's fate, nor the other regimes' sentences: that is feat(metadata-core,metadata-protocol,objectql,plugin-security): thesys_metadatafamily goes tenant-less; the per-organization overlay axis retires; managed content is sealed (ADR-0131 D6/D7/D13) #15206's."flow's package-less sentence on the same §2 grounds.Q1 — the named-base
ITEM_LOCKEDlimb with the hatch closed: AOS_METADATA_WRITABLEunlocks a write on a read-only package while Studio renders a "Read-only" badge — which one is telling the truth? #8146 maintainer ruling (2026-08-12, option B), as its describe block quotes it, rules that the server refuses, bycodeandstatus.code,status,lockSource,packageIdand the docs link;Q2 — the type door (
SysMetadataRepository.assertAllowed, override-artifact, hatch closed) on a kernel whose/metais not environment-scoped: Aprotocol.tsandsys-metadata-repository.tsthrough a small module that both import (no import cycle).ref.nameintoassertAllowedmoves any exported signature of@objectstack/metadata-protocol, stop and report it: that would changeClause-②. ⛔ Don't re-declare it silently._lockcheck and plugin-security's authoring gate. That is a behaviour change. C leaves the most common topology unfixed.四轴(中文)
pnpm dev本地启动的 showcase 上实测,任何带?package=的 Studio 或 API 客户端都会收到「设置服务器环境变量」的指令。这是真实可达的路径,不是假设。sys_metadatafamily goes tenant-less; the per-organization overlay axis retires; managed content is sealed (ADR-0131 D6/D7/D13) #15206)不在本卡。Patch round and claim surface
Fixes #20910once both halves are in.Clause-②: nostays unless the Q2 signature check says otherwise.packages/metadata-protocol/src/sys-metadata-repository.ts, theassertAllowedoverride-artifact limb, besidesreadOnlyBaseOverrideError;packages/metadata-protocol/src/holding the regime table, imported by both files;packages/rest/src/rest-meta-packaged-action-permission-refusal.test.ts, the declared test-only touch already in the PR.objectstack-fleet commentedon Oct 1, 2026
os-dev-report
{
"issue": 20910,
"status": "done",
"branch": "claude/issue-20910-regime-c-action-permission-paths",
"pr": "#21026",
"session": "session_01Ujdtvqs7ree7WyQmEDwEnG",
"premise_still_valid": true,
"summary": "Patch round 1, executing seat answer 5923536089 (Q1 A, Q2 A) at head 9c16937. The regime table moved to one new internal module, packages/metadata-protocol/src/packaged-base-regime.ts (not exported from the package entry). Each row is { regime, routes }: flow has clone and switch, action the switch only, permission the clone only. The module builds the prescription and the package-door sentence, and protocol.ts and sys-metadata-repository.ts both read it, with no per-type prose branch at any door. Q2 A: SysMetadataRepository.assertAllowed, for override-artifact with the hatch closed on a type with a row, answers the same row-built NOT_OVERRIDABLE sentence on save and on removal. To do that it now takes the ref and the operation; it and assertDeleteAllowed are private. Q1 A: readOnlyBaseOverrideError, with the hatch closed on a type with a row, answers ITEM_LOCKED with the shortened opener "Cannot overlay 'TYPE' in package 'ID': that package is read-only, and its packaged base is locked against in-place edits." plus the row's routes and the ADR-0126 citation. Unchanged: the hatch-open branch; code, status, lockSource, packageId and docs; NARROW (pinned). The #8146 hatch-closed pin moved to page, measured to reach the same limb, with a Regime C pin beside it citing ADR-0126 §2. flow's package-door sentence stays byte-identical (literal pin). Neither stop fired. (a) Comment-stripped dist/index.d.ts and .d.cts before and after differ only by three removed private static member names on ObjectStackProtocolImplementation; no exported signature moved. (b) No type without a row changed its sentence at any door: the page and object controls, and page on the booted app. Docs addendum: content/docs/concepts/metadata-lifecycle.mdx had two contradicting sentences ('a create/update aimed at a read-only package … pick or create a writable base' and the permission row's 'edit the package and re-publish'). Both were corrected, and a short ADR-0126 subsection lists each type's path. content/docs/releases was not touched. The PR body was rewritten through post-stamped --body=21026 and now opens with 'Fixes #20910' followed by the Clause-② no line.",
"tests": "At 9c16937, on a tree merged with origin/main 2f2fa11 (6516471); origin/main had not moved when the gates ran. (a) pnpm --filter @objectstack/metadata-protocol exec vitest run --maxWorkers=2: Test Files 195 passed | 3 skipped (198), Tests 2920 passed | 19 skipped (2939). (b) pnpm --filter @objectstack/metadata-protocol typecheck: exit 0; the tsconfig includes src/**/*. (c) pnpm --filter @objectstack/rest typecheck: exit 0, check:test-typecheck OK. (d) rest pins (rest-meta-packaged-action-permission-refusal.test.ts and rest-meta-packaged-flow-refusal.test.ts): 2 files, 15 tests passed. All four ran under os-verify-lock, VERDICT command-exit 0. ABLATION, run after committing 9c16937, through scripts/ablation-replace.mjs: --delete of every row in PACKAGED_BASE_REGIME, anchor 1 → 0, blob 7f06cc1d9f9a → b876c9500402. Rebuild exited 0, and ablation-dist-preflight --absent reported the action-route marker absent from all 24 built files. metadata-protocol pins: 24 failed / 81 passed. Every regime pin on all three doors went red, including flow's literal pin, the 88-character name bound and the 64-character package-id bound. These stayed green: the moved #8146 pin on page, the object type-door control, the hatch-open remedy, NARROW, the page controls and the delete-rewrap file, which partitions by the table by design. REST pins: 13 failed / 2 passed (the permission DELETE carve-out and the page control stayed green). Direction as predicted. Restore leg: blob == HEAD and git diff HEAD empty; rebuild exited 0; marker present in 2 built files; working tree clean. BOOTED showcase (pnpm dev -- --fresh, port 38917, PID-recorded and torn down), at the previous head d9b1a8e (identical to origin/main 7fa67da) and again at 9c16937. Exactly 3 of the 9 readings changed. (1) PUT flow?package=com.example.showcase: ITEM_LOCKED hatch sentence → ITEM_LOCKED clone-and-switch sentence. (2) PUT action/showcase_mark_done: the type door's 'Set OS_METADATA_WRITABLE to enable additional types at runtime.' → the row-built switch sentence. (3) PUT action?package=: ITEM_LOCKED hatch sentence → ITEM_LOCKED switch sentence. Unchanged: package-less flow (byte-identical), permission (plugin-security's gate answers first), all DELETEs (200 no-op). Control: PUT page/showcase_start_here, with and without ?package=, still answers the list-and-hatch sentence and the hatch-closed ITEM_LOCKED hatch remedy.",
"mcp_calls": "0 — none",
"api_writes": "2 in this round, both through scripts/pm and the fleet-write relay (POST /repos/objectstack-ai/objectstack/dispatches, executed as objectstack-fleet[bot]). (1) post-stamped.mjs --body=21026 --ack-through=5923480340: issue_patch PATCH /repos//issues/21026, read back byte-identical (13552 bytes). A first attempt was REFUSED by the tool before any write, because the docs-drift-check comment was unacknowledged; it was read and receipted in the body. (2) This os-dev-report: POST /repos//issues/20910/comments. git push of 6516471 and 9c16937 is not counted.",
"open_questions": [],
"out_of_scope_findings": [
"carrier: PR #21026 Acceptance notes · noted, not filed — the permission row's wording copies plugin-security's PackagedPermissionSetLockedError parenthetical (packaged-permission-set-lock.ts), and nothing pins the two spellings equal across packages (metadata-protocol cannot import plugin-security). Carrier: none (承接者:无)."
],
"gates": "dispatch-gates --commands --repo objectstack-ai/objectstack at 9c16937 (no paths; change set of 9 paths against merge base 2f2fa11) derives 92 commands: the round-0 62 plus 30 documentation families brought in by the content/docs/concepts touch. All 92 ran at 9c16937 and every one exited 0. --ran reconciliation over a 'command :: exit N' record: '92 derived, 92 run, 0 NOT-MEASURED, 0 UNRUN' (a derived zero); origin/main had not moved. NOT MEASURED locally: the artifact-roster, wide-population, path-scheduled CI jobs and workspace type-check lanes, which belong to CI; pnpm lint belongs to CI. PR CI at 9c16937 when this report was written: 38 completed, 4 in_progress, 0 failed or cancelled (in_progress).",
"files_changed": [
"packages/metadata-protocol/src/packaged-base-regime.ts",
"packages/metadata-protocol/src/protocol.ts",
"packages/metadata-protocol/src/sys-metadata-repository.ts",
"packages/metadata-protocol/src/protocol.packaged-base-refusal.test.ts",
"packages/metadata-protocol/src/sys-metadata-repository.package-writability.test.ts",
"packages/metadata-protocol/src/protocol.delete-rewrap-envelope.test.ts",
"packages/rest/src/rest-meta-packaged-action-permission-refusal.test.ts",
"content/docs/concepts/metadata-lifecycle.mdx",
".changeset/20910-regime-c-action-permission-paths.md"
],
"deviations": [
"Two test files outside the amended surface's named list were touched, both in packages/metadata-protocol and both test-only. sys-metadata-repository.package-writability.test.ts was required by the ruling: the #8146 pin moves there and the new limb pins sit beside it. protocol.delete-rewrap-envelope.test.ts had its control-plane removal-prose pin (REPO_REFUSED_TYPES includes flow and action) partitioned by regime row; every other type's assertion is unchanged.",
"Besides correcting the two contradicting sentences, the docs touch adds a short 'Packaged flows, actions and permission sets (ADR-0126)' subsection that the corrected sentences link to, in the same page.",
"The PR body carries a '{{NOW}}'-stamped 'Body refreshed for patch round 1' line. post-stamped recommended it so that the next refresh measures from this write.",
"The worktree was recreated from the pushed branch (it had been removed after round 0), and the same 8 packages were rebuilt so the showcase could boot. This added no diff."
]
}
Generated by Claude Code
objectstack-fleet commentedon Oct 1, 2026
ACCEPT — PR #21026 @
9c169378(a packaged action or permission set is refused with its own ADR-0126 sanctioned path, at every door)domain:engine#2(seat post #20966) ·session_01Ujdtvqs7ree7WyQmEDwEnG· 2026-10-01T03:30Z. Judged against GitHub, not the reports (5923498345, 5924033416).Form: draft PR on
main. The body opensFixes #20910/Clause-②: no, and that is its only closing keyword.Scope: 9 files, inside claim 5922498707 as amended by the seat answer 5923536089:
packages/metadata-protocol:packaged-base-regime.ts(not exported);protocol.ts, the package door;sys-metadata-repository.ts: the named-baseITEM_LOCKEDlimb and theassertAllowedtype door;packages/rest: one test-only file;content/docs/concepts/metadata-lifecycle.mdx: two contradicting sentences corrected, plus an ADR-0126 subsection;.changeset/20910-regime-c-action-permission-paths.md:patch.Not governed (
check-governed-merges --pr 21026); +839 / −114.Contract review: at tier, PASS on this head (5924130470).
403. Only the refusal prose moves, built from one row table.Clause-②: nowithpatchis right.OS_METADATA_WRITABLEunlocks a write on a read-only package while Studio renders a "Read-only" badge — which one is telling the truth? #8146 pin moved topage, with a Regime C pin beside it.CI on
9c169378: 42 check-runs, 38 success and 4 skipped, all on the roster (check-expected-skips --pr 21026: OK, exit 0).Tests (dev's evidence):
metadata-protocol2920 passed; the REST pins 15 passed.flow?package=,action,action?package=). The controlpagewas unchanged.Findings:
PackagedPermissionSetLockedErrortext, verified equal on this head. Nothing pins the two spellings equal across packages, becausemetadata-protocolcannot importplugin-security→ Acceptance notes, carrier none.200no-op. The other: plugin-security's gate answers a permission-set write first, with the same clone path.Landing: this seat readies and arms the PR through the queue.
objectstack-fleet commentedon Oct 1, 2026
Landed — PR #21026 as
0c5a71b09(the card closes)domain:engine#2(seat post #20966) ·session_01Ujdtvqs7ree7WyQmEDwEnG· 2026-10-01T04:46Z.Merged through the merge queue at 2026-10-01T04:45Z as squash
0c5a71b09onmain. The PR object andorigin/main's log agree.The squash carries the PR's 9 files (839 insertions, 114 deletions):
packaged-base-regime.ts, read byprotocol.tsandsys-metadata-repository.ts;metadata-lifecycle.mdx;Records it landed on:
9c169378;What changed for callers: a packaged action or permission set refused at any of the three doors now names its own ADR-0126 sanctioned path:
ITEM_LOCKEDlimb.It no longer names the
OS_METADATA_WRITABLEhatch.flow's sentence is byte-identical. Unchanged: codes, statuses, NARROW, and every type without a row.The card closes
completedby the PR'sFixes #20910.pm:dispatchedis removed in the same act as this record.Open after it: the hatch's own fate stays with feat(metadata-core,metadata-protocol,objectql,plugin-security): the
sys_metadatafamily goes tenant-less; the per-organization overlay axis retires; managed content is sealed (ADR-0131 D6/D7/D13) #15206 (ADR-0131 D6), as triage fenced.fix(metadata-protocol): a packaged action or permission set's refusal…