docs(spec): colorField names a field to derive a colour FROM — timeline, calendar and gantt - #15470
Conversation
`TimelineConfigSchema`, `CalendarConfigSchema` and `GanttConfigSchema` each declare a `colorField`, and all three `.describe()` strings said only that the named field "determines" / "drives" the colour. That reads as "point this at a field that holds a colour" — the case the renderers need least. The common author intent is `colorField: 'status'`, a select field whose options already carry the colours. The renderers resolve it as a ladder (objectui#7243, shared as `createFieldColorResolver` in `@object-ui/core`, present at the console pin 00d3f09c): the option colour the field declares for the record's stored value, else the value itself when it already is a colour literal, else each renderer's own last rung — a semantic colour token (gantt), a theme-aware palette hash (calendar), the default marker (timeline). The three strings now say that, each naming its own last rung. No accept-set change: all three keys stay `z.string().optional()`, and `check:authorable-surface` needed no regeneration. The only regenerated follower is `content/docs/references/ui/view.mdx`. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G4138K1EG7kQ81FNba5Kp4
📓 Docs Drift CheckThis PR changes 1 package(s): ⛔ 1 release-owned page(s) name something this change touched. These are read-only:
What this run could not see
Coarse fallback — 129 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin e95251a6dee3989ee34356988d5e739196221037 && git checkout e95251a6dee3989ee34356988d5e739196221037
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin c3b6da4bca58e7d275ee0f81e5bd58c2591df093 1308bb1a1e97f2ee152b7f2cf15fd397767647ab && git checkout -B drift-repro c3b6da4bca58e7d275ee0f81e5bd58c2591df093 && git merge --no-ff 1308bb1a1e97f2ee152b7f2cf15fd397767647ab
node scripts/docs-audit/affected-docs.mjs --json c3b6da4bca58e7d275ee0f81e5bd58c2591df093
|
Fixes #14471
colorFieldis one authored key declared on three view config schemas, and all three.describe()strings said only that the named field "determines" or "drives" the colour. Read literally that invites pointing the key at a field whose stored value is a colour — the case the renderers need least. The common author intent iscolorField: 'status', a select field whose options already carry the colours, and that is the case the old prose described worst.What changed
Three
.describe()strings inpackages/spec/src/ui/view.zod.ts, each now naming the derivation and its own last rung:TimelineConfigSchemaField to determine item colorCalendarConfigSchemaField whose value determines the event colorGanttConfigSchemaField that drives the bar colorPlus the one regenerated follower,
content/docs/references/ui/view.mdx(9 table rows), and apatchchangeset.Three keys, not two — the card's own condition resolved that way
The card scoped itself to gantt and calendar and fenced the timeline conditionally: excluded "if its describe text already says the derivation — verify on
origin/mainbefore editing". Verified onorigin/mainee32e1cb8, by symbol rather than by line number:TimelineConfigSchema(declared:960),colorFieldat:968—'Field to determine item color'CalendarConfigSchema(declared:1296),colorFieldat:1303—'Field whose value determines the event color'GanttConfigSchema(declared:1338),colorFieldat:1347—'Field that drives the bar color'None of the three names derivation, and the timeline's is the vaguest of them. So the fence's condition is false and the timeline is in scope by the card's own wording, not by scope creep. Being first to get the behaviour right did not propagate to the prose — leaving it out would have re-created this card one key over.
The ladder is read from the code, not from a prose summary
Taken from the objectui source at the console pin
00d3f09c(packages/core/src/utils/record-color.ts, plus the three call sites), not from the card's summary. The sharedcreateFieldColorResolverimplements rungs 1 and 2 only; rung 3 deliberately stays with each renderer because each has a different right answer, which is why the three strings differ in their last clause:colorthe field declares for the record's stored value (keyed by optionvalueonly);rgborhsl;getSemanticHex(getSemanticColorName(...))inObjectGantt.tsx, the raw value handed toCalendarView.resolveEventColor's theme-aware 8-stop hash inObjectCalendar.tsx, the default marker inObjectTimeline.tsx.The pin was verified rather than assumed:
git hash-objectofrecord-color.tsat pin00d3f09cand at objectui's checked-outmainare the same blob,0af3a0a0bd4c00f980ae20e62990d3284f7ab78f.Nothing in the accept set moves
All three keys stay
z.string().optional(). The mechanical proof is thatcheck:authorable-surfacestayed green with no regeneration: of the 15 spec artifacts,check:generatedproved exactly one stale —content/docs/references/**— so the authorable surface and its JSON schemas are byte-identical. A doc snippet or example pointingcolorFieldat a plain hex field is still exactly as valid as before; that is rung 2, and the new text says so explicitly.Verification
All at the final commit
1308bb1a1, working tree clean, exit codes captured before any pipe.pnpm --filter '@objectstack/spec^...' build && pnpm --filter @objectstack/spec build— VERDICT command-exit 0pnpm --filter @objectstack/spec exec tsc --noEmit && pnpm --filter @objectstack/spec test— VERDICT command-exit 0; 471 test files passed (471), 12642 tests passed (12642)pnpm --filter @objectstack/spec check:generated— exit 0, all 15 artifacts current (re-run at1308bb1a1after the regeneration commit, not before it)dispatch-gates.mjs(derived from the diff, paths not hand-supplied), all exit 0:check:nul-bytes,check:corpus-claim-drift,check:doc-anchors,check:doc-authoring,check:docs-single-h1,check:quick-reference-counts,check:role-word,check:docs-audit-scope,check:spec-parsed-alias,check:objectui-changeset,check:pm-half-states,check:changeset-gate-self-testsRepo-wide
pnpm lintwas not run here; it is CI's, and the full farm runs on this PR regardless.No pin quotes these strings
Scanned for the three old strings and for their loose fragments across all tracked
.ts/.tsx/.json/.mjs/.mts— the only hits outsideview.zod.tsare inpackages/spec/json-schema/, which.gitignore:61ignores (it is the gitignored treecheck:authorable-surfacewrites). So there is nothing to move in the same PR, andcontent/docs/references/ui/view.mdxis the single tracked follower.Serial position
Only one file under
content/docs/references/**changed. If PR #15443 or PR #15456 lands first, this branch takesmainthroughbash scripts/pm/os-regen-merge.shand re-regenerates before it may enqueue.Out-of-scope finding
Filed separately as #15469, unassigned:
GanttConfigSchemaisstrictObject(...).passthrough()and is the only one of these three schemas that accepts an undeclared key, so a mistyped gantt key is silent while the same typo on calendar or timeline is a named error. Measured with two controls; not touched here, because either fix is an accept-set change this card forbids.🤖 Generated with Claude Code
https://claude.ai/code/session_01G4138K1EG7kQ81FNba5Kp4
Generated by Claude Code