Skip to content

docs(spec): colorField names a field to derive a colour FROM — timeline, calendar and gantt - #15470

Merged
os-sales merged 1 commit into
mainfrom
claude/issue-14471-colorfield-derive-describe
Sep 4, 2026
Merged

docs(spec): colorField names a field to derive a colour FROM — timeline, calendar and gantt#15470
os-sales merged 1 commit into
mainfrom
claude/issue-14471-colorfield-derive-describe

Conversation

@os-sales

@os-sales os-sales commented Sep 4, 2026

Copy link
Copy Markdown
Collaborator

Fixes #14471

colorField is 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 is colorField: '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 in packages/spec/src/ui/view.zod.ts, each now naming the derivation and its own last rung:

schema before after
TimelineConfigSchema Field to determine item color derive-from + option colour / colour literal / timeline default marker colour
CalendarConfigSchema Field whose value determines the event color derive-from + option colour / colour literal / theme-aware palette colour hashed from the value
GanttConfigSchema Field that drives the bar color derive-from + option colour / colour literal / semantic colour token derived from the value

Plus the one regenerated follower, content/docs/references/ui/view.mdx (9 table rows), and a patch changeset.

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/main before editing". Verified on origin/main ee32e1cb8, by symbol rather than by line number:

  • TimelineConfigSchema (declared :960), colorField at :968'Field to determine item color'
  • CalendarConfigSchema (declared :1296), colorField at :1303'Field whose value determines the event color'
  • GanttConfigSchema (declared :1338), colorField at :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 shared createFieldColorResolver implements 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:

  1. the option color the field declares for the record's stored value (keyed by option value only);
  2. else the value itself when it already is a colour literal — hex 3/6/8 digits, or a string starting rgb or hsl;
  3. else the caller's own rung: getSemanticHex(getSemanticColorName(...)) in ObjectGantt.tsx, the raw value handed to CalendarView.resolveEventColor's theme-aware 8-stop hash in ObjectCalendar.tsx, the default marker in ObjectTimeline.tsx.

The pin was verified rather than assumed: git hash-object of record-color.ts at pin 00d3f09c and at objectui's checked-out main are the same blob, 0af3a0a0bd4c00f980ae20e62990d3284f7ab78f.

Nothing in the accept set moves

All three keys stay z.string().optional(). The mechanical proof is that check:authorable-surface stayed green with no regeneration: of the 15 spec artifacts, check:generated proved exactly one stale — content/docs/references/** — so the authorable surface and its JSON schemas are byte-identical. A doc snippet or example pointing colorField at 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 0
  • pnpm --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 at 1308bb1a1 after the regeneration commit, not before it)
  • Repo-root gates from 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-tests
  • Control-byte self-scan over the three changed files: no hits

Repo-wide pnpm lint was 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 outside view.zod.ts are in packages/spec/json-schema/, which .gitignore:61 ignores (it is the gitignored tree check:authorable-surface writes). So there is nothing to move in the same PR, and content/docs/references/ui/view.mdx is the single tracked follower.

Serial position

Only one file under content/docs/references/** changed. If PR #15443 or PR #15456 lands first, this branch takes main through bash scripts/pm/os-regen-merge.sh and re-regenerates before it may enqueue.

Out-of-scope finding

Filed separately as #15469, unassigned: GanttConfigSchema is strictObject(...).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

`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
@github-actions github-actions Bot added size/s documentation Improvements or additions to documentation protocol:ui tooling labels Sep 4, 2026
@github-actions

github-actions Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

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

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

  • content/docs/releases/v17.mdx (via GanttConfigSchema (symbol, a top-level const))

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
  • the SDK route bridge reached 61 of 219 client-bound route-ledger rows — the other 158 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 158: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 56 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 102 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.

Coarse fallback — 129 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 c3b6da4bca58e7d275ee0f81e5bd58c2591df093packageMentionDocs.

Which tree this was computed on

This run read content/docs from e95251a6dee3989ee34356988d5e739196221037 — the merge of head 1308bb1a1e97f2ee152b7f2cf15fd397767647ab into base c3b6da4bca58e7d275ee0f81e5bd58c2591df093, 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 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

⚠️ 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 c3b6da4bca58e7d275ee0f81e5bd58c2591df093 → pass the list as
args.docs, on the commit named under Which tree this was computed on.

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 protocol:ui size/s tooling

Projects

None yet

2 participants