Skip to content

fix(spec): the gantt, timeline and map binding warnings describe the refusals objectui left, not the floors it deleted - #19788

Merged
objectstack-fleet[bot] merged 1 commit into
mainfrom
claude/issue-19630-view-binding-rows-gantt-timeline-map
Sep 23, 2026
Merged

objectstack-fleet[bot] merged 1 commit into
mainfrom
claude/issue-19630-view-binding-rows-gantt-timeline-map

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #19630

Clause-②: no — author-facing warning text and its premise corrected to what the renderer does; the warning class is held by the #16577 ruling (B), no accept/reject change, no public surface added

What was wrong

Three VIEW_BINDING_BLOCKS rows in packages/spec/src/kernel/functional-completeness.ts stated renderer fallbacks that objectui has deleted, and the warning checkViewCompleteness prints on os validate for those three types inherited the generic body built on the same premise ("the renderer falls back to literal default field names ... the view renders empty"). For map the stale literal locationField || 'location' had three carriers: the row, the block-present map message (which quoted it as the renderer's read), and the functional-completeness.test.ts pin that held it.

The re-derivation, at the pin

The card's verdicts were measured at objectui main 0cf2d6644. They were not inherited: all three were re-derived at the objectui pin this repo ships, .objectui-sha = 87af769e9a3e (read on origin/main at 43e17b8905), from a read-only fetch of that exact commit. Every verdict held at the pin, so there is no fork to report:

  • gantt — FALSE (confirmed). plugin-list/src/ListView.tsx case 'gantt' spreads startDateField / endDateField / titleField only when declared (the date floors deleted by objectui#7070, the progress / dependencies floors by objectui#7499, omitted rather than refused). plugin-gantt/src/ObjectGantt.tsx getGanttConfig takes its flat branch only with both dates, otherwise returns null, and the if (!ganttConfig) arm renders "Gantt configuration required. Please specify startDateField, endDateField, and titleField." A refusal by name, not a blank chart.
  • timeline — HALF FALSE (confirmed). case 'timeline' resolves the axis via resolveTimelineDateBinding and spreads it only when declared; the 'created_at' floor is gone (objectui#7070 step three, on the ruling 日期轴永不虚构), while titleField: dateBinding.titleField || 'name' is kept on purpose. plugin-timeline/src/ObjectTimeline.tsx ends its start-date chain with no literal rung and, with no authored items and no start date, renders the timeline.unconfigured.noDateAxis refusal ("Timeline date axis required — ...", data-testid="timeline-missing-date-axis").
  • map — FALSE (confirmed). case 'map' forwards resolveListMapConfig(schema) with no locationField || 'location' floor (objectui#8169). plugin-map/src/ObjectMap.tsx getMapConfig's unconfigured branch returns only { style } (no guessed coordinate names) and hasCoordinateBinding — applied to every branch, a declared block included — renders "Map configuration required — declare map.locationField or map.latitudeField + map.longitudeField". Zero 'location' literals remain in ObjectMap.tsx at the pin; the one in ListView.tsx is the tombstone comment.
  • kanban (groupByField || groupField || detectStatusField(objectDef)) and tree (labelField || titleField || 'name') were re-read at the pin and still hold. The plugin-view / app-shell ObjectView.tsx faces were grepped at the pin too: they carry the same deletions as tombstone comments, no live floor.

What this PR changes

  • The three rows now state what the pin shows, name both halves of each path, and each carries an asserting pin citation (.objectui-sha = 87af769e9), so the next pin bump reds on them rather than letting them rot. check:objectui-pin-citations lists 4 asserting citations in this file (1 before, 3 new), 46 repo-wide.
  • The re-measurement note that recorded the three as stale ("do not cite them") is replaced by a short history: which card corrected which rows, and at which ref.
  • Per-type bodies. VIEW_BINDING_MESSAGE gains gantt, timeline and map entries beside calendar, each written from its row's reading. The generic unboundBlockMessage body now reaches only kanban and tree, the two rows it is still true of.
  • The block-present map message no longer quotes locationField || 'location'; it names the hasCoordinateBinding refusal and the keys that clear it.
  • The prescription is preserved on every body: gantt names gantt.startDateField / gantt.endDateField / gantt.titleField (the three GanttConfigSchema requires); timeline names timeline.startDateField / timeline.titleField (the two TimelineConfigSchema requires) and says only the title has a renderer default; map names map.locationField or the lat/lng pair. VIEW_BINDING_FIX is unchanged: all three hints were already correct.
  • No severity moves and no firing moves. Every route stays warning; the same six view types warn on the same inputs. That is consistent with spec(ui): the list-view calendar guard gates on appearance.allowedVisualizations ONLY — type: 'calendar' with no calendar: block parses CLEAN at all three doors #16577's ruling B (comment 5634033966) for the calendar route, whose reasoning is that both doors are loud; these rows now measure the same two-door shape. The docblock says explicitly that this is consistency with the ruling, not an extension of it, and it does not reopen it.

Tests

  • The map pin at the old :221 was re-judged in place, not dropped: it now asserts the literal is absent (not.toContain("|| 'location'")) and that the body names Map configuration required, hasCoordinateBinding, map.locationField and map.latitudeField.
  • New it.each over gantt / timeline / map: no literal-fallback sentence, the renderer's refusal text, the component file, every key the refusal names, severity warning.
  • The generic-body pin shrinks from five types to kanban and tree.

Reverse verification (one-shot, not kept). With the fix committed (c0131a1325), functional-completeness.ts was replaced on disk by its 43e17b8905 blob (landing proven: old-literal count 1, new Gantt configuration required count 0) and the test file run against it: 4 failed / 61 passed, exit 1: the three new per-type pins plus the re-judged map pin. Direction: red, as predicted. Restored with git checkout HEAD -- PATH under an EXIT/INT/TERM trap; restoration proven by git hash-object equal to the HEAD blob (57a4b064e8) and an empty git diff HEAD. The test imports ./functional-completeness from source, so no dist rebuild sat between mutation and measurement.

Validation — exit codes captured before any pipe, head c0131a1325

  • pnpm --filter @objectstack/spec build · vitest run src/kernel/functional-completeness.test.ts (65 passed) · pnpm --filter @objectstack/spec typecheck: one locked run, VERDICT command-exit 0.
  • pnpm --filter "@objectstack/lint..." --filter "@objectstack/objectql..." build: 0; then @objectstack/lint vitest run src/validate-functional-completeness.test.ts: 0 (25 passed). That suite asserts rule ids and paths, not message text.
  • pnpm --filter @objectstack/spec check:generated: 0.
  • node scripts/check-issue-citations.mjs --base origin/main: 0 (diff-scoped half; pnpm check:issue-citations is also in the derived set, 0).
  • pnpm exec eslint --no-inline-config --format json over the two changed sources: 0, 2 files, 0 errors, 0 warnings. That is a proven narrowing, not a skipped lint: eslint.config.mjs enables no type-aware linting (no parserOptions.project, no typed rules; its own comment at :327-328 says so), so this diff cannot change the verdict on any file it does not touch. The repo-wide pnpm lint is CI's.
  • node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived 82 commands; all 82 were run, each with its exit code recorded. --ran reconciles: 82 derived, 80 run, 2 NOT MEASURED, 0 UNRUN. The two NOT MEASURED are pnpm check:dual-build-cjs-loads and pnpm check:type-check-debt, both exit 3 PREREQUISITE NOT MET because they need a full ./packages/* build. That is out of reach under the foreground cap, so it is declared for CI. check:doc-formula-expressions and check:lean-entry-closure first exited 3 (unbuilt closure); both were re-run after the closure build above and returned 0. check:objectui-pin-citations: 0 (46 asserting match the pin, 37 historical).

Acceptance notes

  • packages/lint/src/validate-list-view-field-refs.ts (its docblock near :26-31) paraphrases the old premise of view/layout-without-binding ("the renderer then falls back to literal default field names and the view renders empty"). It is a comment in another package, outside this card's file surface. Noted, not filed; carrier: none.
  • resolveTimelineDateBinding at the pin also reads a calendar block's startDateField as a timeline axis, so a type: 'timeline' view that carries a calendar block and no timeline block renders, and still gets this warning. That is unchanged by this PR (firing did not move), and the row now records it. Observation only.
  • Released CHANGELOG.md entries still carry the old generic sentence. Released entries are amended only in a dedicated docs-only PR, never as a rider.
  • Labels: the dispatch names none and a changeset is present, so no label was written.

Generated by Claude Code

…refusals objectui left, not the floors it deleted

Re-derived at the objectui pin (87af769e9): the three VIEW_BINDING_BLOCKS
rows, their per-type warning bodies, the block-present map message and
its test pin now state the named refusal screens, with asserting pin
citations. No severity or firing change.

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

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/spec, touching 3 documentable anchor(s).

9 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:

  • content/docs/automation/flows.mdx (via end_date (literal, a string literal in a comment on a changed line))
  • content/docs/automation/hooks.mdx (via end_date (literal, a string literal in a comment on a changed line), start_date (literal, a string literal in a comment on a changed line))
  • content/docs/data-modeling/fields.mdx (via start_date (literal, a string literal in a comment on a changed line))
  • content/docs/data-modeling/validation.mdx (via end_date (literal, a string literal in a comment on a changed line), start_date (literal, a string literal in a comment on a changed line))
  • content/docs/protocol/objectql/index.mdx (via end_date (literal, a string literal in a comment on a changed line), start_date (literal, a string literal in a comment on a changed line))
  • content/docs/protocol/objectql/schema.mdx (via start_date (literal, a string literal in a comment on a changed line))
  • content/docs/protocol/objectui/index.mdx (via end_date (literal, a string literal in a comment on a changed line), start_date (literal, a string literal in a comment on a changed line))
  • content/docs/protocol/objectui/layout-dsl.mdx (via start_date (literal, a string literal in a comment on a changed line))
  • content/docs/ui/views.mdx (via end_date (literal, a string literal in a comment on a changed line), start_date (literal, a string literal in a comment on a changed line))

1 release-owned page(s) also name something this change touched. These are read-only:

  • content/docs/releases/v16.mdx (via end_date (literal, a string literal in a comment on a changed line))

content/docs/releases/ is RELEASE-OWNED (AGENTS.md "Documentation Guardrails"): release
notes are written centrally at release time, and a code PR that edits them is the exact PR
that guardrail exists to stop. They are still audited — read-only. If one of them is actually
wrong, file an issue or open a dedicated docs-only PR; do not edit it here.

What this run could not see
  • 1 anchor(s) matched too much of the corpus to be a work list: created_at (literal, 34 pages)
  • 3 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 60 of 215 client-bound route-ledger rows — the other 155 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 155: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 55 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 100 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • 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.
  • a key NAME is not a key, so the hand re-read the line above prescribes can land on the wrong schema. The same spelling is authorable on one governed type and a [REMOVED] tombstone on another for each of active, aria, joins, objects, template, tools and version (censused on [finding] tools is a key on BOTH AgentSchema (tombstoned, dead) and SkillSchema (live, cloud-attested), so a name-based search attributes skill examples to the agent key — it produced a false stop-the-line alarm on PR #19059 #19093 over the liveness ledger's governed types, top-level keys); nothing in a search result distinguishes the two, so a grep hit on a LIVE example reads as evidence about the DEAD key. Measured on fix(spec): the agent.tools liveness row says dead — it claimed live on a key the schema tombstoned #19059: content/docs/ai/agents.mdx was reported as contradicting the agent.tools tombstone over its tools: example at :161, which is inside the defineSkill({ block opened at :155 — the page was already correct. Settle ownership by PARSING the value against both schemas, never by the name: that literal PASSES SkillSchema, and as an AgentSchema it FAILS at tools with the tombstone prescription. ⛔ These names are not the whole class — a key retired through a .strict() guidance map leaves no tombstone in the walked shape and none of them here (tool.category, live as AIToolDefinition.category).

Coarse fallback — 136 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 cc2e9511c84823e81219be092e6b285a76b36787packageMentionDocs.

Which tree this was computed on

This run read content/docs from 4aa7d38eb8fb7a3de4888736cfd4b5b35c1950e0 — the merge of head c0131a1325c6a6d86cc1e1812cc2aaa9f9fcd177 into base cc2e9511c84823e81219be092e6b285a76b36787, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 4aa7d38eb8fb7a3de4888736cfd4b5b35c1950e0 && git checkout 4aa7d38eb8fb7a3de4888736cfd4b5b35c1950e0
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin cc2e9511c84823e81219be092e6b285a76b36787 c0131a1325c6a6d86cc1e1812cc2aaa9f9fcd177 && git checkout -B drift-repro cc2e9511c84823e81219be092e6b285a76b36787 && git merge --no-ff c0131a1325c6a6d86cc1e1812cc2aaa9f9fcd177

node scripts/docs-audit/affected-docs.mjs --json cc2e9511c84823e81219be092e6b285a76b36787

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs cc2e9511c84823e81219be092e6b285a76b36787 → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: c0131a1325c6a6d86cc1e1812cc2aaa9f9fcd177

① Derived judgments

Re-derived at the objectui pin .objectui-sha = 87af769e9a3ee28ace099fdd653d3ebd79fe82e2 (a read-only checkout of that exact commit), ⛔ not from the card or the PR body.

  • gantt row + body — TRUE: ListView.tsx case 'gantt' spreads bindings only when declared, no date/progress/dependencies literal floor (tombstones only); ObjectGantt.getGanttConfig returns null without a gantt block or both flat dates and renders 「Gantt configuration required …」; GanttConfigSchema requires exactly startDateField / endDateField / titleField.
  • timeline row + body — TRUE, half handled right: the 'created_at' floor is gone (tombstone quoting 日期轴永不虚构), titleField || 'name' still stands; ObjectTimeline renders timeline-missing-date-axis; TimelineConfigSchema requires startDateField + titleField.
  • map row, absent-block body, block-present message — TRUE: no live 'location' literal in ObjectMap.tsx (seat re-read: 0); hasCoordinateBinding = locationField or both lat/lng, applied after every branch; renders map-missing-location-binding.
  • kanban / tree keep the generic body — TRUE (tree still floors labelField … || 'name').
  • No accept/reject or severity change: VIEW_BINDING_BLOCKS keys, VIEW_BINDING_FIX, every severity: 'warning', every rule/path byte-unchanged; message strings only.
  • Re-judged map pin asserts NEW substance (negative on the literal + positive on the refusal text and the prescribed keys), ⛔ not a deletion.
  • Pin citations: check-objectui-pin-citations --list reads functional-completeness.ts:282,287,299,315 all asserting 87af769e9 current; a simulated .objectui-sha bump (isolated worktree, restored) exits 1 naming exactly those four rows ⇒ the next bump reds on them.
  • spec(ui): the list-view calendar guard gates on appearance.allowedVisualizations ONLY — type: 'calendar' with no calendar: block parses CLEAN at all three doors #16577 ruling B — consistent, not reopened; the docblock scopes B to the calendar route.

② Semver level

patch, as precedent #19623 (ec2ede0888); Clause-②: no in changeset and PR body. Changeset prose checked line by line against the pin — true.

③ Boundary flags

  • Scope: exactly three files — the card's surface plus the two carriers triage required (map message, map pin). No governed surface.
  • Lint docblock paraphrase (packages/lint/src/validate-list-view-field-refs.ts) — internal comment drift, not (a)/(b)/(c); noted-not-filed is right.
  • Timeline warning with only a calendar block — the warning is right to fire spec-side (the named timeline block is absent and required); observation only.
  • Nits (no action): the timeline body quotes the default-locale refusal text; a table intro still says 「re-measurement note」 for a section now titled history.

Implemented-by: claude/issue-19630-view-binding-rows-gantt-timeline-map
Reviewed-by: session_01VWsFyWDp8Rjb2Ma6a3Cyo8

VERDICT: PASS

Rendered by an isolated at-tier review subagent (card, prior ruling, precedent PR, the PR and its check-runs — not the dispatch order), adopted by domain:spec seat 4 after re-reading two pin readings. Checks at review time on this head: 32 success, 3 path-filter / opt-in skips, 0 failures.


Generated by Claude Code

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 size/m tests tooling

Projects

None yet

1 participant