Skip to content

Commit a911cef

Browse files
qq9340100claude
andauthored
docs(spec): ledger the data-field step (slider) as UI-only and deliberately unenforced (#6769)
Fixes #6514 The ADR-0049 "ledger" half of the maintainer's 2026-08-08 ruling: `step` is the slider's UI increment, not a stored-value constraint. Recorded in three places that a future reader actually consults — the declaration site, the liveness ledger row, and the dead-surface disposition plan — with the reason enforcement was rejected and the shape it would take if it ever returns. Zero behaviour change: comments, a ledger note and a handwritten audit row. Claude-Session: https://claude.ai/code/session_01K94yzy5CVgC2JtrqYAuDk2 Co-authored-by: Claude <noreply@anthropic.com>
1 parent d42a92f commit a911cef

3 files changed

Lines changed: 19 additions & 1 deletion

File tree

docs/audits/2026-06-dead-surface-disposition-plan.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ No security implication; no roadmap; removing shrinks false surface. Grouped for
5959
**field (≈24)** — enhanced-type display config + redundant flags:
6060
`theme` / `lineNumbers` (code: only `language` live); `allowHalf` / `maxRating` (rating: `max` live); `displayMap` / `allowGeocoding` / `addressFormat`; `colorFormat` / `allowAlpha` / `presetColors`; `showValue` / `marks` (slider: min/max/step live); `barcodeFormat` / `qrErrorCorrection` / `displayValue` / `allowScanning`; `inlineTitle` / `inlineColumns` / `inlineAmountField` / `relatedList` / `relatedListTitle` / `relatedListColumns` (master-detail explicit overrides — auto-derivation works); `searchable` / `index` / `externalId` (field-level — superseded by object/dataset-level); `cached` / `dependencies` / `trackFeedHistory` / `caseSensitive`.
6161

62+
**✅ Ledgered 2026-08-08 — `field.step` (slider) is UI-ONLY and deliberately unenforced (#6514).** Not a prune row: it is an entry for a key that **stays declared**, added here because the paragraph above is where the next reader looks. The parenthetical "(slider: min/max/step live)" is a *renderer* verdict and had been read as an enforcement one; the two split. **Renderer-live:** objectui `packages/fields/src/widgets/SliderField.tsx:14` reads `field.step ?? 1` and passes it to the Slider, and `packages/spec/liveness/field.json` ledgers `step` as `live` on exactly that evidence — so it is *not* a prune candidate and never joins the pruned `showValue` / `marks` beside it. **Not a value constraint:** the numeric branch of `packages/objectql/src/validation/record-validator.ts` enforces `min`/`max` for `slider` and reads `step` nowhere; an off-grid stored value is accepted. Maintainer ruling 2026-08-08 — **ledger, do NOT enforce** (the ADR-0049 "ledger" half): the settings-side ruling (#6199 / PR #6501, which enforced that schema's `step`) does not transfer, because its hook was the settings schema's own "numeric bounds and step" comment grouping `step` with `min`/`max`, absent from this declaration ("Step increment for slider (default: 1)"); and enforcing a grid would create a stored-data hazard for zero measured demand — `record-validator` judges **updates to existing rows**, so already-stored off-grid values would start failing validation on their next edit. The only declaration in the repo is the showcase's `f_slider` (`min: 0, max: 100, step: 5`). Should grid enforcement gain real user pull, it returns as a feature request in PR #6501's shape: anchor at `min + k * step` (falling back to 0 when no `min` is declared) plus an epsilon-tolerant comparison. Mirrored as a comment beside the declaration in `packages/spec/src/data/field.zod.ts`.
63+
6264
**flow (5)**: `description` / `template` (no reader); `active` (deprecated, redundant with `status`); `nodes.outputSchema` (never validated); `nodes.boundaryConfig` (drives the dead BPMN `boundary_event`); `errorHandling.fallbackNodeId` (engine uses per-node fault edges). *Special:* `flow.status` — currently doesn't gate (engine uses an in-memory `flowEnabled` map); **WIRE** `status` → the enable map, or accept it's display-only and prune. Product call.
6365

6466
**tool (5)**: `category` / `requiresConfirmation` / `permissions` / `active` / `builtIn` — cosmetic on a write-only projection. Prune. *(The deeper question — make `tool` metadata authoritative vs stop projecting a schema that implies authorability — is a design decision, not a prune; track separately.)*

packages/spec/liveness/field.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,8 @@
136136
},
137137
"step": {
138138
"status": "live",
139-
"note": "slider — SliderField.tsx (min/max/step live)."
139+
"verifiedAt": "2026-08-08",
140+
"note": "CAVEAT — RENDERER-only: objectui SliderField.tsx:14 reads `field.step ?? 1`; the write path never validates the grid (record-validator.ts's numeric branch enforces min/max for slider and reads step nowhere). Ruled UI-only and deliberately unenforced 2026-08-08 (#6514) — see docs/audits/2026-06-dead-surface-disposition-plan.md (P2 field) and the comment beside the declaration in src/data/field.zod.ts."
140141
},
141142
"group": {
142143
"status": "live",

packages/spec/src/data/field.zod.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -713,6 +713,21 @@ export const FieldSchema = lazySchema(() => strictObject({
713713
// addressFormat, color colorFormat/allowAlpha/presetColors, slider showValue/marks,
714714
// barcode/qr barcodeFormat/qrErrorCorrection/displayValue/allowScanning.
715715
language: z.string().optional().describe('Programming language for syntax highlighting (e.g., javascript, python, sql)'),
716+
// `step` is the slider's **UI increment** and deliberately NOT a stored-value constraint —
717+
// ADR-0049's "ledger" half, ruled 2026-08-08 (#6514). Note it is renderer-LIVE, not dead,
718+
// which is why it is NOT in the pruned list above and never joins it: objectui's
719+
// `packages/fields/src/widgets/SliderField.tsx:14` reads it (`field.step ?? 1`) and hands it
720+
// to the Slider, and `packages/spec/liveness/field.json` ledgers it `live` on that evidence.
721+
// What it does not do is BIND the written value: the numeric branch of
722+
// `packages/objectql/src/validation/record-validator.ts` enforces `min`/`max` for `slider`
723+
// and reads `step` nowhere. The settings-side ruling (#6199 / PR #6501, which DID enforce a
724+
// grid) does not transfer: its hook was that schema's own "numeric bounds and step" comment
725+
// grouping `step` with `min`/`max`, which this declaration does not share — and enforcing a
726+
// grid here would make already-stored off-grid values start failing on their next edit,
727+
// because record-validator judges updates to existing rows. Should grid enforcement ever
728+
// gain real user pull it returns as a feature request in PR #6501's shape: anchor at
729+
// `min + k * step` (falling back to 0 when no `min` is declared), epsilon-tolerant
730+
// comparison. See docs/audits/2026-06-dead-surface-disposition-plan.md (P2 field prune).
716731
step: z.number().optional().describe('Step increment for slider (default: 1)'),
717732

718733
// Currency field config

0 commit comments

Comments
 (0)