Repository navigation
Commit 4feb2d0
fix(metadata-protocol)!: one sentence for every write over a sealed, package-held name; the inferred create remedy retires (#23626)
Fixes #23577
Clause-②: no
Executes ruling `6184789291` (batch #348 item 1, letter B) on this card.
## What changed
- **One sentence for every write over a sealed, package-held name**
(`packages/metadata-protocol/src/packaged-base-regime.ts`,
`managedItemSealedSentence`). For a type with no regime row, every write
reads: "'TYPE/NAME' is provided by a managed package and is sealed: it
cannot be written here. GET /api/v1/meta/TYPE lists the names in use."
This holds for every mode and pin: the console's generic create (`mode:
'draft'` + `If-None-Match: *`), a publish-mode `*`, and unpinned and
pinned saves. It gives no create advice and no source-artifact advice.
The repository's type door (draft promotion, restore, revert) builds the
same sentence. Code `NOT_OVERRIDABLE` and status `403` are unchanged.
- **Holding package not named.** Neither door has the artifact's package
at hand. `refusePackagedBaseOverride` asks `isSealedManagedItem`, a
boolean, and `isArtifactBacked` discards the `lookupArtifactItem`
result. `SysMetadataRepository.assertAllowed` is told only the write
intent. Per the ruling, no lookup is added.
- **Retired (no producer remains):** `sealedWrite` in `saveMetaItem`;
the `'create'` member of `packagedBaseRefusal`'s `operation`; the
`create` arm of `SealedItemOperation`; `heldNameSentence`; the
`operation` parameter of `refusePackagedBaseOverride` and of
`tenantAuthoredWriteRefusal` (it only ever carried `create`).
`runtime/src/domains/automation.ts` passes `'save' | 'delete'` and is
untouched.
- **Position 2:** the `## create` section of `packagedBaseRefusal`'s
docblock, which named the retired position write-through, is replaced by
a short "one `save`, whatever the write meant" note.
- **Spec docblocks (ruling item 2):**
`GetMetaItemResponseSchema.version` and
`MetadataConflictErrorSchema.currentVersion`
(`packages/spec/src/api/protocol.zod.ts`) now say that a `null` beside
`editable: false` takes no save and prescribe no pin for it. These are
`.describe()` texts. The only generated output that moved is
`content/docs/references/api/protocol.mdx`, written by `check:generated
--fix` (`gen:docs`).
- **Position 3:** the clone door's lost-race 409 came from the
protocol's first-write-pin conflict sentence (`metadataConflictRefusal`,
"Send that version as If-Match …"), which the clone door relayed through
`errorFromThrown`. The fix is in the clone door, the producer of the
clone caller's response. A `METADATA_CONFLICT` from its create-only save
is rethrown as the door's own name-taken refusal: 409, default code
`RESOURCE_CONFLICT`, the same sentence a name the catalog already held
reads. The `/meta` door's conflict sentence is unchanged, so it stays
correct for REST callers that send `If-Match`.
- `scripts/engine-double-contract.pinned.json`: the three rows
`check:engine-double-contract --write` adds for the new `view` control
harness.
- Changeset `.changeset/23577-sealed-write-one-sentence.md` (minor:
metadata-protocol, with the **BREAKING** line and its ADR-0087
disposition; patch: runtime, spec).
- `.changeset/23406-sealed-first-draft-remedy.md` (unconsumed, the same
release) is amended in place so it no longer states the draft/publish
remedy split this PR retires; it keeps its first-write-pin `409`
sentence. A falsified entry is corrected in the entry itself, never by
an erratum in a later one (AGENTS.md, the `packages/*/CHANGELOG.md`
row), so this PR's changeset carries no "supersedes" line (`f0761ed3`,
the seat's patch round).
## Pins (ruling item 4)
`protocol.sealed-remedy-kind.test.ts` is re-pointed. #23565's
draft/publish block now asserts the one sentence. On `position`,
`object` and `flow`, on both kernel shapes, these five requests all read
the identical refusal: console create (draft + `*`), publish `*`,
unpinned draft, unpinned, pinned. For `position` and `object`, that
refusal is the ruled sentence. The registry enumeration holds every
row-less reachable type to the ruled sentence, under the 500-character
bound with an 88-character name. An overlay control shows that a `view`
first write pinned `*` over a packaged view lands an overlay row
(`success: true`, one row). The clone door has a new lost-race pin: 409
`RESOURCE_CONFLICT`, the name-taken sentence, and no `If-Match`.
#23565's REST-door pin `rest-meta-sealed-first-draft-remedy.test.ts` is
re-pointed to the one sentence for the same requests on both kernels
(`3f354c73`).
## Verification
- Closure build `pnpm --filter '@objectstack/runtime^...' --filter
@objectstack/runtime build`: exit 0.
- `pnpm --filter … run typecheck` for spec, metadata-protocol, runtime:
exit 0.
- `pnpm --filter @objectstack/metadata-protocol test`: 236 files passed,
3 skipped; 28418 tests passed.
- `pnpm --filter @objectstack/runtime test`: 383 files, 5773 tests
passed.
- `pnpm --filter @objectstack/spec test`: 678 files, 20728 tests passed.
- Touched pins in other packages:
`rest-meta-packaged-flow-refusal.test.ts` and
`rest-meta-sealed-first-draft-remedy.test.ts` passed 6/6; `objectql
protocol-commit-history.test.ts` passed 30/30. CI Test Core 5/6 caught
`rest-meta-sealed-first-draft-remedy.test.ts` red at `9ec5053e` (it
still pinned the retired split); it was re-pointed in `3f354c73`, and
the full `rest` run at `9ec5053e` had its 284 other files green.
- `f0761ed3` (changesets only): `check-empty-changeset`,
`check-changeset-no-major`, `check-adr-0087-registration`, `pnpm
changeset status --since=origin/main`, `check-closing-keyword-parity`
and `check-changeset-fixed` exit 0.
- `pnpm --filter @objectstack/spec check:generated`: one stale artifact
(`check:docs`); after `--fix`, re-check green.
- Ablations, committed first, each restored with blob equal to HEAD and
an empty `git diff HEAD` (`scripts/ablation-replace.mjs`):
- Clone guard (`METADATA_CONFLICT` to a renamed code): 1 red / 22 green,
predicted 1 red.
- Sentence builder (the `save` branch disabled): 7 red / 6 green,
predicted. The row-less pins went red; flow, reach, removal and the
`view` control stayed green.
- `dispatch-gates --commands` local union (114 derived plus 5 roster
families under my paths), first run at `51f2e48b`:
- Real reds, both fixed in `9ec5053e`: `check:durability-log-level` (the
clone's new catch; it now rethrows to the door's one `errorFromThrown`)
and `check:engine-double-contract` (ledger rows). Both re-run green.
- Exit 2/3 prerequisites, NOT MEASURED: `check-engine-split-ratio` and
`check-plugin-teardown-shape --self-test` (shallow clone);
`check:skill-examples` and `check:dual-build-cjs-loads` (whole-workspace
build absent).
- The final-head re-run of the union is recorded in the card's report
comment.
## Acceptance notes
- **Scope of "every write": Regime C and origin-gated rows are kept.**
`flow`, `action` and `permission` keep their regime sentence. ADR-0126
§2 requires a Regime C refusal to name its sanctioned path, and those
sentences carry neither create advice nor source advice. `datasource` is
origin-gated: its items are code-defined, not package-held, and its
sentence states the datasource-admin door's own verdict. The ruling's
fact base is the row-less pair `managedItemSealedSentence` built. Raised
in the report's `open_questions`.
- **Removals unchanged.** Every removal keeps its sentence, the
hatch-naming one included.
- **`ITEM_LOCKED` named-base limb unchanged.** When a caller names a
read-only base (`readOnlyBaseOverrideError`), the limb keeps its own
code and its "Edit the source artifact and redeploy" remedy. The ruling
binds the package-less `NOT_OVERRIDABLE` refusal.
- **Unreleased changeset.**
`.changeset/23406-sealed-first-draft-remedy.md` described the split this
PR retires. It is amended in place (see the What changed list), and the
`Check Changeset` step-12 red that this produces is the gate's
deliberate-correction class, carried to the maintainer by
`needs-user-decision` (comment 6186537521).
- **Breaking declaration.** The `'create'` member of
`packagedBaseRefusal`'s `operation` and `tenantAuthoredWriteRefusal`'s
`operation` parameter are a released public input surface
(`18.0.0-next.0`), so the `@objectstack/metadata-protocol` changeset
carries a `**BREAKING**` line, an ADR-0087 disposition and the
launch-window `minor` grade (contract review 6186902880, ②).
- The 409 lost-race answer of the clone door now reads code
`RESOURCE_CONFLICT` instead of `METADATA_CONFLICT`. That is the code the
door already declares for a taken name.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01M83DE8c8ySe7XfBTNTxzFd)_
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent 8d1fb1f commit 4feb2d0
15 files changed
Lines changed: 339 additions & 345 deletions
File tree
- .changeset
- content/docs/references/api
- packages
- metadata-protocol/src
- objectql/src
- rest/src
- runtime/src/domains
- spec/src/api
- scripts
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
2 | 2 | | |
3 | 3 | | |
4 | 4 | | |
5 | | - | |
| 5 | + | |
6 | 6 | | |
7 | 7 | | |
8 | 8 | | |
9 | | - | |
| 9 | + | |
10 | 10 | | |
11 | 11 | | |
12 | 12 | | |
13 | | - | |
14 | | - | |
| 13 | + | |
15 | 14 | | |
16 | 15 | | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1453 | 1453 | | |
1454 | 1454 | | |
1455 | 1455 | | |
1456 | | - | |
| 1456 | + | |
1457 | 1457 | | |
1458 | 1458 | | |
1459 | 1459 | | |
| |||
2386 | 2386 | | |
2387 | 2387 | | |
2388 | 2388 | | |
2389 | | - | |
| 2389 | + | |
2390 | 2390 | | |
2391 | 2391 | | |
2392 | 2392 | | |
| |||
0 commit comments