Repository navigation
fix(security)!: under a wall the /packages verbs that publish, discard, roll back, revert, duplicate or adopt catalog items are the platform operator's (ADR-0131 D3) - #23003
Conversation
… door (ADR-0131 D3) Claude-Session: https://claude.ai/code/session_015JgoXpZUGtNxnMDvRZsKzP Co-authored-by: Claude <noreply@anthropic.com>
… with single and assignment controls (ADR-0131 D3) Claude-Session: https://claude.ai/code/session_015JgoXpZUGtNxnMDvRZsKzP Co-authored-by: Claude <noreply@anthropic.com>
…g refusal Claude-Session: https://claude.ai/code/session_015JgoXpZUGtNxnMDvRZsKzP Co-authored-by: Claude <noreply@anthropic.com>
…131 D3) Claude-Session: https://claude.ai/code/session_015JgoXpZUGtNxnMDvRZsKzP Co-authored-by: Claude <noreply@anthropic.com>
…ll-catalog-refusal
…087 semantic entry; census row 55b Claude-Session: https://claude.ai/code/session_015JgoXpZUGtNxnMDvRZsKzP Co-authored-by: Claude <noreply@anthropic.com>
…ll-catalog-refusal # Conflicts: # content/docs/permissions/system-context.mdx
…efusal (ADR-0131 D3) Claude-Session: https://claude.ai/code/session_015JgoXpZUGtNxnMDvRZsKzP Co-authored-by: Claude <noreply@anthropic.com>
… row 55b names the clone door Claude-Session: https://claude.ai/code/session_015JgoXpZUGtNxnMDvRZsKzP Co-authored-by: Claude <noreply@anthropic.com>
…y name the clone door Claude-Session: https://claude.ai/code/session_015JgoXpZUGtNxnMDvRZsKzP Co-authored-by: Claude <noreply@anthropic.com>
…ace: exact doors, capability's unchanged accept set, the D12 delegate Claude-Session: https://claude.ai/code/session_015JgoXpZUGtNxnMDvRZsKzP Co-authored-by: Claude <noreply@anthropic.com>
…he clone door and the Setup env-door write (ADR-0131 D3) Claude-Session: https://claude.ai/code/session_015JgoXpZUGtNxnMDvRZsKzP Co-authored-by: Claude <noreply@anthropic.com>
…nvironmentSets describe Claude-Session: https://claude.ai/code/session_015JgoXpZUGtNxnMDvRZsKzP Co-authored-by: Claude <noreply@anthropic.com>
Resolve content/docs/permissions/system-context.mdx: keep row 55b (this branch) and main's row 58 rewording (#22875); counts re-derived with pnpm gen:system-context-census. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015JgoXpZUGtNxnMDvRZsKzP
…refusal' into claude/issue-15204-wall-package-refusal
…ADR-0131 D3) Under group/isolated, below the platform operator rung, install-local refuses an install whose incoming version declares a position, permission set or capability, an upgrade where either version declares one, and an uninstall of a package whose installed version declares one; DELETE /packages/:id refuses a package holding stored catalog rows. Both ask walledCatalogWriteVerdict once per declared type. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015JgoXpZUGtNxnMDvRZsKzP
… own packages Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015JgoXpZUGtNxnMDvRZsKzP
…talog-package-writes-retired Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015JgoXpZUGtNxnMDvRZsKzP
Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015JgoXpZUGtNxnMDvRZsKzP
…log items; alias objectql in cloud-connection tests Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015JgoXpZUGtNxnMDvRZsKzP
…package-refusal Resolve content/docs/permissions/system-context.mdx: keep this branch's row 55b (it names the /packages delete refusal); counts re-derived with pnpm gen:system-context-census. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015JgoXpZUGtNxnMDvRZsKzP
…nd permission-set provenance pages Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015JgoXpZUGtNxnMDvRZsKzP
… wall (ADR-0131 D3) publish-drafts, revert, rollback, commits/:commitId/revert, duplicate and adopt-orphans ask walledCatalogWriteVerdict through one helper generalising the W2 delete refusal, each judging the items its verb acts on. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015JgoXpZUGtNxnMDvRZsKzP
Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015JgoXpZUGtNxnMDvRZsKzP
…talog-package-verbs-retired; docs Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015JgoXpZUGtNxnMDvRZsKzP
Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015JgoXpZUGtNxnMDvRZsKzP
…ll-package-refusal
…refusal' into claude/issue-15204-wall-catalog-package-doors
Resolve content/docs/permissions/system-context.mdx: main's count-free prose (#22950), this branch's row 55b; no count lines. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015JgoXpZUGtNxnMDvRZsKzP
…refusal' into claude/issue-15204-wall-catalog-package-doors
`--check-drift` takes the run's event (`--event`), required. On `schedule`, the profile scripts/test-shard-timings.json is measured on, a DRIFT exits 1 as before; on every other event it prints the same text, raises one `::warning::` annotation and exits 0. A missing or empty event is refused. Claude-Session: https://claude.ai/code/session_012yK1ddAdAqfzwigRHZq3r2 Co-authored-by: Claude <noreply@anthropic.com>
…one, with a 20s floor The ceiling check takes the run's event (`--event`, required). On `schedule`, the profile the ceilings were measured on, a package over its ceiling reds the step as before; on every other event the same table prints, each package over its ceiling is a `::warning::` annotation, and the step exits 0. Refusals stay red on every event. A package whose effective ceiling is under CEILING_FLOOR_SECONDS (20s) is never red and prints "below floor"; the header records the floor's reading. ci.yml passes `--event "$GITHUB_EVENT_NAME"` to both timing steps, which keep no `if:` and no `continue-on-error`. Claude-Session: https://claude.ai/code/session_012yK1ddAdAqfzwigRHZq3r2 Co-authored-by: Claude <noreply@anthropic.com>
…refusal' into claude/issue-15204-wall-catalog-package-doors
…204-wall-catalog-package-doors The four conflicted files are main's copy of W2's head, which this branch already carried; this branch's W3 versions are kept. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015JgoXpZUGtNxnMDvRZsKzP
📓 Docs Drift CheckThis PR changes 2 package(s): 22 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: ⛔ 10 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails. What this run could not see
Coarse fallback — 144 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 17939343c47eea434e97e6ef907caadee2cd4866 && git checkout 17939343c47eea434e97e6ef907caadee2cd4866
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 9f4b652e3a488f4351b47efdea93202033c064f7 f10bbe8e8aa6f4471f5dd7109c0d53b297fc84a7 && git checkout -B drift-repro 9f4b652e3a488f4351b47efdea93202033c064f7 && git merge --no-ff f10bbe8e8aa6f4471f5dd7109c0d53b297fc84a7
node scripts/docs-audit/affected-docs.mjs --json 9f4b652e3a488f4351b47efdea93202033c064f7
|
Contract reviewServed-tier: Full record on this PR (stage W3 of #15204, the walled catalog refusal at the remaining Gate verdicts on the head, read 2026-10-12T09:35Z. 34 check-runs: 32 ① Derived judgmentsEvery accept-set and public-surface change the diff implies, each named right or wrong.
② Semver level
③ Boundary flagsEvery dev flag and every
Implemented-by: VERDICT: FAIL Generated by Claude Code |
…plicate by the rows it copies POST /packages/:id/discard-drafts now asks refuseWalledCatalogPackageWrite with the package's draft rows, like /revert: the two verbs discard the same drafts. The duplicate door now reads exactly duplicatePackage's selection (active, organization-less rows). Pins: discard-drafts is refused under isolated and group (the draft stays); the operator, single and tenant catalog-free controls run it. The tenant control's title and the file header now name the verbs it covers (all but adopt-orphans). Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015JgoXpZUGtNxnMDvRZsKzP
…e-door texts The changeset, the ADR-0087 entry, census row 55b and both docs pages add discard-drafts (eight /packages write doors with the delete), name capability beside position and permission set, and say duplicate is judged by the rows it copies. The entry's acceptanceCriteria says which verbs the tenant catalog-free control runs. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015JgoXpZUGtNxnMDvRZsKzP
…age-doors Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015JgoXpZUGtNxnMDvRZsKzP
Contract reviewServed-tier: Delta record on this PR (stage W3 of #15204, the walled catalog refusal at the remaining Gate verdicts on the head, read 2026-10-12T13:11Z. 48 check-runs across the head's workflow runs, all ① Derived judgmentsLimited to the delta. Every accept-set or public-surface change the three new commits imply, named right or wrong.
② Semver levelThe delta adds the same narrowing at one more dispatcher door of ③ Boundary flags
Implemented-by: VERDICT: PASS Generated by Claude Code |
Seat ACCEPT: #23003 (stage W3, the walled catalog refusal at the remaining
|
Part of #15204
Clause-②: yes (narrowing)
Stage W3 of #15204, measured in 6117740670 and accepted for build by the seat. This is the walled catalog refusal at the remaining
/packageswrite doors. W (#22895) refused catalog writes at/meta, the clone door and the data door. W2 (#22935) refused them atinstall-localand atDELETE /packages/:id. This PR refuses the seven package verbs that still published, discarded, rolled back, reverted, copied or adopted the operator's catalog items behindmanage_metadataalone.The ruling this executes
ADR-0131 D3: "tenant admins cannot create or edit catalog items; the operator defines the catalog for every tenant (managed packages, Studio)". Under
group/isolated, below the operator rung (and notisSystem), each door refuses when the items its verb acts on include a position, a permission set or a capability. That is the placement (a) to (e) of 6117740670.Measured first
The harness is the W2 one: a booted
examples/app-crmstack (bootStack,multiTenant: 'posture-only') with two organizations.manage_metadatainside organization A (rungTENANT_ADMIN).Isolated and group answered identically:
POST /packages/:id/publish-drafts(the operator's pending draft addsmanage_users)manage_usersPOST /packages/:id/rollback {commitId: c1}POST /packages/:id/commits/:c3/revert(the operator's re-publish)POST /packages/:id/duplicateGET /meta/permission/:namethen serves A's copyPOST /packages/:id/revert(discard drafts)POST /packages/:id/discard-drafts(not measured; found by contract review 6121612552)revertPOST /packages/:id/adopt-orphansBy reading,
commits/:commitId/revertpasses only the commit id torevertCommit; the URL's:idis never read. 17.7.0 (4e4e881427) carries all seven doors, each gated byrequireManageMetadataalone (discard-draftsat4e4e881427:1792).What changed
refuseWalledCatalogPackageWriteinpackages/runtime/src/domains/packages.tsgeneralises W2'srefuseWalledCatalogPackageDelete, and the W2 delete now uses it.walledCatalogWriteVerdictFIRST. Its answer does not vary across catalog types, so when it admits the caller (undersingle, with notenancyservice, forisSystem, or for thePLATFORM_ADMINrung) nothing is read, and the door behaves as before.403 PERMISSION_DENIEDnaming the posture when they include a catalog type.publish-drafts,discard-draftsandrevert: the package'ssys_metadatadraft rows.rollback: the items of everyapplycommit of the target's package at or after the target's instant, a superset of the reverted ones. Read asrollbackToPackageCommitreads them: the target row, thenlistCommits.commits/:commitId/revert: that commit's own items, whatever package the URL names.duplicate: the rowsduplicatePackagecopies (active, organization-less).adopt-orphans: the orphans exactly asreassignOrphanedMetadataselects them (organization-less rows with a null, empty orsys_metadatapackage).walled-catalog-package-verbs-retired. The changeset carries FROM → TO per door./packageswrite doors (the delete included) and the three catalog types; its anchor moves torefuseWalledCatalogPackageWrite. The oneisSystemread moved into the helper, and none was added.api/metadata-api.mdx(each verb and what it is judged by) andpermissions/permission-sets.mdx(the verbs that publish, discard, roll back, revert, duplicate or adopt catalog items,discard-draftsincluded).Tests
Pins:
wall-package-doors-refusal.dogfood.test.ts, 23 tests.Under each of
isolatedandgroup, these are refused403 PERMISSION_DENIEDnaming the posture:Each pin compares B's resolution across its own request only.
Controls: A runs every door but
adopt-orphanson its own catalog-free package as before (that verb reads the environment's orphans, and this environment carries the operator's catalog orphan); the operator runs every door,adopt-orphansincluded, on the catalog-carrying one.singlecontrol: the samemanage_metadataholder runs them all as before.Ablation, one per door: each refusal was forced to admit (
isSystempassed to that call site alone),runtimerebuilt, and the marker confirmed indist/byablation-dist-preflight. Exactly that door's pins went red under both postures: publish-drafts 2, discard-drafts 2, revert 2, rollback 2, commit revert 4, duplicate 2 (re-run after the duplicate reader narrowed), adopt-orphans 2. Everything else stayed green, W2's 19 pins included. Each was restored to theHEADblob and rebuilt, and all seven markers are absent fromdist/.Suites and gates on this head
f10bbe8e8a(main0ede0f8ab3plus W3):check:system-context-censusOK;runtime: 359 files / 5,884 passed, 19 skipped;dispatch-gates --commands: 110 of 110 exit 0;check-commit-card-trailersover the three new commits: clean.Round-1 finding, fixed:
check:query-options-erasurerefused twoas anycasts on the helpers' engine reads, which now carry typed options.Acceptance notes
manage_metadatamay author or publish the environment's non-catalog metadata through these doors. A package whose drafts, commits or rows carry no catalog item is handled as before.adopt-orphansunder a wall: any environment catalog orphan refuses the whole adoption, since the verb rebinds every orphan at once. The operator adopts them, or a catalog-free environment adopts as before.🤖 Generated with Claude Code
https://claude.ai/code/session_015JgoXpZUGtNxnMDvRZsKzP