Repository navigation
fix(lint): walk the four list-view field-naming keys that had no position row - #18860
Merged
Merged
Conversation
… 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>
…walked-field-naming-keys
Contributor
📓 Docs Drift CheckNothing 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
Coarse fallback — 4 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #18835
Clause-②: no (narrowing)
POSITIONSinpackages/lint/src/validate-list-view-field-refs.tsdeclares, 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-liftedganttbindingsborderColorField(:1660),lockField(:1661) andobjectField(:1662), every one of themz.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 objectuidda8f3815(the pin this repo builds against is53ded82b; the two gantt readings below are unchanged between them — the code isplugin-gantt/src/ObjectGantt.tsxin both).calendar.allDayFieldwarningObjectCalendarmaps every event withallDay: allDayField ? Boolean(record[allDayField]) : !endDate. A name no record carries readsundefinedon 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.borderColorFieldwarningborderColorRaw = borderColorField ? record[borderColorField] : undefinedleavesborderColorundefined for every task. Every bar keeps its fill and renders without its alert outline —colorField's case, eight rows up.gantt.lockFielderrorlocked: lockField ? !!record[lockField] : undefinedreadsundefinedon every row, and the drawer'srecLockedfalls 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.objectFielderrorisSyntheticRowis!!objectField && !String(rec[objectField] ?? '').trim(), so a name no record carries answers TRUE for every row.onTaskClickthen never callsnavigation.handleClickandrenderRecordOverlayreturns null: no bar in the chart opens a drawer or a detail page.The two
errorrows are a consequence the rule's severity note did not nameIts 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".
lockFieldandobjectFieldare 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.allDayFieldis 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
calendar.allDayFieldwarninglist-view-field-unknownatviews[0].list.calendar.allDayFieldgantt.borderColorFieldwarninglist-view-field-unknownatviews[0].list.gantt.borderColorFieldgantt.lockFielderrorlist-view-field-unknownatviews[0].list.gantt.lockFieldgantt.objectFielderrorlist-view-field-unknownatviews[0].list.gantt.objectFieldFour 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
POSITIONSand deliberately notDOTTED_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 grepover 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 grepoverexamples/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/*Fieldskey declared onListViewShapeSchemaand on each of the seven view-face config schemas (Kanban,Calendar,Gantt,Timeline,Gallery,ListMap,Tree) has either aPOSITIONSrow or a named owner elsewhere (searchableFields,sort). Enumerated fromview.zod.tskey by key, not recalled.Verification
Measured on
496b71de99, this branch merged withorigin/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 --noEmitpluscheck:test-typecheck).pnpm --filter '@objectstack/lint^...' buildandpnpm --filter '@objectstack/lint...' build— green.pnpm exec eslint . --no-inline-configover the WHOLE repository — exit 0, 6846 files linted, 0 errors, 0 warnings. Not a narrowing: the repo-wide run is the reading.scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackon the merged tree — 58 commands, 54 green.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, andcheck:docs-transcript-drift— the last was re-run after building@objectstack/lintand 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 whosedata.provideris notobject— such a view binds to no object graph, so no field name on it is resolvable here. The row still earns its place:isSyntheticRowruns whatever the provider is, so an object-provider list view that declares a misspeltobjectFieldloses 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.calendar-view-renderer.tsxin objectui defaultsallDayFieldto the literalallDay, whereObjectCalendarhas no default and the spec forbids one. Checked and it is not a contradiction: that renderer serves thecalendar-viewnode with inlineschema.data, a different surface from a list view'scalendarblock, and it defaults every one of its five field names the same way. Noted, not filed.Generated by Claude Code