Skip to content

Commit 5ef2293

Browse files
committed
docs: name discard-drafts and capability in the walled catalog package-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
1 parent 2bc34da commit 5ef2293

5 files changed

Lines changed: 19 additions & 17 deletions

File tree

‎.changeset/15204-walled-catalog-package-verbs.md‎

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,18 +16,19 @@ Clause-②: yes (narrowing)
1616
- rolled the package back, and the member lost it;
1717
- reverted the operator's commit, and the member lost it;
1818
- copied the set, under the same name, into a package of their own, and `GET /meta` then served their copy;
19-
- discarded the operator's pending draft;
19+
- discarded the operator's pending draft (through `revert`, and `discard-drafts` is the same discard by a second door);
2020
- adopted the environment's catalog rows into their own package.
2121

2222
**FROM → TO**, on a `group` or `isolated` deployment, for a caller below the platform operator (the `PLATFORM_ADMIN` rung: the deployment's declared `OS_PLATFORM_OWNER_EMAIL` owner). Every door is on the runtime dispatcher, and each **FROM** is: admitted to any `manage_metadata` holder. Each **TO** is `403 PERMISSION_DENIED`, naming the posture, before the verb runs:
2323

24-
- `POST /api/v1/packages/:id/publish-drafts`, when the package has a pending position or permission-set draft.
24+
- `POST /api/v1/packages/:id/publish-drafts`, when the package has a pending position, permission-set or capability draft.
2525
- `POST /api/v1/packages/:id/revert`, when the package has such a draft.
26-
- `POST /api/v1/packages/:id/rollback`, when a commit it would undo touched a position or permission set.
26+
- `POST /api/v1/packages/:id/discard-drafts`, when the package has such a draft.
27+
- `POST /api/v1/packages/:id/rollback`, when a commit it would undo touched a position, permission set or capability.
2728
- `POST /api/v1/packages/:id/commits/:commitId/revert`, when that commit touched one. The commit's own items are judged: the verb takes the commit id alone, so the URL's package segment changes nothing.
28-
- `POST /api/v1/packages/:id/duplicate`, when the source package holds one.
29+
- `POST /api/v1/packages/:id/duplicate`, when the source package's published environment rows (the ones it copies) hold one.
2930
- `POST /api/v1/packages/:id/adopt-orphans`, when the environment's orphaned rows include one.
3031

3132
**Unchanged.** A package whose drafts, commits or rows carry no catalog item is handled as before, for the same callers. The platform operator runs every verb as before. Under `single` (and with no `tenancy` service) nothing changes, and no extra read happens: the verdict is asked before the items are read.
3233

33-
**The remedy.** The platform operator publishes, rolls back, reverts, duplicates and adopts catalog-carrying packages. `@objectstack/spec` registers the change as the ADR-0087 semantic entry `walled-catalog-package-verbs-retired`. Every door asks the one predicate the catalog's other write doors ask, `walledCatalogWriteVerdict` (`@objectstack/metadata-core`), through the same helper as the package delete.
34+
**The remedy.** The platform operator publishes, discards, rolls back, reverts, duplicates and adopts catalog-carrying packages. `@objectstack/spec` registers the change as the ADR-0087 semantic entry `walled-catalog-package-verbs-retired`. Every door asks the one predicate the catalog's other write doors ask, `walledCatalogWriteVerdict` (`@objectstack/metadata-core`), through the same helper as the package delete.

‎content/docs/api/metadata-api.mdx‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -129,15 +129,15 @@ other caller is refused `403 PERMISSION_DENIED` (ADR-0131 D3).
129129

130130
The package's other write verbs follow the same rule under a wall, judged by
131131
the items each one acts on:
132-
- `POST /packages/:id/publish-drafts` and `POST /packages/:id/revert`, by the
133-
package's pending drafts;
132+
- `POST /packages/:id/publish-drafts`, `POST /packages/:id/discard-drafts` and
133+
`POST /packages/:id/revert`, by the package's pending drafts;
134134
- `POST /packages/:id/rollback`, by the commits it would undo;
135135
- `POST /packages/:id/commits/:commitId/revert`, by that commit's own items,
136136
whatever package the URL names;
137-
- `POST /packages/:id/duplicate`, by the source's items;
137+
- `POST /packages/:id/duplicate`, by the source's published items it copies;
138138
- `POST /packages/:id/adopt-orphans`, by the environment's orphaned rows.
139139

140-
When those items include a position or a permission set, only the platform
140+
When those items include a position, a permission set or a capability, only the platform
141141
operator may run the verb.
142142

143143
### `PATCH /packages/:id/enable`

‎content/docs/permissions/permission-sets.mdx‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -354,8 +354,8 @@ permission sets is the platform operator's (ADR-0131 D3): that delete, and an
354354
`install-local` install, upgrade or uninstall of such a package, refuse any
355355
other caller `403` before anything changes. So do the package verbs that
356356
publish, discard, roll back, revert, duplicate or adopt catalog items
357-
(`publish-drafts`, `revert`, `rollback`, `commits/:commitId/revert`,
358-
`duplicate`, `adopt-orphans`).
357+
(`publish-drafts`, `discard-drafts`, `revert`, `rollback`,
358+
`commits/:commitId/revert`, `duplicate`, `adopt-orphans`).
359359

360360
## One authoritative store — the record is a projection (ADR-0094)
361361

‎content/docs/permissions/system-context.mdx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ One of the largest consumers of the flag;
183183
| 53 | Package REST route capability gate bypassed | rest | Get: a marketplace publish over REST (`POST /packages/publish`, the one route the REST registrar mounts since #14503) without `manage_metadata`; the package read cohort (`studio.access` / `setup.access`) is enforced by the dispatcher `/packages` domain's own read gate, where the reads are served | `packages/rest/src/package-routes.ts#refusePackageRequest` |
184184
| 54 | Package domain capability gates bypassed | runtime | Get: package management and package-inventory reads without the capability | `packages/runtime/src/domains/packages.ts#requireManageMetadata`, `#requireReadCapability` |
185185
| 55 | Activation write / authoring refusals do not fire | runtime | Get: activation artifacts writable and authorable without the activation-authoring capability | `packages/runtime/src/domains/activation-gate.ts#refuseUngrantedActivationWrite`, `#refuseUngrantedActivationAuthoring` |
186-
| 55b | Walled catalog refusal does not fire | metadata-core, rest, plugin-security, runtime | Get: under `isolated` / `group`, a create, change or delete of a position, a permission set or a capability, at the `/meta` write doors (save, reset, publish, rollback), at the permission-set clone door, at the data door on `sys_position`, `sys_permission_set` and `sys_capability`, and at the `/packages` write doors (delete, `publish-drafts`, `revert`, `rollback`, `commits/:commitId/revert`, `duplicate`, `adopt-orphans`) when the items they act on include a position or a permission set. Every other caller below the platform-operator rung is refused `403` there (ADR-0131 D3), because one organization's write would change what every organization resolves by name. The seeders, the package door and migrations write the catalog this way. Under `single` the refusal is inert for every caller | `packages/metadata-core/src/meta-write-capability.ts#walledCatalogWriteVerdict`, `packages/rest/src/rest-server.ts#walledCatalogRefusal`, `packages/plugins/plugin-security/src/walled-catalog-data-door.ts#createWalledCatalogDataDoorRefusal`, `packages/runtime/src/domains/packages.ts#refuseWalledCatalogPackageWrite` |
186+
| 55b | Walled catalog refusal does not fire | metadata-core, rest, plugin-security, runtime | Get: under `isolated` / `group`, a create, change or delete of a position, a permission set or a capability, at the `/meta` write doors (save, reset, publish, rollback), at the permission-set clone door, at the data door on `sys_position`, `sys_permission_set` and `sys_capability`, and at the `/packages` write doors (delete, `publish-drafts`, `discard-drafts`, `revert`, `rollback`, `commits/:commitId/revert`, `duplicate`, `adopt-orphans`) when the items they act on include a position, a permission set or a capability. Every other caller below the platform-operator rung is refused `403` there (ADR-0131 D3), because one organization's write would change what every organization resolves by name. The seeders, the package door and migrations write the catalog this way. Under `single` the refusal is inert for every caller | `packages/metadata-core/src/meta-write-capability.ts#walledCatalogWriteVerdict`, `packages/rest/src/rest-server.ts#walledCatalogRefusal`, `packages/plugins/plugin-security/src/walled-catalog-data-door.ts#createWalledCatalogDataDoorRefusal`, `packages/runtime/src/domains/packages.ts#refuseWalledCatalogPackageWrite` |
187187
| 56 | Automation run-state read, flow-authoring write, the paused-run caller gate (screen read and resume), the two operator run-lifecycle writes and the elevated self-triggered start at every door that starts a flow by name all pass | runtime | Get: run state, flow writes, a paused run's screen and its resume with no grant and without being the run's starter — and cancelling or restoring a suspension without the platform-operator rung. The screen read and the resume ask one predicate, so this is one bypass for both. The lifecycle bypass is the in-process owner's door: plugin-approvals' revise-window recall (ADR-0044) cancels on behalf of a decision it already authorized and recorded. And starting a flow declared `runAs: 'system'` whose type is self-triggered (`autolaunched`, `record_change`, `schedule`) through any door that starts a flow by name — the trigger route, an action of `type: 'flow'` (REST `/actions` and the MCP `run_action` bridge) and a declared endpoint of `type: 'flow'` — which every other caller, the guest principal included, is refused `403 PERMISSION_DENIED` (the maintainer's rulings, letters B and A). The three doors ask one predicate, so this is one bypass for all of them, and it is the in-process caller's, such as a job: inbound HTTP never carries the flag | `packages/runtime/src/domains/automation.ts#mayReadRunState`, `#refuseUngrantedFlowWrite`, `#isRunStarterOrRunStateReader`, `#refuseUngrantedRunLifecycleWrite`, `packages/runtime/src/flow-start-admission.ts#refusesElevatedSelfTriggeredStart` |
188188
| 57 | Audience-binding suggestion authority gate passes | plugin-security | Get: a system caller lists, accepts and dismisses suggestions without `manage_metadata` and, under a wall, without the platform-operator rung | `packages/plugins/plugin-security/src/suggested-audience-bindings.ts#assertDeploymentAuthority` |
189189
| 58 | Email-template organization door passes; webhook provenance stamp skipped | plugin-email, plugin-webhooks | Get (`sys_email_template`): a create or update of a template row. The door refuses every other write that names a caller, `403 PERMISSION_DENIED` (ADR-0131 D6); the plugin itself no longer writes the table (templates resolve from the registry), so the v18 migration ceremony's promotion is the one remaining writer. Lose (`sys_webhook`): the row is not marked as an admin customization | `packages/plugins/plugin-email/src/email-template-door.ts#isOrganizationWrite`, `packages/plugins/plugin-webhooks/src/webhook-provenance.ts#bindWebhookProvenanceStamp` |

‎packages/spec/src/migrations/entries/semantic/18.walled-catalog-package-verbs-retired.ts‎

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,10 @@ export const entry: SemanticMigration = {
1313
// code span AND a table cell.
1414
surface:
1515
'on a group or isolated deployment, by a caller below the platform operator, on the runtime '
16-
+ 'dispatcher: POST /api/v1/packages/:id/publish-drafts and /revert when the package has a pending '
17-
+ 'position or permission-set draft; /rollback when a commit it would undo touched one; '
16+
+ 'dispatcher: POST /api/v1/packages/:id/publish-drafts, /discard-drafts and /revert when the package has a pending '
17+
+ 'position, permission-set or capability draft; /rollback when a commit it would undo touched one; '
1818
+ '/commits/:commitId/revert when that commit touched one, whatever package the URL names; '
19-
+ '/duplicate when the source holds one; and /adopt-orphans when the orphaned rows include one',
19+
+ '/duplicate when the published rows it copies hold one; and /adopt-orphans when the orphaned rows include one',
2020
replacement:
2121
'the platform operator (the `PLATFORM_ADMIN` rung, the deployment\'s declared '
2222
+ '`OS_PLATFORM_OWNER_EMAIL` owner) runs those verbs on catalog-carrying packages; a package whose '
@@ -35,11 +35,12 @@ export const entry: SemanticMigration = {
3535
'On an isolated or group deployment, the operator publishes a permission set in a writable '
3636
+ 'package twice (the second adding manage_users) and leaves a third draft pending; another '
3737
+ 'organization\'s member holds the set. An organization owner holding manage_metadata inside '
38-
+ 'their own organization then calls publish-drafts, revert, rollback to the first commit, '
38+
+ 'their own organization then calls publish-drafts, discard-drafts, revert, rollback to the first commit, '
3939
+ 'commits/<second>/revert (also under a URL naming their own package), duplicate and, from a '
4040
+ 'package of their own, adopt-orphans: each answers 403 PERMISSION_DENIED naming the posture, '
4141
+ 'the draft stays pending, nothing is copied or rebound, and the member resolves exactly what it '
42-
+ 'resolved before. The same owner runs every verb on a package of their own whose items carry no '
42+
+ 'resolved before. The same owner runs every verb but adopt-orphans (which reads the '
43+
+ 'environment\'s orphans, not the package) on a package of their own whose items carry no '
4344
+ 'catalog item with 200; the platform operator runs every verb on the catalog-carrying package '
4445
+ 'with 200; and on a single deployment a manage_metadata holder runs them exactly as before.',
4546
};

0 commit comments

Comments
 (0)