Repository navigation
/meta/types output derivation: mark an ExpressionInputSchema member whose string arm a transform erased, so a consumer can tell 「erased authoring type」 from 「admits anything」 (ruling batch #196 item 3 letter B on objectui#9830) #19295
Description
Activity
huangyiirene commented
on Sep 20, 2026 CollaboratorMore actionsClaim: PM loop round 7 (wave 2)
Session:session_01NcPSwnmJHczmTu6FG7NMjE
Branch:claude/issue-19295-meta-types-erased-authoring-mark
Worktree:objectstack-issue-19295
Domain:domain:engine
Seat:domain:engine#1
File surface:packages/metadata-protocol/src/(stop on breach; explain in the report)
Container & model:M,mode:subagent,model: default judgment tier— the shape choice carries a lane boundary, and getting it wrong crosses into another seat's tree.
Clause-②: MEASURE IT — see below. ⛔ Not pre-declared by this seat.
Thread-read: the card's Acceptance + Sequencing sections
Serial constraints cleared:all 30 open PRs enumerated and file lists READ. ONE other open PR touches packages/metadata-protocol/src/ — #19385 (card #17676), still open. Region measured from its ACTUAL diff, not from the file name: its protocol.ts hunks are @@ -22328 and @@ -22471 in a 22,507-line file. This card's anchors are toJsonSchemaSafe @ :471 and the served projection @ :6540. ⇒ ~16,000 lines apart, regions DISJOINT. Nothing else in the lane is in flight on this tree.
⭐ Why this is dispatchable at all — the rule under it moved, and this is the first case it pays off
⚠️ Under the old 「同文件单跨轮硬串行」 this card would be blocked behind PR #19385. PR #19317 rewroteSKILL.md:441to 「同区域**」** — the constraint is a claimed REGION, ⛔ not a shared file. Andlanes/engine.md:24governs how a region is cleared: only from the other card's real change extent — with a PR, read the diff; without one, declare UNKNOWN. #19385 has a PR, so the diff was read, and the two regions are ~16,000 lines apart.⇒
⚠️ If you find yourself editing anywhere near:22328–:22500, stop — that is #19385's claimed region and the clearance above no longer covers you.⛔⛔ The fence that decides whether this card is even this lane's
The card's own Acceptance says: "
Clause-②declared from the diff (a new declared key on the envelope ⇒ yes; an annotation on the served projection only ⇒ the claiming seat measures)."⇒ ⭐ The shape you choose decides the LANE.
- Annotation on the served projection only, inside
packages/metadata-protocol/src/⇒ this lane's work. Proceed. - A new declared key on the ADR-0089 envelope ⇒ that lands in
packages/spec/src/**and is clause ②.SKILL.md:512makes a clause-② hit spec-lane work on a single limb. ⛔ STOP. Do not write it. Do not open the PR. Report the measurement and the reason, and this seat routes the card. ⛔ Crossing intopackages/specon this claim is a fence breach, not initiative.
⚠️ Decide this before you write code, ⛔ not after — and say in the report which shape you took and what made the other one wrong.⛔ The pin that must stay green — and what it is guarding
packages/metadata-protocol/src/protocol.meta-types-degenerate-derivation.test.ts:19-28.The global repair (derive everything with
io: 'input') was measured and REFUSED: 24 of 26 types answer differently andrequireddrops 1132 → 867 — a weakening of a published contract. ⇒ ⛔ that pin is not to be touched, relaxed, or routed around. If your mark can only be produced by widening the derivation, you have found the refused answer again and should say so rather than ship it.The acceptance, and the one part that is easy to skip
- Every served member built on
ExpressionInputSchemacarries the mark. - ⭐ A member that derives
{}for another reason carries NONE — the card names this as a negative control and it is the half that makes the mark mean anything. A mark that appears on every{}is indistinguishable from the husk it was supposed to disambiguate. ⛔ Do not skip it and do not report the positive half alone. - The mark is declared once, documented in the reference page, and versioned so objectui can gate on its presence.
⚠️ The consumer reads the mark, ⛔ never the key name. If your design requires objectui to match onhook.condition/visibleWhen/ etc., it is the wrong design.
⚠️ Verify the card's premise against the tree before building on itThe card's measurement comes from objectui's dev reports (
5729362918/5732080628), seat-verified there, ⛔ not re-measured on this tree. Re-derive it here: thatExpressionInputSchema's string arm is a transform, thatz.toJSONSchemaerases it, and that the served member really derivesanyOf: [ {}, {…envelope} ]. If the tree disagrees with the card, follow the tree and say so. ⭐ This lane has had three cards this week whose own instruction would have shipped a broken feature; the order is always the same — verify, then follow the file.Sequencing — ⛔ do not claim to unblock anything
objectui#9830 unblocks when the marked derivation is installable by objectui (the pinned release carries it), ⛔ not merely merged. ⇒ your PR body may say what it enables; it may ⛔ not say objectui#9830 is unblocked.
Harness drift, hand-carried (⛔ the shared checkout is STALE; this is the current rule)
认领带
Clause-②:行才照抄进正文行首:Check Changeset读正文不读卡;无则零写,⛔ 不自造。⇒ this claim does not carry a
Clause-②value — it tells you to measure one. ⭐ Write the line you MEASURED into the PR body at column 0. ⛔ Do not copy the word "MEASURE IT" into the body.Scratchpad
Use a per-issue path:
/tmp/claude-0/issue-19295/. ⛔ Never a shared filename —/tmp/claude-0is shared across parallel devs and same-named files collide silently.
Generated by Claude Code
- Annotation on the served projection only, inside
huangyiirene commented
on Sep 20, 2026 CollaboratorMore actionsos-dev-report
{
"issue": 19295,
"status": "rework",
"branch": "claude/issue-19295-meta-types-erased-authoring-mark",
"pr": "#19399",
"session": "session_01NcPSwnmJHczmTu6FG7NMjE",
"premise_still_valid": true,
"summary": "Shape measured FIRST, per the fence: the annotation on the served projection, not a declared key on the ADR-0089 envelope. packages/spec is untouched, so Clause-2 measuresnoand this stayed inside the engine lane; the envelope key was also the wrong shape on merit, because it would describe the OBJECT arm while the ambiguous node is the sibling arm the derivation erased, and it would ride into the persistence contract where nothing is erased at all. Implemented as one vendor keyword, x-objectstack-erased-authoring-input, carrying {version, type}, emitted by a three-fact structural predicate in packages/metadata-protocol/src/erased-authoring-mark.ts (the node admits everything, the zod node is a ZodPipe, the pipe's input side names a type) and passed as zod'soverrideon both derivations toJsonSchemaSafe can serve. status isreworkfor ONE named reason only: the documentation half of Acceptance item 3 is not delivered. content/docs/references/ is auto-generated from packages/spec TSDoc and may not be hand-edited, and the hand-written content/docs/api/metadata-api.mdx does not document the /meta/types schema derivation at all -- both homes are outside the claim's declared file surface (packages/metadata-protocol/src/), so the PR isPart of #19295and the card stays open for the seat to route that half. The code half is complete and green. The claim's assignee field was already set by the dispatch and was not written by this seat.",
"tests": "pnpm --filter '@objectstack/metadata-protocol^...' build -- VERDICT command-exit 0. pnpm --filter @objectstack/metadata-protocol test -- 'Test Files 183 passed | 3 skipped (186) / Tests 2608 passed | 19 skipped (2627)', 0 failed. pnpm --filter @objectstack/metadata-protocol typecheck -- VERDICT command-exit 0, tsc --noEmit clean. New suite protocol.meta-types-erased-authoring-mark.test.ts -- 12 passed: the structural sweep over every anyOf/oneOf carrying an ADR-0089 envelope (dialect pinned by enum OR const, so typed slots such as job.schedule are not walked past), 7 named members pinned by pointer, the negative-control census, theactioncontrol, the no-constraint assertion and the reader's own refusal cases. GATE FAMILIES: scripts/pm/dispatch-gates.mjs --commands derived 68 after the changeset landed; reconciled with --ran and exit codes -- '68 derived famil(ies) accounted for -- 65 run, 3 NOT-MEASURED'. NOT MEASURED: check:dual-build-cjs-loads, check:lean-entry-closure, check:type-check-debt, each exit 3 refusing its own prerequisite (a fullpnpm build); CI's Build Core supplies it. check:engine-double-contract went red on the new fake engine and was discharged withnode scripts/check-engine-double-contract.mjs --write(+15 lines, 3 rows, 0 lost), then green. REPO-WIDE LINT:pnpm lint(eslint . --no-inline-config) exit 0 on the final tree at 03809f0 -- the full run, not a narrowing. ABLATION 1 (the refused widening is still refused): scripts/ablation-replace.mjs on packages/metadata-protocol/src/protocol.ts, anchor x1 to x0, blob 0d3b02615841 to 6b658f894f7b, adding io:'input' to the served derivation. protocol.meta-types-degenerate-derivation.test.ts went RED with moved = ['action','agent','api', ...(21)] = 24 types against the pinned ['action'] -- so giving that pin's baseline the sameoverridedid not disarm it. Restored: blob == HEAD (0d3b02615841) andgit diff HEADempty. ABLATION 2 (the negative control is not vacuous): anchor x1 to x0 on erased-authoring-mark.ts, blob 5285d2da9104 to de49889d4ff5, making every husk read as an erased pipe arm. BOTH negative assertions went RED -- theastpin and 'actionmarked nodes: expected [ ...(9) ] to deeply equal []'. Restored: blob == HEAD (5285d2da9104) andgit diff HEADempty. No permanent ablation artefact remains. NO BUILD/DIST LEG NEEDED FOR EITHER ABLATION: metadata-protocol's vitest resolves './protocol.js' to src, so the mutation is read from source; only @objectstack/spec resolves through dist, and neither ablation touched it. SURFACE CENSUS (served derivation, this tree): 78 marked arms across 8 types (app, field, flow, hook, job, object, page, view), 187 empty-schema nodes left unmarked, 0 marks onaction.",
"mcp_calls": "0 -- no MCP GitHub tool was called, read or write.",
"api_writes": "2 -- POST /repos/objectstack-ai/objectstack/pulls (draft PR #19399), POST /repos//issues/19295/comments (this report). Zero label writes, as dispatched. Reads (GET on the card, the claim comment, the comment list and the PR body read-back) are not writes.",
"open_questions": [
{
"question": "Acceptance item 3 asks the mark to be 'documented in the reference page'. On this tree there is no reference page this lane may write: content/docs/references/api/rest-server.mdx names GET /meta/types but is AUTO-GENERATED from packages/spec TSDoc and carries a DO NOT EDIT banner, and the hand-written content/docs/api/metadata-api.mdx documents no schema derivation at all. Both are outside the claim's declared file surface. Where should the mark be documented?",
"options": [
"A -- a docs-only follow-up on content/docs/api/metadata-api.mdx (hand-written, no generator, needs its own file-surface clearance against the docs lane)",
"B -- a spec-lane follow-up that puts the sentence in the packages/spec TSDoc the reference page is generated from, so the generated page carries it (crosses into packages/spec, which is exactly the fence this card was dispatched under)",
"C -- the package's own README (packages/metadata-protocol/README.md), which ships inside the npm tarball an upgrading agent greps, but is not 'the reference page' the card names"
],
"recommendation": "A, because the mark is a property of what the PROTOCOL serves, not of a spec schema, so B would put protocol prose inside the spec's generated surface and buy a spec-lane crossing for a sentence. A is a one-page docs-only PR with no generator and no gate to satisfy. C is worth doing beside A but does not answer the card on its own."
}
],
"out_of_scope_findings": [
"noted, not filed: flow.nodes.items derives an EMPTY schema on the served output derivation -- a whole-member husk of the #17501 class one level below the type, so the property panel for a flow's node list is told nothing about a node's shape. The mark now labels it (erased authoring typeobject) rather than leaving it anonymous, which is why it is visible at all. Making it derive is a different change with a different blast radius. Carrier: no open PR touches that member today, and the mark makes the gap legible in the payload rather than silent, so filing it now would be a card nobody is about to pick up.",
"noted, not filed: the card namesExpressionInputSchemafor the served predicate slots; on this tree they areEvaluatedExpressionInputSchemasince #15807/#15811, withExpressionInputSchemaand its aliasPredicateInputSchemaleft as the persistence contract. Structurally identical for this card (both are two-arm unions over a string ZodPipe) and the implementation keys on the STRUCTURE rather than on either name, so nothing is wrong to fix -- it is a wording drift in the card body, not a defect.",
"noted, not filed:sharing_ruleis on the card's list of served predicate slots but resolves no zod schema in the registry-only harness (it is plugin-registered), so it is covered by the structural sweep wherever it is served rather than pinned by name. Not a defect, and not a gap in the mark."
]
}
Generated by Claude Code
huangyiirene commented
on Sep 20, 2026 CollaboratorMore actionsACCEPT — the code half of PR #19399. ⛔ The card stays open, and the doc half needs a ROUTE, not a rider.
domain:engine#1, round 7,session_01NcPSwnmJHczmTu6FG7NMjE. Written 2026-09-20T16:24Z. Head03809f0972. Reviewed againstorigin/main; every reading below is this seat's own.⭐ The dev measured the shape BEFORE writing code, which is exactly what the fence asked for — and the measurement is what kept this card in this lane.
⛔ The fence — verified mechanically, ⛔ not taken on the report's word
The claim said: an annotation on the served projection is this lane's; a new declared key on the ADR-0089 envelope lands in
packages/spec/src/**⇒ clause ② ⇒SKILL.md:512makes it spec-lane work on a single limb ⇒ STOP.$ git diff --name-only <merge-base> origin/pr-19399 | grep '^packages/spec/' (zero)⇒ ✅ ZERO
packages/specpaths.Clause-②: nois a measured answer here, ⛔ not a copied one.⭐ And the dev refused the envelope key on merit as well as on scope, which is the stronger reason: the key would describe the object arm while the ambiguous node is the sibling arm the derivation erased, and it would ride into the persistence contract where nothing is erased at all. ⇒ the fence and the design agree; ⛔ this was not a seat rule overriding a better answer.
⚠️ The pin that guards a REFUSED widening was modified — and it was NOT loosenedprotocol.meta-types-degenerate-derivation.test.tsis the pin standing between this repo and the blanketio: 'input'widening that was measured and refused (24 of 26 types answer differently;required1132 → 867). A diff touching it is exactly the shape that deserves the 「read the half it did NOT touch」 test, so this seat read the diff rather than the summary:- What changed: the baseline side now derives with the same
overridethe server passes — the emitter's own function, ⛔ never a second spelling — plus a header section and an import. - What did not change: the assertion itself.
movedvs the pinned['action']is ⛔ not in the diff at all.
⇒ this is symmetrisation, ⛔ not relaxation: both sides of the comparison are now derived by the same code, so what is left to find is again exactly the degeneracy retry's blast radius.
⭐ And it is proved rather than argued — ablation 1 mutated the served derivation to add
io: 'input'(blob0d3b026158→6b658f894f, restored to blob-equality withHEADand an emptygit diff HEAD) and the pin went RED with 24 moved types against the pinned one. ⇒ giving the baseline the sameoverride⛔ did not disarm it.⭐ The negative control is real, and it was ablated too
Acceptance item 2 is the half that makes the mark mean anything — a member deriving
{}for another reason must carry none. Census on this tree: 78 marked arms across 8 types · 187 empty-schema nodes left unmarked · 0 marks onaction.Ablation 2 made every husk read as an erased pipe arm (blob
5285d2da91→de49889d4f, restored and verified) and both negative assertions went red. ⇒ the control ⛔ is not vacuous, and 187-vs-78 is a discrimination rather than a coincidence.⭐ Neither ablation needed a build/dist hop, and the dev said why rather than assuming:
metadata-protocol's vitest resolves./protocol.jstosrc, so the mutation is read from source; only@objectstack/specresolves throughdist, and neither ablation touched it.⚠️ It touched the silent-loss path — and took the safest possible shapescripts/engine-double-contract.pinned.jsonis the file this lane's seat post flags as a relay hazard:git check-attr mergereadsunspecified, so it is ⛔ not routed to themerge=os-regendriver, and two branches adding rows would merge exit 0, no conflict marker, one side silently lost.Measured by this seat:
reading result other open PRs claiming that path ZERO (all open PRs' file lists read) the change's shape git diff --numstat⇒ +15 / −0 — purely additivetop-level rows 2 → 2, ⛔ none lost how it was produced node scripts/check-engine-double-contract.mjs --write— the repo's own tooling, ⛔ not by hand⇒ ⭐ a pure addition is the one shape that cannot lose another side's rows in that merge, so the hazard is not triggered even in principle here.
PR form
draft ✅ · first line
Part of #19295✅ ·Clause-②: noat column 0 ✅ · one footer ✅ · zero closing-keyword bindings anywhere in the body, scanned under GitHub's own grammar ✅ · zero model identifiers in body or commits ✅ · region disjoint from in-flight PR #19385 (:23/:475/:498/:518vs:22328/:22471) ✅.⚠️ Gate honesty, recorded as the dev recorded it68 families derived, 65 run, 3 NOT-MEASURED —
check:dual-build-cjs-loads,check:lean-entry-closure,check:type-check-debt, each exit 3 refusing its own prerequisite (a fullpnpm build). ⛔ Recorded as NOT MEASURED, ⛔ never as passes; CI'sBuild Coresupplies that prerequisite.check:engine-double-contractwent red on the new fake engine and was discharged by regenerating, ⛔ not by editing the pin.Repo-wide
pnpm lintexit 0 on the final tree — the full run, ⛔ not a narrowing.⛔ What is NOT delivered — Acceptance item 3's documentation half
The dev reports it as undelivered and names why. This seat verified both halves of that claim on
origin/mainrather than accepting it:claim reading content/docs/references/api/rest-server.mdxis generated✅ its banner, verbatim: ⚠️ AUTO-GENERATED — DO NOT EDIT. Run build-docs.ts to regenerate. Hand-written docs live in the module folders under content/docs/.content/docs/api/metadata-api.mdxexists but documents no schema derivation✅ the file exists; and the hand-written pages that mention meta/typesareerror-catalog.mdx— ⛔metadata-api.mdxis not among them⇒ the card's acceptance is not met,
Part ofis the correct verb, and #19295 stays open.⚠️ The doc half is CROSS-LANE ⇒pm:retriage, ⛔ not a seat decisioncontent/docs/**belongs to another lane by the lane table. ⇒ this seat ⛔ cannot claim it and ⛔ will not write adomain:*label. Asking triage to split the card: the code half is delivered and landing; the documentation half is a one-page docs card in the owning lane.This seat's recommendation, with the reason rather than a vote — option A: put the sentence in the hand-written
content/docs/api/metadata-api.mdx.- ⛔ B (the spec TSDoc the reference page is generated from) is refused: it buys a
packages/speccrossing — the exact fence this card was dispatched under — for one sentence, and it would put protocol prose inside the spec's generated surface. The mark is a property of what the protocol serves, ⛔ not of a spec schema. - ⭐ The generated page's own banner argues for A: "Hand-written docs live in the module folders under
content/docs/." The tree is telling the reader where this belongs. - C (the package README) is worth doing beside A — it ships inside the npm tarball an upgrading agent greps — but ⛔ it is not "the reference page" the card names, so it does not answer the acceptance alone.
Two card-body drifts the dev found, ⛔ neither a defect
- The card names
ExpressionInputSchema; on this tree the served predicate slots areEvaluatedExpressionInputSchemasince spec/automation:FlowEdgeSchema.conditionstill accepts an envelope the engine cannot evaluate (ast-only, whitespace-onlysource) — the evaluated-slot rule of #15430 has not reached the edge condition #15807/spec: the evaluated-slot rule of #15430 reaches only the flow-node ledger — every otherExpressionInputSchemaslot an engine evaluates (formulaexpression, validation / hook / sharingcondition,visibleWhen…) still accepts anast-only or blank-sourceenvelope #15811, withExpressionInputSchema(and its aliasPredicateInputSchema) left as the persistence contract. ⭐ The implementation keys on the structure, ⛔ not on either name, so nothing is wrong to fix — a wording drift, ⛔ not a defect. sharing_ruleis on the card's list but resolves no zod schema in the registry-only harness (it is plugin-registered) ⇒ covered by the structural sweep wherever it is served, ⛔ not pinned by name.
Recorded, ⛔ not filed
flow.nodes.itemsderives an empty schema on the served output derivation — a whole-member husk of the #17501 class one level below the type, so a flow's node-list property panel is told nothing about a node's shape. ⭐ The mark now labels it (erased authoring typeobject) rather than leaving it anonymous, which is the only reason it is visible at all. Making it derive is a different change with a different blast radius. 承接者:无 — no open PR touches that member, and the mark makes the gap legible in the payload rather than silent.
Generated by Claude Code
- What changed: the baseline side now derives with the same
- addedpm:retriageQuestion for triage, answered each fire; coexists with the standing pm:* label; no dispatchQuestion for triage, answered each fire; coexists with the standing pm:* label; no dispatchand removed
on Sep 20, 2026 huangyiirene commented
on Sep 20, 2026 CollaboratorMore actionsLANDED — PR #19399, verified by two readings. ⛔ The card stays open for its documentation half.
domain:engine#1, round 7. Written 2026-09-20T17:09Z.reading result squash on origin/main0870fb5418f9d1ac0a317617209669648d4469e0— parent count 1 (git show -s --format='%p' | wc -w), and--is-ancestor origin/main⇒ YEScontent markErasedAuthoringInputpresent inprotocol.ts; the new moduleerased-authoring-mark.tsexists on the treethe refused widening protocol.meta-types-degenerate-derivation.test.ts:270still readsexpect(moved).toEqual(['action'])— byte-identical, green in CI⛔ Not judged by the merge event, and not by
merge_commit_sha— see the note below on why that field is worse than the merge event.⚠️ A negative control this seat wrote BADLY, recorded because the lesson is the seat's ownTo check the refused
io: 'input'widening had not landed, this seat grepped the raw string overprotocol.tsand asserted it must be 0. It returned 3. ⛔ The finding was the control, not the tree::441and:484are docblock prose — and:484's sentence is literally "the refusedio: 'input'widening stays refused.":520is a real call, in the separate authoring retry that ⛔ pre-existed this PR. The diff shows it: the old one-liner was removed and re-emitted multi-line only to accept the newoverride. Net new occurrences: one, and it is prose.
⭐ The seat post already carries this rule and this seat re-earned it: 「a control must be able to tell apart the two cases it is invoked to tell apart」. A raw-string grep over a 22,507-line file cannot distinguish "the widening landed" from "the docblock discusses the widening". ⇒ the correct control is the pin, unchanged and green — and that is the one that actually answers.
⛔ What is still owed — the card does NOT close
Part of #19399was the right verb: Acceptance item 3's documentation half is undelivered, because ⛔ no reference page this lane may write exists (content/docs/references/api/rest-server.mdxcarries a verbatimDO NOT EDITbanner;content/docs/api/metadata-api.mdxdocuments no schema derivation, and this seat confirmed it is ⛔ not among the hand-written pages that mentionmeta/types).⇒ state is now
pm:queue+pm:retriage, unassigned — the documented combination for a card whose routing question is open (apm:queuecard carryingpm:retriageis skipped for dispatch).pm:dispatchedwas removed by hand at the landing, ⛔ not left to a later patrol, because aPart ofmerge closes nothing.⚠️ The ask to triage is unchanged and is on this card: split the documentation half into the lane that ownscontent/docs/**. This seat recommends option A (the hand-writtenmetadata-api.mdx) and ⛔ refuses B, which would buy apackages/speccrossing — the exact fence this card was dispatched under — for one sentence.
Generated by Claude Code
- added a commit that references this issue
on Sep 28, 2026
Path: P1 | 那条路第 1 步「写元数据」 |
/meta/types把 string 臂擦成{},消费方分不清「被擦掉的作者类型」和「什么都收」分诊重测与定级:2026-09-20T15:25Z
Filed by the director seat, summon #25 (
session_012GcsUbuqFGBibkEDMRC1eE), executing ruling batch #196 item 3 letter B on objectui#9830 (maintainer 「同意」). ⛔ Filed bare:domain:*,priority:*andtypeare triage's production. Landing:packages/metadata-protocol(toJsonSchemaSafe/ the served projection) and, if the mark rides the envelope,packages/spec(ADR-0089 envelope). objectui#9830 carriesBlocked-by:on this card.The gap, measured by objectui (dev report 5729362918 / 5732080628, seat-verified)
Every predicate key the platform serves —
hook.condition,sharing_rule.condition,field.visibleWhen/readonlyWhen/requiredWhen, flowedge.condition— isExpressionInputSchema, a union whose string arm is a transform. In the output derivation/meta/typesserves,z.toJSONSchemaerases the transform's input type, so the member derives asanyOf: [ {}, { …ADR-0089 envelope } ]. On the wire{}is indistinguishable from 「admits everything, strings included」, so the metadata designer cannot tell an erased string predicate from a member that genuinely accepts anything; it vetoes the condition builder for all of them (objectui PR #9911,admitsString()), and an author only gets the builder by pinningwidget: 'condition'in the form spec.⛔ The global repair — derive everything with
io: 'input'— was measured across the served surface and refused as a weakening of a published contract (24 of 26 types answer differently;required1132 → 867), with a pin guarding it:packages/metadata-protocol/src/protocol.meta-types-degenerate-derivation.test.ts:19-28. ⛔ Not reopened here.Ruling this card executes
The served derivation carries a machine-readable mark for a member whose authoring arm a transform erased — either on the ADR-0089 envelope object (a declared key naming the erased authoring type, e.g. that a string source is accepted) or as an annotation the projection emits beside the husk arm for
ExpressionInputSchemamembers. The consumer reads the mark, ⛔ never the key name. Nothing else in the output derivation moves; the refused global widening stays refused and its pin stays green.Acceptance
ExpressionInputSchema, the derived document carries the mark; a member that genuinely derives{}for another reason carries none (negative control).io: 'input'widening).Clause-②declared from the diff (a new declared key on the envelope ⇒yes; an annotation on the served projection only ⇒ the claiming seat measures).Sequencing
objectui#9830 unblocks when the marked derivation is installable by objectui (the pinned release carries it), ⛔ not merely merged; then
admitsString()reads the mark and the husk-boundary pin inverts for marked members only.Dedupe words:
ExpressionInputSchema transform erased string arm·meta/types husk anyOf empty envelope·erased authoring type mark·degenerate derivation refused io input·objectui#9830Generated by Claude Code