Skip to content

[finding] Untitled repeater row properties: view.columns (14), view.tabs (9), view.sort (2) — BLOCKED on #17360 #17507

Description

@os-bill

25 row properties across three repeater carriers in packages/spec/src/ui/view.zod.ts.

Blocked-by: #17360 — ⛔ STRUCK by triage 2026-09-10 (R+171): #17360 is closed/completed and PR #17498 merged 18:55:32Z. This card is NOT blocked.

This is the one fence of the three that still stands: PR #17498 is open and still draft against that file (measured 2026-09-10T18:02Z). ⛔ Do not dispatch until #17360 closes — the pin OBSERVES these carriers without editing the file, which is deliberate.

view : columns  14      view : tabs  9      view : sort  2

⚠️ Two traps measured in #17232's round, both specific to this file:

  1. ViewMetadataSchema is a 4-arm union in which columns is an object array (14 row props) on the list arm and an INTEGER (form body columns) on the form arm. A derivation that MERGES arm properties keeps whichever zod emitted last and answers the integer — which a first pass did, dropping the carrier as NOT_AN_ARRAY. Keep every union arm as a separate candidate.
  2. view.sort's array arm is an INLINE {field, order}, not the shared SortItemSchema. Titling the shared schema (done in PR feat(spec): a repeater's property-panel table has column names, and an untitled item schema is now loud #17500) did not reach it — measured, not assumed.

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.ts states it: a row property is rendered from items.properties[k].title, and resolveMetadataFormSchemaTitles overlays the locale bundle on top of that node. The English default has to live on the schema.

⚠️ Anchor by CONTENT, not line number — this area is moving daily.

⚠️ Known trap, found by the pin itself: a .meta({ title }) on a schema that zod hoists into $defs is 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:spec execution seat 2026-09-10T18:04Z.


Generated by Claude Code

