Skip to content

fix(lint): walk the four list-view field-naming keys that had no position row - #18860

Merged
os-bill merged 2 commits into
mainfrom
claude/issue-18835-unwalked-field-naming-keys
Sep 18, 2026
Merged

os-bill merged 2 commits into
mainfrom
claude/issue-18835-unwalked-field-naming-keys

Conversation

@os-bill

@os-bill os-bill commented Sep 18, 2026

Copy link
Copy Markdown
Collaborator

Fixes #18835

Clause-②: no (narrowing)

POSITIONS in packages/lint/src/validate-list-view-field-refs.ts declares, per view face, which field-reference keys are walked and at what level. Four declared, authorable field-naming keys had no row at all — calendar.allDayField (view.zod.ts:1552) and the three objectui-lifted gantt bindings borderColorField (:1660), lockField (:1661) and objectField (:1662), every one of them z.string().optional(). A misspelt field name at any of them cleared the schema door, was walked by nothing, and was dropped by the renderer in silence. Each now has a row.

The tier is per key, and deliberately not per schema shape

All four are .optional(). That is the one thing they share, and it is not what tiers them: each is tiered by what its own renderer does with a name no record carries, read off the key's own .describe() and measured in objectui dda8f3815 (the pin this repo builds against is 53ded82b; the two gantt readings below are unchanged between them — the code is plugin-gantt/src/ObjectGantt.tsx in both).

key tier what a misspelt name does
calendar.allDayField warning ObjectCalendar maps every event with allDay: allDayField ? Boolean(record[allDayField]) : !endDate. A name no record carries reads undefined on every row, so nothing is banded — and the renderer's own no-end-date inference is switched off too, because a DECLARED key is absolute (the spec's own "⛔ No default field name" note). Every event still renders, at its start time: the all-day band is the one decoration dropped.
gantt.borderColorField warning borderColorRaw = borderColorField ? record[borderColorField] : undefined leaves borderColor undefined for every task. Every bar keeps its fill and renders without its alert outline — colorField's case, eight rows up.
gantt.lockField error A declared WRITE GUARD that fails OPEN. locked: lockField ? !!record[lockField] : undefined reads undefined on every row, and the drawer's recLocked falls the same way, so every row the author froze becomes draggable, resizable, progress-draggable, link-able, inline-editable and deletable — and the drag persists.
gantt.objectField error isSyntheticRow is !!objectField && !String(rec[objectField] ?? '').trim(), so a name no record carries answers TRUE for every row. onTaskClick then never calls navigation.handleClick and renderRecordOverlay returns null: no bar in the chart opens a drawer or a detail page.

The two error rows are a consequence the rule's severity note did not name

Its two tiers read "the miss changes what data the view returns, or collapses the layout it configures" against "the renderer drops one decoration and renders the rest". lockField and objectField are neither: nothing is missing from the picture, which is exactly why they gate. The note now names the third consequence — a binding whose job is to RESTRICT or to ROUTE, where the miss is read as "no restriction" / "no route" on every row. Both are the shape Prime Directive #10 names, a capability advertised in the metadata and not delivered by the runtime, and both are worse DECLARED than omitted, because each renderer guards its behaviour on the key's mere presence.

allDayField is deliberately NOT put in that third class: no restriction and no route rides on it, and a wrongly-banded event is still an event the user can click.

Controls — both arms, all four keys separately

One instrument, run against the rule before the edit and after it, over one list view carrying every walked position, one mutation at a time. The diff of the two runs is four lines long.

LIT — each key pointed at a field that does not exist

probe before after
calendar.allDayField SILENT warning list-view-field-unknown at views[0].list.calendar.allDayField
gantt.borderColorField SILENT warning list-view-field-unknown at views[0].list.gantt.borderColorField
gantt.lockField SILENT error list-view-field-unknown at views[0].list.gantt.lockField
gantt.objectField SILENT error list-view-field-unknown at views[0].list.gantt.objectField

Four lit for four. No key was proven by a neighbour: each is its own mutation, its own path assertion and its own tier assertion, and the suite-level pin puts all four misses on one stack and asserts four findings rather than one.

DARK — must read zero, and does

probe before after
the 53 already-walked position probes 53 reported, each at its severity the same 53, each at its same severity — byte-identical lines
each of the four keys pointed at a REAL field, one at a time SILENT SILENT
the clean fixture, all four bound to real fields 0 findings 0 findings
the four keys carrying a dotted path whose head resolves no finding no finding (they join POSITIONS and deliberately not DOTTED_AXIS; pinned)

Does anything in this repository redden? No, and it is a proof rather than a sample

The four rows read exactly four keys. git grep over every tracked file finds those key names only in the spec declaration, its own schema tests, the generated reference docs, the changesets that landed them, and unrelated identifiers (objectFieldLabelKey, objectFieldTypeRefusal). git grep over examples/ matches nothing at all. No example app, fixture or seed in this tree authors any of the four, so no existing declaration can start reporting — 0 findings before, 0 after, with no declaration data touched.

Completeness, since this was a hole in a table

After these four rows, every *Field / *Fields key declared on ListViewShapeSchema and on each of the seven view-face config schemas (Kanban, Calendar, Gantt, Timeline, Gallery, ListMap, Tree) has either a POSITIONS row or a named owner elsewhere (searchableFields, sort). Enumerated from view.zod.ts key by key, not recalled.

Verification

Measured on 496b71de99, this branch merged with origin/main:

  • pnpm --filter @objectstack/lint exec vitest run src/validate-list-view-field-refs.test.ts — 130 passed.
  • pnpm --filter @objectstack/lint test — 104 files, 3902 tests, all passed.
  • pnpm --filter @objectstack/lint typecheck — green (tsc --noEmit plus check:test-typecheck).
  • pnpm --filter '@objectstack/lint^...' build and pnpm --filter '@objectstack/lint...' build — green.
  • pnpm exec eslint . --no-inline-config over the WHOLE repository — exit 0, 6846 files linted, 0 errors, 0 warnings. Not a narrowing: the repo-wide run is the reading.
  • The gate families derived from this diff by scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack on the merged tree — 58 commands, 54 green.
  • ⊘ NOT MEASURED, 4 of the 58, every one of them PREREQUISITE NOT MET (exit 3, which is neither a pass nor a finding) for want of a whole-workspace build this worktree does not carry: check:dual-build-cjs-loads, check:lean-entry-closure, check:type-check-debt, and check:docs-transcript-drift — the last was re-run after building @objectstack/lint and is green, so three remain. CI builds before it runs them.

Acceptance notes

  • gantt.objectField's own headline use case is out of this rule's reach, by construction. Its .describe() frames it for "an api provider composing parent-object rows with child-object rows", and this rule's fourth skip drops any list view whose data.provider is not object — such a view binds to no object graph, so no field name on it is resolvable here. The row still earns its place: isSyntheticRow runs whatever the provider is, so an object-provider list view that declares a misspelt objectField loses every drawer in the chart, and that is the case the row reports. Noted, not filed — nothing here is a defect, it is the skip doing what it says.
  • The floor assertion's remaining slack is untouched. It moved 47 → 51, by exactly the four cases added; the table now holds 52. Whether the floor should be tight against the table is the separate card's question and its semantics were not altered here.
  • calendar-view-renderer.tsx in objectui defaults allDayField to the literal allDay, where ObjectCalendar has no default and the spec forbids one. Checked and it is not a contradiction: that renderer serves the calendar-view node with inline schema.data, a different surface from a list view's calendar block, and it defaults every one of its five field names the same way. Noted, not filed.

Generated by Claude Code

… row

`POSITIONS` in `packages/lint/src/validate-list-view-field-refs.ts`
declares, per view face, which field-reference keys are walked and at
what level. Four declared, authorable field-naming keys had no row at
all: `calendar.allDayField` and the three objectui-lifted `gantt`
bindings `borderColorField`, `lockField` and `objectField`. A misspelt
field name at any of them cleared the schema door, was walked by
nothing, and was dropped by the renderer in silence.

All four are `.optional()`, and that is deliberately not what tiers
them. Each is tiered by what its own renderer does with a name no
record carries, measured in objectui `dda8f3815`:

- `calendar.allDayField` `warning` — a declared key is absolute, so
  every event loses its all-day band and the renderer's own
  no-end-date inference is switched off too; every event still
  renders, at its start time.
- `gantt.borderColorField` `warning` — `borderColor` is undefined for
  every task, so every bar keeps its fill and renders without its
  alert outline.
- `gantt.lockField` `error` — a declared write guard that fails open:
  every row the author froze becomes draggable, resizable,
  inline-editable and deletable, and the drag persists.
- `gantt.objectField` `error` — every row answers the renderer's
  synthetic-row test, so no bar in the chart opens a drawer or a
  detail page.

The two `error` rows are a consequence the rule's severity note did
not name and now does: a binding whose job is to RESTRICT or to ROUTE,
where the miss is read as "no restriction" / "no route" on every row.

Claude-Session: https://claude.ai/code/session_01JbZnqu8bt6YqfJsr9vaFb3
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added size/m documentation Improvements or additions to documentation tests tooling labels Sep 18, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

Nothing in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 1 changed package(s)), so this run has no opinion about the docs.

What this run could not see
  • 2 name(s) were too generic to anchor anything (single lowercase words)
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.

Coarse fallback — 4 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json a2cf0f16ce886e26318840bcbd0745d083999655 → packageMentionDocs.

@os-bill os-bill added domain:spec priority:p2 Medium: important, M3 labels Sep 18, 2026 — with Claude
@os-bill
os-bill marked this pull request as ready for review September 18, 2026 01:47
@os-bill
os-bill added this pull request to the merge queue Sep 18, 2026
Merged via the queue into main with commit a43b9d0 Sep 18, 2026
44 checks passed
@os-bill
os-bill deleted the claude/issue-18835-unwalked-field-naming-keys branch September 18, 2026 02:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation domain:spec priority:p2 Medium: important, M3 size/m tests tooling

Projects

None yet

2 participants