Repository navigation
[finding] Untitled repeater row properties: dashboard.widgets (17) and dashboard.globalFilters (10) — fence dropped, dispatchable now #17505
Description
Activity
- addedbugSomething isn't workingSomething isn't workingand removed
on Sep 10, 2026 Triage:
domain:specconfirmed; typeBug,priority:p3,pm:queue.Fence independently re-verified, not taken from the card. It was held by #17474 — read 2026-09-10T19:30:55Z:
closed, and the card's own probe re-run on currentorigin/maingivesgit grep -c 'only widget type' -- packages/spec/src/ui/dashboard.zod.ts= 1, matching what the card measured. ⇒ Dispatchable.Carriers:
dashboard.widgets(17) anddashboard.globalFilters(10) inpackages/spec/src/ui/dashboard.zod.ts.⚠️ SixretiredKey()tombstones are excluded from those counts (5 ondashboard.widgets) and are #17502's, which this seat graded p2 earlier today. ⛔ Do not title a tombstone — a titled tombstone is a better-presented offer of a key the parse door refuses, which is worse than an untitled one.The family, graded as one shape
All three are
Part of #17232(pm:dispatched, p2) and share the mechanism that makes them measurable —packages/spec/src/kernel/repeater-item-titles.test.ts, an exact, shrink-only ledger: a carrier absent from it must be fully titled (a new untitled repeater reds on arrival), and a carrier present in it must still be untitled (a paid debt must be deleted from the ledger).⭐ That ledger is why these are p3 rather than anything worse: the debt cannot grow. A new untitled repeater reddens immediately; what remains is a fixed, enumerated backlog.
⇒ Each card is one carrier set = one slice = one changeset. ⛔ Do not fold them into one dispatch: they touch three different files, and folding gate ② (same package/area, one worktree, one changeset) fails on the file face even though the package is shared.
⚠️ Counts were derived through the platform's own predicate (z.toJSONSchemaovergetMetadataTypeSchema), ⛔ not by regexing source. Re-derive the same way; a source regex will disagree and the ledger will red.priority:p3: raw property keys as column headers in the Studio property panel. Nothing behaves wrongly, no data moves, and the reader is a metadata author looking at a panel — not an end user.type Bug: the panel shows an identifier where the schema means to show a title.Triage seat ·
session_017VGfRocA8VjczSe84fgjY3· R+171 · 2026-09-10T19:31Z (timestamp taken in the same tool call that posts) · comment from the triage seat
Generated by Claude Code
Claim:
domain:specexecution seat, sessionsession_01KB5PFtxuy1x3dcR5gxudx6, 2026-09-16T16:07Z. Assignee set in the same label write (pm:queue→pm:dispatched,needs:contract-reviewhung, read back and matched). Theos-devround inherits this claim and this assignee — ⛔ it posts no secondClaim:and ⛔ never writes the assignee field.Branch:
claude/issue-17505-dashboard-repeater-row-titlesClause-②: yes, carrier hung on the card now and on the PR the moment it exists — ⛔ by this seat, never by the dev. Declared
yeson the 「拿不准就挂」 rule, ⛔ not because the seat is certain:.meta({ title })adds no authorable key, but it does put a new key into the emitted JSON Schema, which is a published artifact. That is exactly the call the in-seat contract review exists to make, so it goes there rather than being settled here.⚠️ If the review's measurement saysno, that is a correction to this seat, ⛔ not to the round.⭐ This is application, not design — the mechanism is ruled and already landed twice
#16458 ruled mechanism A; PR #17227 built both halves of it and PR #17500 applied it to three more carriers. ⇒ this round needs no new contract decision, and it has a landed pattern to copy rather than invent.
Staleness pre-check on
origin/mainbf61f0a196(2026-09-16T16:07Z) — all three facesface reading 工作项面 .meta({ titleinpackages/spec/src/ui/dashboard.zod.ts= 4, and reading them shows all four are #17227'sheader.actionsrows (label,actionUrl,actionType,icon) ⇒dashboard.widgetsanddashboard.globalFiltersare still untitled, as this card says. Dark controlmeta({ zztitlezz= 0 (exit 1)卡引用面 the fence this card was filed behind — #17474 — has LANDED ( c7af6bd6f9on the tree), which is what made it dispatchable; parent #17232 is open and stays open (its PR declaredPart of, notFixes)动作面 ⚠️ dashboard.zod.tsmoved twice since this card was written:#17616(fix(spec)!: refuse dashboard.widgets[].options.stageOrder on every widget type that does not read it) and#17474. Both touch the widget schema⚠️ ⇒ the card's count of 27 row properties is a READING FROM 2026-09-10, ⛔ not a specification.#17616is a refusal that removed a key from some widget types. The round must re-derive the row-property set on today's tree and report the number it actually finds. If it is not 27, the round's number is right and the card's is stale — say so; that is a good outcome, ⛔ not a discrepancy to reconcile away.⛔ The 6
retiredKey()tombstones must NOT be titled — they are #17502's subject. A tombstone declares a key unwritable; giving it an authoring label would advertise it as writable.File face declared
packages/spec/src/ui/dashboard.zod.tsand whatevercheck:generatedregenerates from it. ⛔ Nothing outside without re-declaring here first. Declared NOT touched:packages/spec/src/ui/view.zod.tsanddataset.zod.ts(#17507's carriers),field.zod.ts/object.zod.ts(#17506's), the translation catalog (#17508, a decision card), andpackages/spec/authorable-surface.base.json(an in-flight anchor-only PR owns that file — this change should move no authorable key; if the measurement says otherwise, stop and report).Batch independence: dispatched alongside #18095, #18056 and #17235. File faces measured disjoint at claim time (
packages/lint/scripts/check-reference-carrier-shape.mjs·packages/spec/src/system/email-template*·packages/spec/src/api/auth.zod.ts). ⛔ Not folded with #17506 or #17507 — they were deliberately split when their fences dropped independently, and folding them now would re-create the serialisation that split avoided.Verify-lock: read at claim time —
queue: empty, arrival depth 1 (<LOCK_DEPTH_HOLD= 2). ⛔ Re-read before the first heavy run; exit 99 is NOT MEASURED, not red.
⚠️ Standing instruction — every item above is a premise for you to FALSIFY first. A measured 「already fixed / does not reproduce / the card's own claim is false」 is a good outcome and ⛔ is not a failed round. ⛔ Closing the card is the seat's act, never yours.⭐ A count or a zero is not a reading until you look at what it matched. Lit control (> 0) and dark control (0) on every absence claim, same-subject.
⚠️ grep -ccounts LINES not occurrences — and on this card that matters, because several titled properties can sit on one line.⚠️ grepis line-oriented so a wrapped declaration gives a FALSE zero (grep -zfirst).⚠️ Read$?by redirect-then-$?, ⛔ never through a pipe.⚠️ A broken instrument's exit code is not a reading: exit 3 is PREREQUISITE NOT MET, exit 99 is NOT MEASURED, a crash is not a red gate, a timeout-wrapper kill is not a verdict.⭐ A prior seat's own recorded mistake on this exact card, worth inheriting: its probe
git grep -c 'repeater-item-titles' -- packages/spec/src/kernel/read 0 and was nearly read as 「not landed」 — but that string is the pin's file NAME, not text inside it, so the grep was searching contents for something that exists only as a path. 「A zero with a structural cause is not a measurement of absence.」 Checkgit ls-treebefore concluding a file's contents are missing.
Generated by Claude Code
Clause-②-correction: 5700636082
Clause-②: yes
Session: session_01KB5PFtxuy1x3dcR5gxudx6⛔ Not a second
Claim:— the claim stands; only its declaration limb is being re-declared in the fixed spelling. Filed before this card's PR exists, so its enqueue gate never sees the malformed shape.Why, and it was mine
My claim comment
5700636082opened a line with the key and then kept talking:Clause-②: yes, carrier hung on the card now and on the PR the moment it exists — ⛔ by this seat, never by the dev.
Measured on the sibling card #17235, whose claim carried the same shape:
check-clause2-carriers.mjs --pair 18501→ exit 4, row C2 — 「that line QUOTES the spelling rather than declaring a value … ⛔ A quotation of the protocol is not a judgement about this diff」. The gate tells them apart by markdown structure, never by words, and it is right to.⇒ I am correcting this card on the same ground rather than waiting for its PR to open and reproduce the failure. ⛔ Same mistake, same shift, twice — both from composing the line from memory instead of copying the template in
.claude/skills/pm-dispatch/SKILL.md〈模板与表〉.The substance is unchanged
Clause-②: yeswas and is the value, declared on the 「拿不准就挂」 rule:.meta({ title })adds no authorable key, but it does put a new key into the emitted JSON Schema, which is a published artifact. ⛔ That call is the in-seat contract review's to make, not this line's — and if the review measuresno, that corrects this seat, ⛔ not the round.needs:contract-reviewis on the card, hung by this seat; the PR side is hung the moment the PR exists. ⛔ The dev neither hangs, strips nor waits on it.
Generated by Claude Code
os-dev-report
{ "issue": 17505, "session": "session_01KB5PFtxuy1x3dcR5gxudx6", "status": "done", "branch": "claude/issue-17505-dashboard-repeater-row-titles", "pr": "https://github.com/objectstack-ai/objectstack/pull/18505", "premise_still_valid": true, "summary": "Added `.meta({ title })` to all 27 authorable row properties of the two repeater carriers in packages/spec/src/ui/dashboard.zod.ts (dashboard.widgets 17, dashboard.globalFilters 10), and DELETED both paid entries from the shrink-only LEDGER in packages/spec/src/kernel/repeater-item-titles.test.ts. THE CARD'S COUNT IS NOT STALE: re-derived on today's tree through the platform's own predicate it is still 17+10=27. #17616 removed `stageOrder` from widget types that do not read it, but that key lives INSIDE `options` -- one level below the repeater's row properties, where `options` itself is the single row property -- so a refusal at that depth cannot move this count. TOMBSTONES: exactly 5 on these two carriers, all on dashboard.widgets (actionUrl, actionType, actionIcon, responsive, aria); globalFilters holds 0. None was titled; all 5 still emit title=undefined in both io modes. ONE IN-SCOPE COLLATERAL EDIT: dashboard.test.ts used `widgets[].id` as its 'a sibling with no authored title has none' dark control, which this change titles. It was RE-POINTED onto a tombstone (widgets[].actionUrl) and given a lit leg (widgets[].id is now 'Widget ID') rather than deleted, so it now pins the tombstone rule instead of borrowing a coincidence. FILE FACE NOTE: the dispatch declared the face as dashboard.zod.ts plus whatever check:generated regenerates. Two files outside that face were necessarily touched and are reported here rather than taken silently -- the LEDGER (the card's own stated second half: 'then remove this carrier's ledger entry'; leaving it is as red as never paying, the pin being exact in both directions) and the dashboard.test.ts control above. dashboard.form.ts was deliberately NOT touched: enumerating repeater children is the i18n-catalog half and belongs to #17508; the spec-side pin holding a form label equal to a schema title is scoped to header.actions only. FENCE HELD: check:authorable-surface is green and packages/spec/authorable-surface.base.json did not move and was not touched, so the in-flight anchor-only PR that owns it is unaffected. HARNESS DIVERGENCE: the container's .claude/agents/os-dev.md is STALE vs origin/main in exactly 7 line-regions (L51, L53, L287, L301, L366, L370, L380) -- the expected count. origin/main was followed throughout. CLAUSE-2 CARRIER: not hung, not stripped, not waited on, per the L287 reversal.", "tests": "ALL under the shared verify lock where heavy, slot OS_VERIFY_LOCK_SLOT=issue-17505-dev, verdict read from the wrapper's own 'VERDICT command-exit' line, never a bare $?. GREEN: `pnpm --filter @objectstack/spec build` VERDICT command-exit 0 (141s). `pnpm --filter @objectstack/spec test` VERDICT command-exit 0 -- 483 test files / 13773 tests passed. `pnpm --filter @objectstack/spec typecheck` VERDICT command-exit 0 (includes check:scripts-typecheck and check:test-typecheck: 54 files / 259 errors / 144 pinned signatures held). `pnpm --filter @objectstack/spec check:generated` exit 0 -- ALL 15 generated artifacts up to date, check:authorable-surface among them. packages/rest meta-types-schema-titles.test.ts 3/3 passed; packages/platform-objects translations 8 files / 100 tests passed. 41 further gate commands run individually with per-command exit capture (redirect-then-$?, never through a pipe): all exit 0 except the two below. BEFORE/AFTER ON THE EMITTED JSON SCHEMA (not on src -- the card's whole point): derived with the helper functions copied BYTE-IDENTICAL out of repeater-item-titles.test.ts so the probe cannot drift from the pin, reading the property node FIRST and never the $ref target (a .meta title on a $defs-hoisted schema is emitted as a sibling of the $ref). dashboard:widgets untitled 17 -> 0; dashboard:globalFilters untitled 10 -> 0; widgets.items.properties.id.title undefined -> 'Widget ID' in BOTH io:'input' and io:'output'. DARK CONTROLS, same subject: all 5 tombstones title=undefined before AND after, both io modes; dashboard top-level `columns` and `header.showTitle` still untitled (which is what keeps the two absence controls in packages/rest valid). NOTHING GOT A TITLE THAT SHOULD NOT: in the same AFTER run all 20 other repeater carriers hold their exact prior counts -- field:options 6, object:fields.options 6, view:columns 14, view:sort 2, view:tabs 9 still untitled -- so no shared item schema leaked a title sideways. LEDGER DIRECTION CHECK (lit/dark on the pin itself, via --reporter=verbose): dashboard:widgets and dashboard:globalFilters now execute WITHOUT the '(ledger: still owed titles)' arm while the 5 survivors keep it; because the pin is exact in both directions, this run would have gone RED had the titles landed without the ledger deletion, or the deletion without the titles. STRUCTURAL-ZERO GUARD: the prior seat's recorded mistake was re-checked -- `repeater-item-titles` is a FILE NAME, so `git ls-tree` (lit: file present) was used rather than a contents grep (which reads 0 for a structural reason). EDIT PROOF: all 27 anchors were content-anchored and count-verified (each hit exactly 1, a 0-hit aborts), then proven on disk -- .meta({ title OCCURRENCES (grep -o, NOT grep -c which counts LINES) went 4 -> 31 = 4 pre-existing + 27 new; grep -naP control-character scan clean; check:nul-bytes exit 0. SEAT PRE-CHECK: CONFIRMED, not falsified -- .meta({ title in dashboard.zod.ts = 4 at bf61f0a196 (occurrences AND lines both 4), all four #17227's header.actions rows; dark control meta({ zztitlezz = 0, exit 1. NOT MEASURED (marked, not assumed green): `pnpm check:type-check-debt` exit 3 = PREREQUISITE NOT MET, which its own text distinguishes from a finding's exit 1 -- it wants a whole-monorepo turbo build first, a farm-scale run CI owns; NOT a red. `pnpm check:cross-package-test-inputs` exit 1 -- MEASURED NOT MINE, see findings. 56 of 85 derived families UNRUN per `dispatch-gates.mjs --ran` -- predominantly whole-repo scans CI owns (org-identifier, where-matcher, slot-lookup, driver-memory-census, dts-closure and the like); reported as NOT MEASURED, not assumed green. Gate families were derived from the real changeset twice, before and after a fresh `git fetch origin main`, and the two lists are BYTE-IDENTICAL, so the tool's STALE TREE warning did not move the answer. No `pnpm lint` repo-wide run: CI owns it.", "mcp_calls": "0 - no MCP GitHub tool was called at all, neither read nor write; every GitHub read and write went through the REST proxy with curl and $GITHUB_TOKEN", "api_writes": "2 - POST /repos/objectstack-ai/objectstack/pulls (draft PR 18505, HTTP 201); POST /repos/objectstack-ai/objectstack/issues/17505/comments (this report). ZERO label writes by design: per the origin/main harness L287 reversal and the dispatch, needs:contract-review is the SEAT's carrier - neither hung, stripped nor waited on. Reading only: it was on the card throughout; at 16:33Z `check-clause2-carriers --pair 18505` exited 4 (card carried it, PR bare - a half-written pair); re-read after the seat hung it on the PR side, the same command exits 0 with both carriers agreeing. Git pushes are not REST writes: 2 (empty branch as the write-routing probe, then the commit).", "open_questions": [], "out_of_scope_findings": [ "to file (class (a) reproducible defect; dedupe words: cross-package-test-inputs, init-created-files-summary, spec/dist, declared glob, 7802) - `pnpm check:cross-package-test-inputs` reds as a function of BUILD STATE rather than of any diff. Measured, not inferred: in a clean control worktree at bf61f0a196 with ZERO source changes the gate exits 0; `mkdir -p packages/spec/dist && echo 'export {};' > packages/spec/dist/index.js` then flips it to exit 1 with the identical finding ('rooted in packages/cli/test/init-created-files-summary.e2e.test.ts' / 'packages/spec/dist/ (no declared glob reaches inside it)'); removing dist restores exit 0. So it is green on an unbuilt checkout and red on a built one, and none of the named paths is in this diff. Its own --self-test is fully green, so the checker is healthy - this is the real scan.", "noted, not filed: the card body and its triage comment both say 'Six retiredKey() tombstones are excluded from these counts (5 on dashboard.widgets)'. Re-derived on today's tree, the two carriers THIS card owns hold exactly 5, all on dashboard.widgets, and dashboard.globalFilters holds 0 - so the sixth is on neither carrier of this card (the pin itself names flow:nodes.outputSchema as a tombstone, the likely sixth in the parent's family accounting). Not an error in the work, only in the count's attribution. 承接者: #17502, whose subject is the tombstone set.", "noted, not filed: `check-clause2-carriers --pair` emits 'ATTRIBUTION NOT VERIFIED' on this card - the governing claim comment carries no `Session:` line, so its Clause-2 correction comment has nothing to be compared against. The reading stands and the pair is green; the omission is the claim's. 承接者: the domain:spec seat that authored the claim comment." ] }
Generated by Claude Code
Round returned
done. ⛔ Two corrections to this seat, both measured by the round, both mine.domain:specexecution seat,session_01KB5PFtxuy1x3dcR5gxudx6, 2026-09-16T16:57Z. ⛔ No label change; card stayspm:dispatchedwith its assignee. An isolated at-tier review starts next.⛔ 1. My「the card's 27 is a 2026-09-10 reading」 warning — the method was right, the inference was wrong
I told the round that
#17616(「refusedashboard.widgets[].options.stageOrderon every widget type that does not read it」) had probably moved the count, and to re-derive it.Re-deriving was correct and it is what I asked for. The answer is that the count did not move: still 17 + 10 = 27. And the reason is structural, not incidental:
stageOrderlives insideoptions— one level below the repeater's row properties, whereoptionsitself is the single row property — so a refusal at that depth cannot move this count.⇒ ⛔ I reasoned from 「the file moved」 to 「the population moved」 without asking at what depth. The card's number was never stale. ⭐ The right shape of my warning was 「this is a premise to re-derive」; the wrong part was the 「
⚠️ it probably changed」 I wrapped around it, which is a prediction wearing a measurement's clothes.⛔ 2. My claim comment omitted the
Session:line, and the gate says socheck-clause2-carriers --pair 18505→ exit 0, but it prints:ATTRIBUTION NOT VERIFIED: the governing claim comment carries no
Session:line, so there is nothing to compare the correction against. The reading stands.Advisory, ⛔ not blocking — but it is real: my
Clause-②-correctioncomment carries aSession:line and the claim it corrects does not, so nothing can check they are the same session.⚠️ And the cause is the one I already owned once today: the template carries the line and I did not copy it.SKILL.md:802spellsSession: \session_`immediately above theClause-②line. That is now **three** claim-comment defects this shift from composing the block from memory — the malformedClause-②line on #17235, the same on this card, and this omission. ⛔ Not filing a card: the template is correct and the repo is not at fault. The fix is that this seat copiesSKILL.md` 〈模板与表〉 verbatim, every time.What the round did that is worth naming
⭐ It re-pointed a dark control its own change would have silently invalidated.
dashboard.test.tsusedwidgets[].idas the 「a sibling with no authored title has none」 control — and this change titleswidgets[].id. Rather than delete the control or let it pass for the wrong reason, it re-pointed it onto a tombstone (widgets[].actionUrl) and gave it a lit leg (widgets[].idis now'Widget ID'). ⇒ the control now pins the tombstone rule instead of borrowing a coincidence. That is the 「does my test still recognise what it should not?」 question asked of its own suite.⭐ The ledger pin is exact in both directions, so the run 「would have gone RED had the titles landed without the ledger deletion, or the deletion without the titles」. Measured via
--reporter=verbose, not asserted.Tombstones: exactly 5 on these two carriers, all on
dashboard.widgets,globalFiltersholds 0 — and all 5 still emittitle=undefinedin both io modes after the change. ⛔ Nothing got a title that should not: the other 20 repeater carriers hold their exact prior untitled counts (field:options 6, object:fields.options 6, view:columns 14, view:sort 2, view:tabs 9).Fence held:
check:authorable-surfacegreen andpackages/spec/authorable-surface.base.jsonneither moved nor was touched ⇒ the anchor-only PR that owns that file is unaffected.Two files outside the declared face — reported, ⛔ not taken silently
The LEDGER (
repeater-item-titles.test.ts) and thedashboard.test.tscontrol above. The first is the card's own stated second half (「then remove this carrier's ledger entry」) and leaving it would be as red as never paying; the second is the re-pointing.dashboard.form.tswas deliberately not touched — enumerating repeater children is the i18n-catalog half and belongs to #17508. All three calls are right.For the successors, not this card
- The card body and its triage comment say 「Six tombstones (5 on
dashboard.widgets)」. Re-derived: these two carriers hold exactly 5,globalFiltersholds 0 ⇒ the sixth is on neither carrier of this card. An attribution error in the count, ⛔ not in the work. 承接者: spec:retiredKey()tombstones are emitted into a repeater row'sitems.properties, so the served schema offers six columns the parse door refuses #17502. check:cross-package-test-inputs— already [regression] check:cross-package-test-inputs 的裁决取决于被 gitignore 的空目录 packages/spec/dist 存不存在 —— 构建过的工作树一律红,CI 绿只因那一步跑在构建之前(#18340 引入) #18348 (and [finding]check:cross-package-test-inputsanswers 1 or 0 depending on whetherpackages/spechas been BUILT — the author who follows AGENTS.md is the only one who sees the red, and CI never does #18440 / [finding]check:cross-package-test-inputsgoes red the momentpackages/specis built locally —packages/cli/test/init-created-files-summary.e2e.test.tswalks intopackages/spec/dist/and no declared glob reaches it #18495). ⛔ Not re-filed. ⭐ But this round produced a sharper repro than any of those cards carry, and it is worth having on the surviving card: in a clean control worktree with zero source changes the gate exits 0;mkdir -p packages/spec/dist && echo 'export {};' > packages/spec/dist/index.jsflips it to exit 1 with the identical finding; removingdistrestores exit 0. ⇒ two lines reproduce it from nothing. This seat will carry that onto [regression] check:cross-package-test-inputs 的裁决取决于被 gitignore 的空目录 packages/spec/dist 存不存在 —— 构建过的工作树一律红,CI 绿只因那一步跑在构建之前(#18340 引入) #18348.
Generated by Claude Code
- The card body and its triage comment say 「Six tombstones (5 on
Landed — verified git-side on
origin/mainPR #18505 squashed as
97233b90ca479bd65b36842aa168ce67ae34b0a2, merged 2026-09-16T19:15:06Z. Verification at 19:15:23Z aftergit fetch origin main— read from the tree, ⛔ not from the API'smergedfield:git merge-base --is-ancestor 97233b90ca origin/main→ exit 0; the squash is main's tip.- Subject probe (lit):
.meta({ titleinpackages/spec/src/ui/dashboard.zod.ts→ 31 occurrences (grep -o | wc -l, i.e. occurrences, not lines), matching the review's measured 4 → 31. - Lit control, same file:
z.enum(→ 6 occurrences, so the probe reads a file it can see. - Dark control:
.meta({ zztitlezz→ 0. - Ledger: the only surviving mention of the two carriers in
packages/spec/src/kernel/repeater-item-titles.test.tsis the prose at:94— "dashboard:widgetsanddashboard:globalFilterswere paid by [finding] Untitled repeater row properties:dashboard.widgets(17) anddashboard.globalFilters(10) — fence dropped, dispatchable now #17505" — i.e. both ledger ENTRIES are gone, exactly as the review measured, while the lit controlview:columnsis still there.
At-tier contract review PASSED on head
82838f4fac(record posted verbatim in the PR thread, tier census 103/103); both carriers were cleared in one stroke,--pair 18505exit 0, and the landing pre-check's three conditions were read after the last state write.pm:dispatchedcomes off here — a closed card carrying a pm state label is a half-state.domain:spec,bugandpriority:p3stay: attribution is not state.Residue, filed rather than left in a comment
- spec: the dashboard tombstone control pins 1 of 5 tombstones, not the untitled-tombstone rule #18517 — the
dashboard.test.tscontrol this PR re-points pins the untitled-tombstone rule for 1 of the 5 tombstones on the widget row, not the class. Measured by the reviewer: titlingactionUrlreds 2 cases; titlingariaoractionTypepasses all 109. The card carries the reviewer's verified recommended shape (iterate the[REMOVED]-described entries and asserttitleundefined for each, keeping the'Widget ID'lit leg). Not owed by this PR — it titles no tombstone, and fix(metadata-protocol): GET /meta/types stops publishing properties no instance can satisfy #18231 strips unsatisfiable nodes from the served schema regardless oftitle.
A seat correction, on the record
The dispatch for this round warned that #17616 had "probably moved" the count of 27. The reviewer measured 27 on today's tree in both io modes and named two independent reasons it could not have moved —
stageOrdersits one level below the row properties, and #17616 is asuperRefinerefusal rather than a schema removal. The warning was a prediction wearing a measurement's clothes; the card's count was never stale.⚠️ For the parent #17232: this closes thedashboard.widgets(17) +dashboard.globalFilters(10) half. The five ledger survivors remain —field:options6,object:fields.options6,view:columns14,view:sort2,view:tabs9 — and are #17506's and #17507's.
Generated by Claude Code
- added a commit that references this issue
on Sep 17, 2026
27 row properties on two repeater carriers in
packages/spec/src/ui/dashboard.zod.tsstill render column headers from raw property keys in the Studio property panel.The fence on this file has DROPPED. It was held by #17474, which landed — measured on
origin/main76ddab772f:git grep -c 'only widget type' -- packages/spec/src/ui/dashboard.zod.tsreads 1. So this is dispatchable now, not blocked.Carriers and counts, derived through the platform's own predicate (
z.toJSONSchemaovergetMetadataTypeSchema), not by regexing source:retiredKey()tombstones are excluded from these counts (5 ondashboard.widgets). They are separately reported as #17502 — ⛔ do not title a tombstone.Part of #17232. The mechanism that makes this measurable is
packages/spec/src/kernel/repeater-item-titles.test.ts— an exact, shrink-only ledger: a carrier absent from the ledger must be fully titled (a new untitled repeater reds on arrival), and a carrier present in it must still be untitled (a paid debt must be DELETED from the ledger). So the work here is: add.meta({ title })to each row property, then remove this carrier's ledger entry. ⛔ Do not weaken the ledger to make it pass.Titles are hard-coded English by design, not a gap.
packages/spec/src/system/translation.zod.tsstates it: a row property is rendered fromitems.properties[k].title, andresolveMetadataFormSchemaTitlesoverlays the locale bundle on top of that node. The English default has to live on the schema..meta({ title })on a schema that zod hoists into$defsis emitted as a sibling of the$ref—{"title":"…","$ref":"#/$defs/__schema0"}. Read the property node first, as Studio does; deref'ing first reports genuinely-titled properties as untitled.⛔ Ungraded — triage sets type and priority. Filed by the
domain:specexecution seat 2026-09-10T18:04Z.Generated by Claude Code