Skip to content

fix(metadata-protocol): a sealed-item refusal of a create names a free name, not the edit remedy (#22591) - #22623

Merged
objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/issue-22591-sealed-remedy-kind
Oct 10, 2026
Merged

objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/issue-22591-sealed-remedy-kind

Conversation

@objectstack-fleet

@objectstack-fleet objectstack-fleet Bot commented Oct 10, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #22591
Clause-②: yes

The sealed-item refusal (403 NOT_OVERRIDABLE) now names the remedy that fits what the caller did. This closes out the family that #20819 (flow) and #20910 (action, permission) worked through one type at a time.

  • An edit of the item under a held name keeps its sentence, byte for byte. That is the row's sanctioned path, or "Edit the source artifact and redeploy" for a type without a row.
  • A create under a name that a managed package or a built-in holds, or a rename into such a name, is now told: the name is taken; to author your own item, choose a name no package or built-in holds, and GET /api/v1/meta/TYPE lists the names in use.

Which writes are refused does not change, and neither do the code or the status.

Measured at the real doors (showcase, single)

The probe was a local file, never committed and deleted after the run. It booted the real showcase composition through bootShowcase, signed in as the seeded admin, and used the HTTP doors. Before was origin/main d748ae80af, with the dogfood dependency closure built from it. After was this branch at 6499e49c0f, with metadata-protocol and plugin-security rebuilt from that head.

Door and request Before After
Setup POST /data/sys_position with manager (package com.example.showcase) 403 NOT_OVERRIDABLE, "… Edit the source artifact and redeploy." 403 NOT_OVERRIDABLE, "… so its name is taken. To author your own position, choose a name no package or built-in holds (GET /api/v1/meta/position lists the names in use)."
Setup create of guest (built-in, com.objectstack.plugin-security) 403, edit remedy 403, create remedy
Setup PATCH renaming an admin position into exec 403, edit remedy 403, create remedy
PUT /meta/position/manager with If-None-Match: * (the first-write precondition) 403, edit remedy 403, create remedy
PUT /meta/flow/showcase_task_completed with If-None-Match: * 403, clone and switch 403, create remedy, citing ADR-0126
PUT /meta/position/manager with no precondition, and with ?mode=draft 403, edit remedy 403, edit remedy (unchanged)
PUT /meta/flow/showcase_task_completed with no precondition 403, clone and switch unchanged
POST /automation onto showcase_task_completed 403, clone and switch unchanged (see the Acceptance notes)
Setup create of p22591_lead (control) 201 201

GET /api/v1/meta/position listed manager, exec, everyone, guest and the other held names, so the remedy's pointer answers.

One create-aware sentence, not a position row

This was the claimant's choice to make by measurement. The create remedy does not depend on the type: on every type, a create under a held name has one remedy, which is another name. So heldNameSentence in packaged-base-regime.ts is one sentence for every type. Only its opener and its citation come from the type's row, as in the edit sentences. An origin-gated datasource is "code-defined", not shipped by a managed package. A Regime C type cites ADR-0126, whose §2 makes a new machine name mandatory for an item authored beside a packaged one. A per-type position row would have fixed one of the 24 reachable types and left the rest for a fourth card. A type added to the registry later gets the create remedy without a row.

Where the act comes from

The act is the caller's fact and is never inferred. A door that cannot tell keeps the edit remedy.

  • managedItemSealedSentence(type, name, operation) takes 'save' | 'create' | 'delete'. A 'create' reads heldNameSentence.
  • ObjectStackProtocolImplementation.packagedBaseRefusal takes operation: 'create', with the same predicate, code and status as 'save'. refusePackagedBaseOverride passes the act through to the sentence and to nothing else.
  • Setup (plugin-security, position-write-through.ts): the S10 verdict is asked only for a create or a rename into a name, so it now asks with operation: 'create'. This is the producer of the card's measured symptom, in another package. The dispatch allowed fixing it at the producer.
  • The /meta save door (saveMetaItem): its one fact about the name is the first-write pin. A present null parentVersion (If-None-Match: *) asserts that the caller holds no row there, which the spec calls the first-write pin, so it is read as a create. It reaches both locked-base doors: tenantAuthoredWriteRefusal (flows, which gains an optional operation) and the package door. Any other save is an edit.
  • SysMetadataRepository.assertAllowed (the third caller the dispatch named) is unchanged. Its saves are server replays of the item's own stored history (draft promotion, restore, revert), which are edits, and it has no create fact to pass.

Tests

  • packages/metadata-protocol/src/protocol.sealed-remedy-kind.test.ts (new):
    • Enumeration pin. Every type the refusal reaches is measured by asking the verdict, never from a transcribed list. That is the 23 registry types with no overlay channel plus a plugin-registered type, on both kernel shapes. Asserted equal to the registry's own flag, with a floor. For each type, an edit names the edit remedy and a create names the create remedy. The kind is read off what the sentence points at: the row's own routes or source (read from PACKAGED_BASE_REGIME), the source/redeploy remedy, or the type's listing route. The wording is not asserted.
    • Controls. A create is refused exactly where a save is, with NOT_OVERRIDABLE / 403 on every row. A free name is null for both acts. A removal names no new name. Every create sentence for an 88-character name arrives whole under the REST door's 500-character bound, with no hatch, clone or switch.
    • The /meta door. For position and flow: a first-write save names the create remedy, and an unpinned or If-Match save keeps the edit remedy, with the same code and status. The door's message equals the verdict's (one emitter).
  • packages/plugins/plugin-security/src/position-write-through.test.ts:
    • S10's "the door's own refusal" pin now compares the Setup create with the door's first-write save, because a create relays the door's answer to a create.
    • New: a create under a package-held name and under a built-in (everyone) names the create remedy, while the door's edit refusal of the same name keeps the edit remedy. A rename into guest names the create remedy.

Ablation, run at 6499e49c0f with the fix committed first. Through scripts/ablation-replace.mjs, the line if (operation === 'create') return heldNameSentence(singular, name); was replaced with a comment, which collapses a create back to the edit remedy. On disk the anchor count went 1 → 0 and the marker count 0 → 1. Both suites then ran. metadata-protocol had 5 failed and 5 passed: every create row flipped to "create": "edit", while the reachability, verdict and removal controls stayed green. plugin-security had 3 failed and 41 passed. The restore put the blob back equal to HEAD (a0fda00025d8) with git diff HEAD empty. Neither suite reads dist/: the test imports ./protocol.js, and plugin-security's vitest config aliases @objectstack/metadata-protocol to its src. So the ablation needed no rebuild.

Runs at 3c3e4ef362 (this branch after merging origin/main 18d999031b; the merge brought no change to either touched file):

  • pnpm --filter @objectstack/metadata-protocol exec vitest run: Test Files 224 passed | 3 skipped (227), Tests 28314 passed | 19 skipped (28333).
  • pnpm --filter @objectstack/plugin-security exec vitest run: Test Files 194 passed (194), Tests 4096 passed | 45 skipped (4141).
  • typecheck passes for both packages, plugin-security's check:test-typecheck included. tsc --listFiles counts each touched test file once in its package's program.
  • The 69 gate commands dispatch-gates --commands derives for this diff all exit 0, reconciled with --ran: 69 run, 0 NOT MEASURED. One note: check:dual-build-cjs-loads first answered PREREQUISITE NOT MET (exit 3) because 8 unrelated packages had no dist/. After building them it exited 0.
  • eslint, narrowed to the 5 touched TypeScript files, with --no-inline-config and --format json: 5 files, 0 errors, 0 warnings. Each file is inside the config (--print-config resolves). The config enables no type-aware linting (no parserOptions.project), so this diff cannot move the verdict on any untouched file. The repository-wide pnpm lint is CI's.

Acceptance notes

  • Studio's create form still gets the edit remedy until objectui declares its create. The /meta PUT is an upsert, so an unpinned save onto a held name is an edit by the door's contract. Studio's create mode sends no precondition (objectui ResourceEditPage create path). Its comment says the door cannot express "expect no row", which has not been true since the door took If-None-Match: *. The server half is here. The client half is to send If-None-Match: * on create. It is reported for the seat to file in objectui, with this PR as its blocker.
  • POST /automation onto a packaged flow's name is unchanged. That door's own contract makes a create onto an existing name an overwrite, which is an edit, so its clone-and-switch remedy matches what it does. POST /automation/:name/clone refuses any target name the engine already holds with its own name-taken refusal first (read in automation.ts, not probed).
  • Code-only types (allowRuntimeCreate: false) are refused at /meta by their own emitter (codeOnlyOverrideError) before the package door. It says "declare it in source". That is right for a create too, because such a type has no runtime create.
  • Clause-② reads yes. packagedBaseRefusal accepts operation: 'create', and tenantAuthoredWriteRefusal takes an optional operation. That widens a public input surface, so both packages take minor. No verdict, code or status moves. The seat answered the question A and amended its claim (6093560675). Patch round 1 (6094616222, head caeb7b58f1) changed the changeset only.
  • No other region of protocol.ts or sys-metadata-repository.ts is touched. The seat's feat(metadata-core,metadata-protocol,objectql,plugin-security): the sys_metadata family goes tenant-less; the per-organization overlay axis retires; managed content is sealed (ADR-0131 D6/D7/D13) #15206 S5 read-path work does not overlap.

Generated by Claude Code

…e name, not the edit remedy

The locked-base sentence now follows what the caller did: a save that takes a
name a managed package or a built-in holds (a create, or a rename into it)
is told to choose a name no package or built-in holds; an edit keeps the
remedy it had, byte for byte. The verdict, code and status do not move.

- packaged-base-regime.ts: one create sentence for every type
  (heldNameSentence), chosen by the new 'create' operation.
- protocol.ts: packagedBaseRefusal accepts operation 'create'; the /meta
  save door reads its first-write pin (parentVersion null, If-None-Match: *)
  as a create, for the flow rule and the package door alike.
- plugin-security: Setup's position write-through asks as a create.

Claude-Session: https://claude.ai/code/session_01Bw3y2DWhT9RPnrmDsNqEVG
Co-authored-by: Claude <noreply@anthropic.com>
…and a create on every reachable type

The enumeration pin iterates every type the refusal reaches (the registry's
types with no overlay channel, and a plugin-registered type) for both acts and
asserts the remedy kind: an edit names the edit remedy, a create names a name
no package or built-in holds. Controls: the verdict, code and status do not
move. The /meta door's first-write pin and Setup's position create and rename
are pinned at their doors.

Claude-Session: https://claude.ai/code/session_01Bw3y2DWhT9RPnrmDsNqEVG
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added size/m documentation Improvements or additions to documentation tests tooling labels Oct 10, 2026
@github-actions

github-actions Bot commented Oct 10, 2026 •

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 2 package(s): @objectstack/metadata-protocol, @objectstack/plugin-security, touching 14 documentable anchor(s).

6 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:

  • content/docs/api/client-sdk.mdx (via getTypes (sdk, the bare tail of client method meta.getTypes, bound to GET /api/v1/meta), meta.getTypes (sdk, the route ledger binds it to GET /api/v1/meta))
  • content/docs/concepts/metadata-lifecycle.mdx (via ObjectStackProtocolImplementation (symbol, a top-level class), saveMetaItem (symbol, a method of class ObjectStackProtocolImplementation))
  • content/docs/deployment/validating-metadata.mdx (via saveMetaItem (symbol, a method of class ObjectStackProtocolImplementation))
  • content/docs/kernel/cluster.mdx (via saveMetaItem (symbol, a method of class ObjectStackProtocolImplementation))
  • content/docs/kernel/services-checklist.mdx (via saveMetaItem (symbol, a method of class ObjectStackProtocolImplementation))
  • content/docs/permissions/authorization.mdx (via saveMetaItem (symbol, a method of class ObjectStackProtocolImplementation))

⛔ 2 release-owned page(s) also name something this change touched. These are read-only:

  • content/docs/releases/v16.mdx (via ObjectStackProtocolImplementation (symbol, a top-level class))
  • content/docs/releases/v17/17-0.mdx (via ObjectStackProtocolImplementation (symbol, a top-level class))

content/docs/releases/ is RELEASE-OWNED (AGENTS.md "Documentation Guardrails"): release
notes are written centrally at release time, and a code PR that edits them is the exact PR
that guardrail exists to stop. They are still audited — read-only. If one of them is actually
wrong, file an issue or open a dedicated docs-only PR; do not edit it here.

What this run could not see
  • 1 anchor(s) matched too much of the corpus to be a work list: /api/v1/meta (route, 36 pages)
  • the SDK route bridge reached 54 of 206 client-bound route-ledger rows — the other 152 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 152: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 55 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 97 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.
  • a key NAME is not a key, so the hand re-read the line above prescribes can land on the wrong schema. The same spelling is authorable on one governed type and a [REMOVED] tombstone on another for each of active, aria, joins, objects, template, tools and version (censused on [finding] tools is a key on BOTH AgentSchema (tombstoned, dead) and SkillSchema (live, cloud-attested), so a name-based search attributes skill examples to the agent key — it produced a false stop-the-line alarm on PR #19059 #19093 over the liveness ledger's governed types, top-level keys); nothing in a search result distinguishes the two, so a grep hit on a LIVE example reads as evidence about the DEAD key. Measured on fix(spec): the agent.tools liveness row says dead — it claimed live on a key the schema tombstoned #19059: content/docs/ai/agents.mdx was reported as contradicting the agent.tools tombstone over its tools: example at :161, which is inside the defineSkill({ block opened at :155 — the page was already correct. Settle ownership by PARSING the value against both schemas, never by the name: that literal PASSES SkillSchema, and as an AgentSchema it FAILS at tools with the tombstone prescription. ⛔ These names are not the whole class — a key retired through a .strict() guidance map leaves no tombstone in the walked shape and none of them here (tool.category, live as AIToolDefinition.category).

Coarse fallback — 24 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json e22315238f9426708e10cfff1d6a96e56cca33a1 → packageMentionDocs.

Which tree this was computed on

This run read content/docs from fdf8fbff1516569e6c4b10fe5e8206930043230c — the merge of head caeb7b58f1a0f6a207d0036b73788c19be28595f into base e22315238f9426708e10cfff1d6a96e56cca33a1, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin fdf8fbff1516569e6c4b10fe5e8206930043230c && git checkout fdf8fbff1516569e6c4b10fe5e8206930043230c
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin e22315238f9426708e10cfff1d6a96e56cca33a1 caeb7b58f1a0f6a207d0036b73788c19be28595f && git checkout -B drift-repro e22315238f9426708e10cfff1d6a96e56cca33a1 && git merge --no-ff caeb7b58f1a0f6a207d0036b73788c19be28595f

node scripts/docs-audit/affected-docs.mjs --json e22315238f9426708e10cfff1d6a96e56cca33a1

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs e22315238f9426708e10cfff1d6a96e56cca33a1 → pass the list as
args.docs, on the commit named under Which tree this was computed on.

…or for both packages

packagedBaseRefusal on the exported ObjectStackProtocolImplementation now
accepts operation 'create', and tenantAuthoredWriteRefusal takes an optional
operation. That widens a public input surface, so the declaration reads
Clause-②: yes and both packages take minor. No verdict, code or status moves.

Claude-Session: https://claude.ai/code/session_01Bw3y2DWhT9RPnrmDsNqEVG
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: caeb7b58f1a0f6a207d0036b73788c19be28595f
Local-runs: none

Inputs: card #22591 (body; triage 6093105279; claim 6093560675 as amended; os-dev-reports 6094519413 and 6094616222), PR #22623 (body, its six-file list, the net diff against main at e22315238f), and the check-runs on this head. Nothing was built, run or re-run; the source cited below was read through the API at the head or the base sha, never from a checkout.

① Derived judgments

Public surface of @objectstack/metadata-protocol (its exports map has the . entry only, and index.ts does not re-export packaged-base-regime.ts, so managedItemSealedSentence and the new SealedItemOperation type stay internal):

  • ObjectStackProtocolImplementation.packagedBaseRefusal — operation widens from 'save' | 'delete' to 'save' | 'create' | 'delete'. An additive input on an exported class's method; every existing call compiles unchanged. Right, and the widening the declaration names.
  • ObjectStackProtocolImplementation.tenantAuthoredWriteRefusal — gains an optional operation ('save' | 'create'), read as 'save' when absent. Additive. Right.
  • ObjectStackProtocolImplementation.saveMetaItem — no signature change. Behaviour: a save whose parentVersion is a present null is handed to both locked-base doors (the flow rule and the package door) as a create, so its 403 NOT_OVERRIDABLE sentence names the create remedy; every other save is an edit as before. The reading is the door's standing contract, not a new inference: packages/rest/src/meta-save-request.ts maps If-None-Match: * to parentVersion: null ("no row of this lifecycle is here", the first-write pin), and saveMetaItem's own text already says a null "says that save is a create: the state If-None-Match: * asserts". Right.

Accept set:

  • Which writes are refused, and with which code and status, moves on no door. refusePackagedBaseOverride reads operation for the sentence and nothing else; isSealedManagedItem and the limbs before the sentence are untouched; heldPositionNameRefusal keeps the save predicate. The new pin asserts a create is refused exactly where a save is, with NOT_OVERRIDABLE / 403, on every reachable type on both kernel shapes (the registry's types with no overlay channel, asserted equal to the allowOrgOverride flag, plus a plugin-registered type; floor 20, 24 reached), and that a free name is null for both acts. Right — no accept-set change, so yes carries no narrowing arm and no ADR-0087 disposition is owed.
  • An edit (save) and a removal (delete) keep their sentences byte for byte: the save/delete path of managedItemSealedSentence is unchanged, and regimeCPrescription now reads the same ADR path through the REGIME_C_ADR constant (declared after the function, read only at call time; no module-load hazard). Right.
  • heldNameSentence is ONE sentence for every type; only the opener (an origin-gated row's noun, else "provided by a managed package") and the citation (ADR-0126 for Regime C, the row's docs for origin-gated, ADR-0131 otherwise) come from the row. It names no source artifact, no sanctioned path and no OS_METADATA_WRITABLE hatch, and the pin holds it under the REST door's 500-character bound with an 88-character name on every type. Right — the measured choice the card and triage left to the claimant (one create-aware sentence rather than a position row), and it closes the family instead of a fourth type. One wording observation, unpinned and owing no action: for an origin-gated name the host owns (datasource/default) the create opener reads "is code-defined" where the edit sentence names the host's configuration; the remedy kind is right either way.
  • canonicalizeMetaRequestType spreads the request, so folded.operation reaches the sentence in both methods; the ablation of the one create line reads red in both suites (5 and 3 failures), which pins that path as load-bearing.

@objectstack/plugin-security (its exports map has the . entry only, and index.ts does not re-export position-write-through.ts, so PositionMetadataDoor is internal):

  • heldPositionNameRefusal asks the door with operation: 'create'. At this head it is called at exactly two sites — a created row, and a PATCH whose pre.name !== post.name — and an edit that keeps a held name stands down before it, so no edit can receive the create remedy through Setup. The verdict it relays is the door's save verdict. Right; this is the producer of the card's measured symptom, named in the amended claim.
  • The PositionMetadataDoor.packagedBaseRefusal member type widens to match; the real door satisfies it, and no published type moves.

Tests against the triage pin (6093105279): protocol.sealed-remedy-kind.test.ts is the enumeration pin asked for — every reachable type read off the registry, never a transcribed list, for an edit and a create, asserting the remedy KIND off what the sentence points at (the row's own routes or source read from the table, the redeploy remedy, or the type's listing route), with controls for the verdict, a free name, a removal and the 500-character bound, plus the /meta first-write versus unpinned versus If-Match rows for position and flow with the door's message equal to the verdict's (one emitter). position-write-through.test.ts pins the Setup create under a package-held name and a built-in (everyone), a rename into guest, and the S10 relay now against the door's first-write save, with the door's edit refusal as control. A type added to the registry later is iterated the day it lands. Right.

Docs: no page under content/docs restates the edit sentence the diff keeps or the create remedy it adds; permissions/authorization.mdx prescribes the package-source remedy for a posture edit of a sealed permission set, which is an edit and keeps its sentence. The Docs Drift Check's six rows name saveMetaItem and the class, not the sentence. No doc edit owed.

② Semver level

Clause-②: yes

  • Declared yes on PR body line 2 and in .changeset/22591-sealed-remedy-kind.md; the arm is absent, which clause2-line.mjs reads as the same widening as yes (widening). It matches the diff: a public method's input domain grows (packagedBaseRefusal accepts 'create'; tenantAuthoredWriteRefusal takes an optional operation), the accept set does not move, and nothing an author can write is removed or renamed — so no narrowing arm, no ADR-0087 marker and no migration mapping is owed.
  • @objectstack/metadata-protocol: minor — right: a widening takes at least minor.
  • @objectstack/plugin-security: minor — right in what it publishes, over-graded on its own surface. Its published dist changes (the sentence Setup relays on a create), so a changeset is owed. Graded on its own surface it is a patch: no exported type of the package moves, and the gate's level axis would have accepted patch beside metadata-protocol's minor (its discharged state exits 0, printing the residual). Both packages sit in the one fixed release group, so the bump that ships is minor for both whichever word the changeset carries; the declaration is not wrong about what ships, and no change is owed. Check Changeset is green on this head, both runs.
  • skip-changeset is not used, correctly. The changeset body names every door the create remedy reaches and the FROM → TO of the sentence.

③ Boundary flags

Every deviation and open question in 6094519413 and 6094616222, and the amended claim's clauses:

  1. File surface beyond the claim's two protocol.ts sites. (a) plugin-security position-write-through.ts and its test — covered by the claim amendment (6093560675) naming them as the producer side, declared to domain:services. Answered. (b) tenantAuthoredWriteRefusal plus the saveMetaItem first-write derivation — within the card's ask (the /meta door is where the card measured the sentence), and without it a flow would be the one type the first-write pin missed. Answered, right.
  2. sys-metadata-repository.ts unchanged against the claim's file surface — its callers are server replays of the item's own stored history (draft promotion, restore, revert), all edits with no create fact to pass. Answered, right.
  3. The changeset also bumps plugin-security — right (its dist moved; see ②).
  4. Clause-② copied as no with patch in round 1 — superseded by patch round 1 at this head (yes, minor for both). The PR body's declaration line and its Acceptance-notes bullet both read yes now, and the gate-count paragraph is labelled as 3c3e4ef362's. Closed.
  5. Merged origin/main 18d999031b; built eight unrelated packages so check:dual-build-cjs-loads could measure; suites sequenced under one verify-lock call with per-part exits captured — local process notes with no bearing on the diff. Noted.
  6. open_questions[0], yes (widening) versus no — the seat answered A in the amended claim and the dev applied it at caeb7b58f1. Judged right: a new accepted value on an exported class's method is the widening the declaration asks about. Closed.
  7. Round-2 deviations: the bare yes spelling (accepted; one reading), check-changeset-fixed.mjs run beyond the derivation (fine), PR CI not read by the dev (read here, below). Closed.
  8. out_of_scope_findings[0] — escalated to the seat; action owed: Studio's create form (objectui ResourceEditPage create path) sends no If-None-Match: *, so after this PR an unpinned PUT /api/v1/meta/position/NAME from Studio still receives the edit remedy. The server half is this PR; the client half is an objectui card (send If-None-Match: * on create; MetadataClient needs the option), blocked by fix(metadata-protocol): a sealed-item refusal of a create names a free name, not the edit remedy (#22591) #22623 and back-linked to metadata-protocol: a refused save of a package-held position name tells the author to "Edit the source artifact and redeploy", which is wrong for a create, where the remedy is a name no package or built-in holds #22591. Not yet filed. Not a defect of this diff.
  9. out_of_scope_findings[1] (POST /automation keeps the clone/switch remedy — by that door's own contract a create onto an existing name is an overwrite, and the clone door refuses a taken target name first) and [2] (code-only types are refused by codeOnlyOverrideError before the package door, right for a create too) — agreed, no carrier.
  10. Claim clauses: "Stop on a breach" — none reported; the serial constraint (PR feat(plugin-security)!: under single, a Setup position create or rename into a name a package or a built-in holds answers the metadata door's refusal (C2 stage S10) #22582 on main) was cleared at claim time; the feat(metadata-core,metadata-protocol,objectql,plugin-security): the sys_metadata family goes tenant-less; the per-organization overlay axis retires; managed content is sealed (ADR-0131 D6/D7/D13) #15206 S5 read-path regions are untouched. Governed surfaces: none in the file list (Governed Surface Queue Guard green); this record is owed by the claim's widening clause, not by a Tier S path. The PR is still a draft; the seat flips it when it lands.

Check-runs on caeb7b58f1, read at 2026-10-10T06:47Z: 41 runs, all completed — 36 success, 5 skipped (Build Docs, Console Pin Gate, Packed-tarball smoke, and Auto Label plus Check PR Size on the re-triggered run; each skip is a path filter, not a verdict), 0 pending, 0 failed, cancelled or timed out. Green by name: Build Core; Test Core 1/6–6/6 and the aggregate; Lint & Repo Gates; Type Check (workspace, source gates, consumer gates, debt ledger) and TypeScript Type Check; Check Changeset (both runs); Temporal Conformance (live PG + MySQL); Dogfood Regression Gate 1/3–3/3 and the aggregate; Dogfood Verify CLI; Governed Surface Queue Guard; Flag docs affected by code changes; Check Documentation Links; the card-claims-branch, part-of, same-issue and single-writer guards (two runs each); filter. These conclusions are the gate verdicts; nothing was re-run here.

Implemented-by: claude/issue-22591-sealed-remedy-kind
Reviewed-by: session_01Bw3y2DWhT9RPnrmDsNqEVG

VERDICT: PASS


Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/m tests tooling

Projects

None yet

2 participants