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 }}
Repository navigation
ADR-0049 enforce-or-remove: targetVariable on element:text_input / element:record_picker — a declarative hint with zero readers in any repo and no liveness-ledger entry #9198
Filed by the triage seat (scheduled round 2026-08-17) as the enforce-or-remove channel for objectstack-ai/objectui#3834, whose grading history reached this route after its sibling card resolved by retirement.
The declared-but-unenforced shape
packages/spec declares targetVariable on two elements (packages/spec/src/ui/component.zod.ts:1635/:1743/:1810 at 2026-08-17 origin/main; also listed in authorable-surface.base.json and authorable-surface/ui.json). Its own describe text says it is a declarative hint: "Page variable this input writes to. Declarative hint; the live binding resolves via the variable whose source equals this component id (see PageVariableSchema)."
Measured (objectui#3834, re-verified 2026-08-17 against objectui origin/main):
Zero production readers in objectui — the only hits are the reverse-parity gate's exemption block (apps/console/src/__tests__/registry-inputs-spec-parity.test.ts:545-556, citing objectui#3834) and CHANGELOG lines. The live binding runs the other direction (usePageVariableBinding(schema?.id) reverse-looks-up PageVariableSchema.source), which works and matches the spec's own description.
No entry for it in any liveness ledger / ADR-0087 registry (grep across packages/spec, docs/adr, docs/audits — zero hits outside the schema and generated surface files).
Why this card, and why now
The hazard was written on objectui#3834: an author (human or AI) who reads the manifest will write targetVariable, skip the variable's source, and get an input that writes nothing, with zero diagnostics. The card sat as a hold pending a publish-vs-retire pick; the deciding precedent has since landed — objectui#3829, the paired inert-hint card, resolved via the retirement route (objectui PR #4794, commit 183d09b).
The work
Run the ADR-0049 enforce-or-remove flow for these two keys (both shapes identical — one disposition covers both):
Establish the liveness verdict properly (the spec-property-retirement playbook's ledger discipline — the measurement above is a lead, not a ledger row).
If the verdict is dead: retire via the standard route (ADR-0087 conversion, tombstone, generated baselines/forms/docs, pin tests) — the removal decision inside that flow follows its own maintainer gates as usual.
If instead the disposition is "publish as intent metadata": the describe text must state explicitly that authoring it alone binds nothing (objectui#3834 records why the silent-no-op risk makes this the weaker option).
objectui follow-through rides the outcome: drop the parity-gate exemption block either way (objectui#3834 is pm:blocked on this card).
Backlinks: objectui#3834 (origin, full analysis and grading history) · objectui#3829 → objectui PR #4794 (the sibling settled by retirement) · ADR-0049 · ADR-0087.
Dispatch tier: fable (mandatory if the verdict is dead → retirement is a contract accept/reject change; the ledger-verdict half needs the same judgment either way).
Route: the spec-property-retirement playbook end-to-end — ledger verdict FIRST (the card's measurement is a lead, not a row), then the route the verdict dictates. Same-family precedent inherited: the paired inert-hint card (objectui#3829) settled by retirement via objectui PR fix(objectql,platform-objects): 空库自证改在本次启动写完数据之后 —— 一次启动不能证明它随即违反的契约 (#4769) #4794 — not re-asked, per the standing rule.
Hot-file note: retirement implies ADR-0087 conversion + registry.ts regen — no sibling in flight on it (verified this round); relay discipline stands.
Cross-repo follow-through (objectui parity-gate exemption drop) rides the outcome — recorded as owed, dispatched separately after this lands; objectui#3834 stays blocked on this card.
PR #9221 verified; spot-checked on the branch head: retiredKey() tombstones on both elements, the two 18.ui__*__targetVariable retired-key entry files, the element-input-target-variable-removed D2 conversion in the regenerated registry, and element:filter untouched (grep-zero in the diff).
{
"issue": 9198,
"status": "done",
"branch": "claude/issue-9198-targetvariable-enforce-or-remove",
"pr": "https://github.com/objectstack-ai/objectstack/pull/9221",
"pr_number": 9221,
"premise_still_valid": true,
"ledger_verdict": "dead",
"verdict_evidence_summary": "Re-measured against sources at retirement time: objectui origin/main 372d9f9b4 has zero production readers (only the parity-gate exemption block + CHANGELOG; live binding is usePageVariableBinding(schema?.id) reverse-lookup over PageVariableSchema.source); framework repo has only spec's own accept tests; cloud origin/main d8c3c44 has zero hits. No liveness-ledger row exists or is owed — element props schemas are outside the walked metadata-type shapes (the #5775 precedent likewise has no rows; check:liveness green both directions); the verdict is recorded in the retired-key entry files.",
"route_taken": "retire",
"semantic_entry": "not required — D2 conversion `element-input-target-variable-removed` (toMajor 18, retiredFromLoadPath, stripKeys) is the channel; page components are stack collection members, unlike the kernel/Manifest:loading family",
"changeset": ".changeset/element-input-target-variable-retired.md — @objectstack/spec: minor (post-GA launch-window convention, #8586 precedent), FROM→TO + `adr-0087: registered element-input-target-variable-removed` marker",
"files_touched": [".changeset/element-input-target-variable-retired.md", "content/docs/references/ui/component.mdx", "packages/spec/authorable-surface/ui.json", "packages/spec/src/conversions/registry.ts", "packages/spec/src/migrations/entries/retired-keys/18.ui__ElementRecordPickerProps__targetVariable.ts", "packages/spec/src/migrations/entries/retired-keys/18.ui__ElementTextInputProps__targetVariable.ts", "packages/spec/src/migrations/registry.ts", "packages/spec/src/ui/component.test.ts", "packages/spec/src/ui/component.zod.ts", "packages/spec/src/ui/page.test.ts"],
"objectui_followthrough_owed": true,
"tests_added": "2 refusal pins (prescription text, s-flag) + 2 accept-side controls (clean parse materializes nothing) in component.test.ts; conversion fixture (4 notices; nested container, slot, live variables[].source and element:filter controls) replayed by conversions.test.ts; stale page.test.ts fixture re-spelled (dropped #5775's displayField drift + targetVariable)",
"tests": "All at final head e4204118b: spec test 407 files / 10832 passed; spec typecheck green; check:generated 13/13 ✓ (after --fix regenerated only gen:docs); check:liveness / empty-state / skill-examples / variant-docs PASS; check:adr-0087-registration + check:nul-bytes green; lint 2061 passed; dogfood expression-conformance 3 passed (dependency closure built first); dispatch-gates union all PASS incl. type-check-debt and dev-prereqs after full workspace build",
"gates_run": {"spec_test": "pass", "spec_typecheck": "pass", "check_generated": "pass(13/13)", "liveness_audits": "pass", "adr_0087_registration": "pass", "nul_bytes": "pass", "dispatch_gates_union": "pass", "ci": "in_progress (draft PR just opened; PM reads conclusions)"},
"reverse_verification": "Direction decided beforehand: red. Fix committed first; component.zod.ts restored to origin/main tree-only (git restore --source, no staging); exactly the 2 refusal pins went red, 183 others green; restored from the branch commit, 185/185 green, working tree clean",
"deviations": ["dispatch-gates first run used a two-dot diff and picked up one disjoint docs-comment commit that landed on origin/main (#9199, plugin-security) — re-derived from the merge-base set; no merge needed, zero overlap", "spec-changes.json / upgrade guide deliberately unchanged: they carry published majors only (no major-18 entries exist for #8586 either); gates green"],
"open_questions": [],
"risks": ["conversions/registry.ts and migrations step-18 block are hot files — a sibling landing another major-18 conversion will conflict textually in CONVERSIONS_BY_MAJOR[18]/conversionIds; regen-merge discipline applies", "#9220's future disposition of element:filter will need to update this conversion's negative-control fixture"],
"out_of_scope_findings": ["filed as #9220: element:filter declares targetVariable (and possibly the whole element) with no renderer or reader in any repo — element-grain verdict needed before per-key retirement"]
}
Activity
os-zhuang commentedon Aug 17, 2026
Claim: PM seat (spec lane), round 12.
session_01225pUjnCKWqxcc1PeqKFUqclaude/issue-9198-targetvariable-enforce-or-removespec-property-retirementplaybook end-to-end — ledger verdict FIRST (the card's measurement is a lead, not a row), then the route the verdict dictates. Same-family precedent inherited: the paired inert-hint card (objectui#3829) settled by retirement via objectui PR fix(objectql,platform-objects): 空库自证改在本次启动写完数据之后 —— 一次启动不能证明它随即违反的契约 (#4769) #4794 — not re-asked, per the standing rule.registry.tsregen — no sibling in flight on it (verified this round); relay discipline stands.required: trueon amaster_detailreference undercontrolled_by_parent(ruled Direction 2 of #8772) #9138 builder / The publish door discards the runtime authoring gate's advisory findings — and Studio's designer only ever uses that door #9176 protocol.ts).Generated by Claude Code
feat(spec): retire inert targetVariable on element:text_input / eleme…
os-zhuang commentedon Aug 17, 2026
PM review — ACCEPT (round 12, session
session_01225pUjnCKWqxcc1PeqKFUq).PR #9221 verified; spot-checked on the branch head:
retiredKey()tombstones on both elements, the two18.ui__*__targetVariableretired-key entry files, theelement-input-target-variable-removedD2 conversion in the regenerated registry, andelement:filteruntouched (grep-zero in the diff).check:livenessgreen both directions.element:filter's equally reader-lesstargetVariableNOT absorbed — filed as ADR-0049 candidate:element:filterdeclarestargetVariable(and possibly the whole element) with no renderer or reader in any repo #9220 (possibly whole-element grain).Landing flow: flip ready + auto-merge at gate-green.
Generated by Claude Code
os-zhuang commentedon Aug 17, 2026
{ "issue": 9198, "status": "done", "branch": "claude/issue-9198-targetvariable-enforce-or-remove", "pr": "https://github.com/objectstack-ai/objectstack/pull/9221", "pr_number": 9221, "premise_still_valid": true, "ledger_verdict": "dead", "verdict_evidence_summary": "Re-measured against sources at retirement time: objectui origin/main 372d9f9b4 has zero production readers (only the parity-gate exemption block + CHANGELOG; live binding is usePageVariableBinding(schema?.id) reverse-lookup over PageVariableSchema.source); framework repo has only spec's own accept tests; cloud origin/main d8c3c44 has zero hits. No liveness-ledger row exists or is owed — element props schemas are outside the walked metadata-type shapes (the #5775 precedent likewise has no rows; check:liveness green both directions); the verdict is recorded in the retired-key entry files.", "route_taken": "retire", "semantic_entry": "not required — D2 conversion `element-input-target-variable-removed` (toMajor 18, retiredFromLoadPath, stripKeys) is the channel; page components are stack collection members, unlike the kernel/Manifest:loading family", "changeset": ".changeset/element-input-target-variable-retired.md — @objectstack/spec: minor (post-GA launch-window convention, #8586 precedent), FROM→TO + `adr-0087: registered element-input-target-variable-removed` marker", "files_touched": [".changeset/element-input-target-variable-retired.md", "content/docs/references/ui/component.mdx", "packages/spec/authorable-surface/ui.json", "packages/spec/src/conversions/registry.ts", "packages/spec/src/migrations/entries/retired-keys/18.ui__ElementRecordPickerProps__targetVariable.ts", "packages/spec/src/migrations/entries/retired-keys/18.ui__ElementTextInputProps__targetVariable.ts", "packages/spec/src/migrations/registry.ts", "packages/spec/src/ui/component.test.ts", "packages/spec/src/ui/component.zod.ts", "packages/spec/src/ui/page.test.ts"], "objectui_followthrough_owed": true, "tests_added": "2 refusal pins (prescription text, s-flag) + 2 accept-side controls (clean parse materializes nothing) in component.test.ts; conversion fixture (4 notices; nested container, slot, live variables[].source and element:filter controls) replayed by conversions.test.ts; stale page.test.ts fixture re-spelled (dropped #5775's displayField drift + targetVariable)", "tests": "All at final head e4204118b: spec test 407 files / 10832 passed; spec typecheck green; check:generated 13/13 ✓ (after --fix regenerated only gen:docs); check:liveness / empty-state / skill-examples / variant-docs PASS; check:adr-0087-registration + check:nul-bytes green; lint 2061 passed; dogfood expression-conformance 3 passed (dependency closure built first); dispatch-gates union all PASS incl. type-check-debt and dev-prereqs after full workspace build", "gates_run": {"spec_test": "pass", "spec_typecheck": "pass", "check_generated": "pass(13/13)", "liveness_audits": "pass", "adr_0087_registration": "pass", "nul_bytes": "pass", "dispatch_gates_union": "pass", "ci": "in_progress (draft PR just opened; PM reads conclusions)"}, "reverse_verification": "Direction decided beforehand: red. Fix committed first; component.zod.ts restored to origin/main tree-only (git restore --source, no staging); exactly the 2 refusal pins went red, 183 others green; restored from the branch commit, 185/185 green, working tree clean", "deviations": ["dispatch-gates first run used a two-dot diff and picked up one disjoint docs-comment commit that landed on origin/main (#9199, plugin-security) — re-derived from the merge-base set; no merge needed, zero overlap", "spec-changes.json / upgrade guide deliberately unchanged: they carry published majors only (no major-18 entries exist for #8586 either); gates green"], "open_questions": [], "risks": ["conversions/registry.ts and migrations step-18 block are hot files — a sibling landing another major-18 conversion will conflict textually in CONVERSIONS_BY_MAJOR[18]/conversionIds; regen-merge discipline applies", "#9220's future disposition of element:filter will need to update this conversion's negative-control fixture"], "out_of_scope_findings": ["filed as #9220: element:filter declares targetVariable (and possibly the whole element) with no renderer or reader in any repo — element-grain verdict needed before per-key retirement"] }Generated by Claude Code
feat(spec): retire the element:filter element at element grain (#9220,…
feat(spec): retire inert targetVariable on element:text_input / eleme…
feat(spec): retire the element:filter element at element grain (objec…
fix(spec): the protocol 16 → 17 conversion summaries and three descri…
fix(spec): the protocol 17 → 18 conversion summaries state each decis…
test(spec): the second ui/ file group's test titles state each cited …