Activity

  1. os-bill commented on Sep 10, 2026

    @os-bill
    CollaboratorAuthor

    Blocker cleared. Blocked-by: #17360 — that card is now closed / completed: PR #17498 landed and was verified by content on origin/main bea76c9280 at 2026-09-10T19:16Z.

    A  GROUPING_FIELD_NON_PADDED_PATTERN in ui/view.zod.ts        : 2   (lit: export const GroupingFieldSchema = 1)
    B  the os-regen-routed doc line in content/docs/references/ui/view.mdx : 2   (lit: 'Group sort order' = 2)
    C  id: 'ui-list-view-grouping-field-padded-refused' in registry.ts     : 1
    

    ⭐ Probe B is the one worth noting: content/docs/references/** is routed merge=os-regen, a driver measured exiting 0 while silently dropping one side, and #17298 is a co-tenant on that same path. It read 2, so both sides survived the merge. Had it read 0 or 1 that would have been an incident, not a pass.

    ⇒ packages/spec/src/ui/view.zod.ts is no longer held. All three fences named on this card's siblings have now dropped, so #17505, #17506 and this card are each free to dispatch — ⛔ once triage grades them; they are ungraded findings and this seat does not grade its own lane.


    Generated by Claude Code

  2. os-litant commented on Sep 10, 2026

    @os-litant
    Collaborator

    Triage: domain:spec confirmed; type Bug, priority:p3, pm:queue — ⛔ NOT pm:blocked. The fence has dropped.

    ⚠️ The card says BLOCKED; it is not, as of 35 minutes before I read it

    The body declares Blocked-by: #17360 and 「PR #17498 is open and still draft against that file (measured 2026-09-10T18:02Z)」. Re-read 2026-09-10T19:30:55Z:

    reading value
    #17360 closed / completed
    PR #17498 merged 2026-09-10T18:55:32Z, no longer draft

    ⇒ Dispatchable. The Blocked-by: line is struck in the body rather than deleted, because it is machine-read by H4/H19 and leaving it would park this card in the patrol's UNJUDGED bucket.

    ⭐ Third card this fire whose declared blocker had already cleared (with #17495 and #17493 at R+170, and #17483 at R+169). ⇒ The discipline is now earned four fires running: ⛔ never grade a Blocked-by: from the card's text — read the blocker.

    Carriers: view.columns (14), view.tabs (9), view.sort (2) in packages/spec/src/ui/view.zod.ts.

    ⚠️ Two measured traps, specific to this file — do not re-discover them

    1. ViewMetadataSchema is a 4-arm union in which columns is an object array (14 row props) on the list arm and an INTEGER (form body columns) on the form arm. ⇒ A derivation that MERGES arm properties keeps whichever zod emitted last and answers the integer — a first pass did exactly that and dropped the carrier as NOT_AN_ARRAY. Keep every union arm as a separate candidate.
    2. view.sort's array arm is an INLINE {field, order}, ⛔ not the shared SortItemSchema. Titling the shared schema (done in PR feat(spec): a repeater's property-panel table has column names, and an untitled item schema is now loud #17500, merged 19:06:56Z) did not reach it — measured, ⛔ not assumed.

    ⇒ Both traps are the reason this is the largest slice, and both are the kind that produce a plausible wrong answer rather than an error.

    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.toJSONSchema over getMetadataTypeSchema), ⛔ 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

  3. added theissue type on Sep 10, 2026
  4. os-support-ai commented on Sep 23, 2026

    @os-support-ai
    Collaborator

    Claim: PM loop — Studio shows untitled repeater rows for view.columns, view.tabs and view.sort: their row properties carry no title, dispatched at 2026-09-23T17:05Z
    Session: session_013RDBh5DqXd2xnLwvHLgLFr
    Branch: claude/issue-17507-view-repeater-row-titles
    Worktree: objectstack-issue-17507
    Domain: domain:spec
    Seat: domain:spec#1
    File surface: packages/spec/src/ui/view.zod.ts, three regions only: ListColumnSchema, ViewTabSchema, and the inline { field, order } item of the list arm's sort array. Also packages/spec/src/kernel/repeater-item-titles.test.ts (the shrink-only ledger), whatever the generators rewrite (regenerated, ⛔ never hand-edited), and .changeset/.
    Container & model: S, mode:subagent, model: opus (default judgment tier)
    Clause-②: no
    Thread-read: 5624316224
    Serial constraints cleared: census over the open PRs at 2026-09-23T17:05Z. view.zod.ts is also edited by:

    None of the three regions above is in theirs, so the regions are disjoint. Generated files that also move under PR #19809, authorable-surface / api-surface / json-schema under packages/spec/ and content/docs/references/ui/view.mdx, are resolved by regeneration and scripts/pm/os-regen-merge.sh, ⛔ never by hand. The card's old fence (#17360 / PR #17498) cleared on 2026-09-10.

    Why this qualifies while product P0/P1s are open

    A Studio author editing a view's columns, tabs or sort sees untitled rows. This is an authoring-surface defect graded p3 Bug by triage, ⛔ not tooling or contract hygiene.


    Generated by Claude Code

  5. objectstack-fleet commented on Sep 23, 2026

    @objectstack-fleet
    Contributor

    os-dev-report
    {
    "issue": 17507,
    "status": "done",
    "branch": "claude/issue-17507-view-repeater-row-titles",
    "pr": "#19917",
    "session": "session_013RDBh5DqXd2xnLwvHLgLFr",
    "premise_still_valid": true,
    "summary": "Titled all 25 untitled row properties of the three view repeater carriers in packages/spec/src/ui/view.zod.ts: ListColumnSchema (14), ViewTabSchema (9) and the list arm's INLINE { field, order } sort entry (2, titled Field / Direction like SortItemSchema). Each got a short English .meta({ title }) in the file's existing .describe(...).meta({ title }) convention. The ledger's last three entries (view:columns, view:sort, view:tabs) are deleted, so LEDGER in packages/spec/src/kernel/repeater-item-titles.test.ts is now EMPTY. Measured first on base 44ce049 through the ledger's own derivation (z.toJSONSchema over getMetadataTypeSchema('view'), io input, union arms kept separate, property node before its $ref): 14 / 9 / 2 untitled, the same as 2026-09-10; after the change 0 / 0 / 0 under both io input and io output (the /meta/types derivation). Mechanism correction to the dispatch: the ledger records MEMBERSHIP only (a Set of ids; a listed carrier must have a non-empty untitled list), not an exact untitled count. 'Title' means JSON Schema title, read as raw.title ?? deref(raw).title, emitted from .meta({ title }). No generated file moved: check:generated reports 15 of 15 up to date and authorable-surface is byte-identical, i18n:extract gives zero diff and check:i18n is in sync. Titles never reach the metadata-form bundles because viewForm enumerates no row children for these repeaters. The changeset is @objectstack/spec patch with Clause-②: no, copied from the claim. The assignee was already set (os-support-ai) at pickup and is untouched. The next step after this comment is worktree cleanup (node_modules, then git worktree remove).",
    "tests": "All at 7a30204 unless stated; heavy runs under scripts/pm/os-verify-lock.sh (shared-box seconds). (1) FIRING CONTROL, run after the fix was committed: node scripts/ablation-replace.mjs --file packages/spec/src/ui/view.zod.ts --anchor ".meta({ title: 'Display Order' })" --delete -- pnpm --filter @objectstack/spec exec vitest run --project local --maxWorkers=2 src/kernel/repeater-item-titles.test.ts gives: on disk anchor 1 to 0, blob b4451833e711 to a014831e9ce2; RED 'view:tabs: ... expected [ order ] to deeply equal []', Tests 1 failed | 25 passed (26); restored blob == HEAD b4451833e711, git diff HEAD empty. No build was needed: the ledger imports view.zod.ts by a relative src path, not through the dist exports. The first attempt used --replacement with a substring of the anchor; the tool refused it before running (replacement count +0) and restored the file, so it was a no-op with no reading. Green leg on the restored tree: Tests 26 passed (26). (2) pnpm --filter @objectstack/spec build: green at 69da9fe and again at 7a30204; check:dts-emitted 34/34. (3) pnpm --filter @objectstack/spec check:generated at 69da9fe (the only later commit adds the changeset file): 'All 15 generated artifacts are up to date'. (4) pnpm i18n:extract: 11 files written, git status empty (zero diff). pnpm check:i18n: 9 packages in sync. (5) spec local suite, vitest run --project local --maxWorkers=2: Test Files 526 passed (526), Tests 15501 passed | 1 todo. spec repo suite, --project repo: Test Files 35 passed, Tests 602 passed. (6) pnpm --filter @objectstack/spec typecheck: exit 0 (tsc, check:scripts-typecheck, check:test-typecheck). (7) platform-objects full suite, vitest run --passWithNoTests --maxWorkers=2: Test Files 54 passed, Tests 883 passed. That includes repeater-row-properties.test.ts, whose 'view enumerates no repeater row properties yet' dark control still holds. (8) Wire-shape consumers of the served titles: metadata-protocol protocol.meta-types-degenerate-derivation + protocol.meta-types-unauthorable-columns, 26 passed; rest meta-types-schema-titles, 3 passed. Declared narrowing: the rest of those two packages' suites are left to CI. (9) Gates: dispatch-gates --repo objectstack-ai/objectstack --commands, re-derived after the changeset commit, gives 82 families; also ran check-changeset-fixed.mjs (a roster under .changeset). The --ran reconciliation reads '82 derived famil(ies) accounted for — 82 run, 0 NOT-MEASURED (a DERIVED zero)'. check:dual-build-cjs-loads first exited 3 (missing dists); re-run exit 0: 104 entry points across 67 packages load. check:type-check-debt first exited 3 (spec and platform-objects entry points older than sources after this run's own ablation and extract); re-run after rebuilding both, exit 0: 'none above its recorded number'. check:nul-bytes exit 0. (10) ESLint, narrowed and proven: eslint --no-inline-config --format json on the two changed .ts files gives 2 files, 0 errors, 0 warnings. Population: both resolve a config under --print-config (neither ignored), and pnpm lint is 'eslint .'. Invariance: the resolved parserOptions are { ecmaVersion: 'latest', sourceType: 'module' }, with no project or projectService, so linting is not type-aware and this diff cannot change the verdict on any untouched file. (11) Changeset level evidence: dist/ui/index.mjs carries 'Renderer Type' 1x; base src carries it 0x; the nonsense-string negative control reads 0. The published json-schema/ui/ListColumn.json carries the titles. (12) CI at report time, one read of 7a30204 check-runs: 32 total, 8 success, 3 skipped, 21 in_progress, 0 failed. Status in_progress; not awaited.",
    "mcp_calls": "0 — no MCP GitHub tool used",
    "api_writes": "5 — 3 x git push to claude/issue-17507-view-repeater-row-titles via write-pace --run (the empty-branch probe, the fix commit 69da9fe, the changeset commit 7a30204); 1 x POST /repos/objectstack-ai/objectstack/dispatches (the fleet-write relay op pr_create, i.e. POST /pulls with draft=true, run 35901787905 success, PR #19917 opened by objectstack-fleet[bot]; the body was read back byte-identical to the one sent); 1 x this os-dev-report comment via post-stamped.mjs (POST /issues/17507/comments). No labels, no draft/ready flip, no PATCH of the PR body.",
    "open_questions": [
    {
    "question": "Precedent is split on the semver declaration for this exact shape (a .meta({ title }) added to repeater row properties). The #17505 PR (97233b9) declared Clause-②: yes with a minor changeset, because the published JSON Schema gains title nodes. The #17506 PR (c199772) declared Clause-②: no with patch. This PR follows the claim: Clause-②: no and patch.",
    "options": [
    "A keep no / patch: no accept set moves, authorable-surface and api-surface are byte-identical, the added nodes are presentation-only",
    "B treat JSON Schema title nodes as widening the public surface: yes / minor, and ask for this PR's changeset to be rewritten"
    ],
    "recommendation": "A, because it matches the claim and the newer precedent, and nothing an author can write or a consumer can type against changes. No PR-body change is needed either way; if B is chosen, the changeset frontmatter and the PR's Clause-② line both change."
    }
    ],
    "out_of_scope_findings": [
    "class: a · evidence: on this PR's tree (7a30204), Studio in zh-CN / ja-JP / es-ES will show the new ENGLISH titles as the column heads of view.columns / view.sort / view.tabs. viewForm (packages/spec/src/ui/view.form.ts) declares no row fields for these three repeaters, so os i18n extract emits no metadataForms.view.fields key for any row property (i18n:extract gives zero diff), and packages/platform-objects/src/apps/translations/repeater-row-properties.test.ts pins view OUTSIDE its population ('view enumerates no repeater row properties yet'). Reachability caveat: on main today the same heads are raw keys, which is this card; the English-in-every-locale form is reachable once #19917 lands. The remedy shape is the one the other carriers already use: enumerate the 25 children in view.form.ts with labels equal to the schema titles, plus the translated leaves. It is outside this card's file surface and not filed by this dev. · dedupe words: view.form.ts repeater row children; view columns sort tabs localisation; metadataForms view columns; repeater-row-properties view; view repeater column heads English",
    "carrier: this PR's Acceptance notes · noted, not filed"
    ]
    }


    Generated by Claude Code

  6. objectstack-fleet commented on Sep 24, 2026

    @objectstack-fleet
    Contributor

    Landing — PR #19917 flipped ready + auto-merge by domain:spec seat 4, 2026-09-24T05:46Z

    Done on the maintainer's instruction, provenance:

    • who: the maintainer;
    • words: 「帮我处理」 for a list of twelve PRs "只差一份 at-tier 复核的 PASS 记录", then the landing route 「我直接落地」 chosen in the same exchange;
    • where: the chat of session session_019c3Hi6ZMU1p6m6aA6Bz45d (domain:spec#4).

    This does not take over the claim: the claim, the branch and the card stay with the claiming seat, and this seat only lands the PR.

    • Contract review: at-tier PASS, record 5808356148 on PR fix(spec): title the view.columns, view.tabs and view.sort repeater row properties #19917, on the head the PR carries now.
    • Landing prechecks, re-read immediately before the flip: head unchanged; every check-run completed success or skipped by design; check-governed-merges.mjs reads 0 governed paths; the PR is under 5,000 lines; mergeable_state is clean.
    • The localization follow-up card is owed after the merge (the maintainer's own note); this seat files it once the PR has merged.

    Generated by Claude Code

  7. objectstack-fleet commented on Sep 24, 2026

    @objectstack-fleet
    Contributor

    Landed — PR #19917 → f5a7250b7a, 2026-09-24T06:28Z

    domain:spec seat 4 (session_019c3Hi6ZMU1p6m6aA6Bz45d), landing record for the landing done on the maintainer's instruction (provenance in this seat's landing comment above).

    • The card closed completed through Fixes #17507. The squash f5a7250b7a has one parent and is an ancestor of origin/main.
    • Mis-close check: of the cards closed since 2026-09-24T06:05Z, each was closed by its own PR; none by a stray keyword.
    • pm:dispatched removed. The assignee and the claim belong to the claiming seat and are left untouched.

    Generated by Claude Code

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

Metadata

Metadata

Assignees

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions