Skip to content

docs(spec): ledger the data-field step (slider) as UI-only and deliberately unenforced (#6514) - #6769

Merged
qq9340100 merged 2 commits into
mainfrom
claude/issue-6514-slider-step-ledger
Aug 8, 2026
Merged

docs(spec): ledger the data-field step (slider) as UI-only and deliberately unenforced (#6514)#6769
qq9340100 merged 2 commits into
mainfrom
claude/issue-6514-slider-step-ledger

Conversation

@qq9340100

Copy link
Copy Markdown
Collaborator

Fixes #6514

The ADR-0049 ledger half of the maintainer's 2026-08-08 ruling: the data-field step (slider) is a UI increment, deliberately not a stored-value constraint. No enforcement, no behaviour change — comments, a liveness-ledger note and a handwritten audit row, so the next reader finds a verdict instead of silence.

Premise check (against origin/main, not the issue's snapshot)

All three premises hold, re-verified at a5d25734c (and again after merging main):

  • packages/objectql/src/validation/record-validator.ts still has no def.step reader. Its numeric branch (number | currency | percent | rating | slider) reads def.min and def.max only; git grep -n step origin/main -- packages/objectql/src/validation/ returns four hits, all prose (lock-step, "step 2" in rule-validator.ts).
  • step is still absent from the comment block above the declaration, and no one has enforced or ledgered it since filing.
  • The only declaration remains the showcase's f_slider (min: 0, max: 100, step: 5).

Measured, not asserted — driving the built validateRecord against exactly that field definition:

f_slider=5    (min 0, max 100, step 5) -> ACCEPTED
f_slider=7    (min 0, max 100, step 5) -> ACCEPTED
f_slider=7.3  (min 0, max 100, step 5) -> ACCEPTED
f_slider=-1   (min 0, max 100, step 5) -> REJECTED VALIDATION_FAILED
f_slider=101  (min 0, max 100, step 5) -> REJECTED VALIDATION_FAILED

That is the asymmetry the issue named, in one run: the declared window binds, the declared grid does not. Deliberately not committed as a test — a pin asserting "off-grid values are accepted" would lock in the non-enforcement and have to be deleted by the very feature request the ruling leaves the door open for.

One mechanism assumption falsified (the dispatch invited this)

The ruling's disposition was "add step to the runtime-ignored key list at field.zod.ts:691-695, beside showValue / marks". That block exists (now at 709-714, line numbers drifted) but it is not a list of surviving keys the runtime ignores — it is a pruned-keys tombstone:

// Pruned 2026-06 — per-type *display* knobs that were dead in both layers (no
// runtime reader; renderers ignore them). See
// docs/audits/2026-06-dead-surface-disposition-plan.md (P2 field prune): code
// theme/lineNumbers, ... slider showValue/marks, ...

showValue and marks are gone from the schema (git grep showValue -- packages/spec/src/ finds only this comment). Adding step to that list would therefore have stated that step was removed — false, and exactly the kind of ledger rot the liveness gate's ORPHAN direction exists to catch.

The second half of the falsification changes the verdict's shape, not its outcome: step is not dead surface at all. It is 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 already ledgers it live on that evidence. So the honest ledger is not "inert like showValue" but "live in the renderer, deliberately unbinding on the write path", which is a different fact and the one an author needs.

Implemented as the ruling's intent rather than its letter: a sibling comment on the surviving declaration, carrying the ruling, its reasoning, and the return path.

What changed (3 files, 19 insertions)

  • packages/spec/src/data/field.zod.ts — a comment above the step declaration: UI increment, not a value constraint; renderer-live (with the objectui file:line) which is why it is not in the pruned list above; record-validator.ts enforces min/max and reads step nowhere; why settings 的 step 是同族第五个声明但不执行的值约束键 —— 先要一个「它算不算值约束」的判断 #6199 / PR fix(service-settings): enforce the declared step grid on the settings write path and the env path (#6199) #6501 does not transfer (its hook was that schema's own "numeric bounds and step" grouping); the stored-data hazard (record-validator judges updates to existing rows, so stored off-grid values would start failing on their next edit); and the return path — PR fix(service-settings): enforce the declared step grid on the settings write path and the env path (#6199) #6501's shape, anchor at min + k * step falling back to 0 when no min is declared, epsilon-tolerant comparison. .describe() untouched, so nothing reader-visible regenerates.
  • docs/audits/2026-06-dead-surface-disposition-plan.md — the ledger row, appended after the P2 field paragraph. The historical paragraph is left verbatim (that doc's correction header says the sections are kept for history); the row instead splits the parenthetical that caused the ambiguity — "(slider: min/max/step live)" is a renderer verdict that had been read as an enforcement one.
  • packages/spec/liveness/field.json — one deliberate extension beyond the ruling's two files, flagged for review: step's row said "slider — SliderField.tsx (min/max/step live).", an unqualified live that is precisely the ambiguity this issue is about, and that row is where a future agent looks first. Rewritten with the file's own existing convention for live-but-not-on-the-write-path — unique carries "CAVEAT — DDL-only; NOT validated on the write path" and precision "CAVEAT — UI display formatting only" — plus verifiedAt: "2026-08-08". Status stays live; check:liveness green. Revert this one file if it reads as scope creep; the other two carry the ruling on their own.

Regeneration: MEASURED, and it is zero

Not inherited from #6723 (that was contracts/, this is data/). Ran the spec build and every generated-artifact gate on the edited tree:

  • check:generatedAll 10 generated artifacts are up to date
  • check:authorable-surface — green; authorable-defaults/ verified against upstream a5d25734c9a0, 1308 defaults unchanged. authorable-surface.base.json did not move, so the dispatch's STOP condition never fired.
  • check:docs 230 files in sync · check:skill-refs 9 in sync · check:skill-docs in sync · check:react-blocks 2 in sync · check:api-surface unchanged · check:spec-changes up to date · check:upgrade-guide up to date
  • git status after all of it: only the 3 files above.

A plain // comment (no .describe(), no TSDoc bound to a generator) regenerates nothing. One honest caveat: gen:authorable-surface-base refuses to run in this environment — a shallow checkout cannot establish the anchor's ancestry, so it exits 1 by design (#5235 / #5370). Pre-existing, unrelated to this change, and the gate CI actually runs (check:authorable-surface) is green.

Reverse verification: the template does not fit, and here is why

There is nothing to take out. The change adds no branch, no rule and no assertion, so no diagnostic can move in any direction — predicting red, extra-findings or inverted would all be fabrication. What was verifiable was the ledger's factual claim, and that is the probe above: restoring the claim's negation is impossible without writing the enforcement the ruling rejected. Reported as a blank rather than dressed up.

Gates + tests

Enumerated one by one from .github/workflows/lint.yml on the merged tree (not from memory) — all PASS: lint (ESLint), slot-lookup, query-options-erasure, verify-stand-in, nul-bytes, doc-authoring, docs-audit-scope, role-word, quick-reference-counts, adr-anchors, org-identifier, authz-resolver, service-providers, route-envelope, error-code-casing, wildcard-fallthrough, meta-type-normalized, init-service-contract, durability-log-level, startup-registry-verdict, objectui-changeset, release-notes, release-body, node-version, workflow-status-functions, shard-attestation, published-files, engine-double-contract, kernel-hook-pairs, resume-authority-declared, driver-memory-census, merge-driver, spec-parsed-alias, type-check-coverage, type-check-debt, driver-conformance, stall-guard, skill-frame-sync, skill-compatibility, i18n, i18n-coverage, app-nav-i18n, plus the spec set (generated, skill-docs, spec-changes, upgrade-guide, authorable-surface, docs, skill-refs, react-blocks, exported-any, dual-source-exports, skill-examples, api-surface) and @objectstack/lint check:doc-formula-expressions.

Also run, since I edited a liveness ledger — spec-liveness-check.yml's own set: check:liveness ✓ (every governed property classified, every live entry's evidence path resolves), check:empty-state ✓, check:variant-docs ✓, check:strictness-ledger ✓.

  • pnpm --filter @objectstack/spec test345 files / 8846 tests passed
  • pnpm --filter @objectstack/spec typecheck — green (incl. check:test-typecheck)
  • turbo run typecheck over packages + apps — 120 tasks, all green; turbo run build — 70 tasks green; examples + downstream-contract typecheck green

Three gates (check:i18n, check:i18n-coverage, check:app-nav-i18n) first reported PREREQUISITE NOT MET in the fresh worktree — the missing-build trap, not a failure. Re-run after building the workspace: all three green.

Merge with main

origin/main moved 12 commits mid-flight, including the sibling spec PR #6705 (api/export.zod.ts describe + regenerated export.mdx). Followed the four-step sequence — git merge origin/main (never rebase), no conflicts on any os-regen path, merge committed before any regeneration, then the generated-artifact gates re-run on the merged tree (check:generated 10/10). Asserted both sides survived: #6705's dryRun prose is present in content/docs/references/api/export.mdx, and this PR's three entries are intact.

Changeset

None, deliberately. No published behaviour changes: a source comment, an internal ledger note and an internal audit doc under docs/audits/ (not content/docs/), with zero regenerated product in the diff — so there is nothing for a release note to say. That is the measurement's answer, not a preference; #6705's patch-changeset precedent applies to a diff that carries reader-visible regenerated docs, and this one does not. skip-changeset applied to this PR.

content/docs/releases/ untouched.


Generated by Claude Code

claude added 2 commits August 8, 2026 14:38
…berately unenforced

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.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K94yzy5CVgC2JtrqYAuDk2
@vercel

vercel Bot commented Aug 8, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectstack Ignored Ignored Aug 8, 2026 2:54pm

Request Review

@github-actions github-actions Bot added the size/s label Aug 8, 2026
@qq9340100 qq9340100 added skip-changeset PR has no user-facing published change; bypasses the changeset gate and removed size/s labels Aug 8, 2026 — with Claude
@github-actions

github-actions Bot commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/spec.

112 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/ai/agents.mdx (via @objectstack/spec)
  • content/docs/ai/skills-reference.mdx (via @objectstack/spec)
  • content/docs/ai/skills.mdx (via @objectstack/spec)
  • content/docs/api/client-sdk.mdx (via @objectstack/spec)
  • content/docs/api/environment-routing.mdx (via @objectstack/spec)
  • content/docs/api/error-catalog.mdx (via @objectstack/spec)
  • content/docs/api/error-handling-client.mdx (via @objectstack/spec)
  • content/docs/api/error-handling-server.mdx (via @objectstack/spec)
  • content/docs/api/index.mdx (via @objectstack/spec)
  • content/docs/automation/approvals.mdx (via @objectstack/spec)
  • content/docs/automation/connectors.mdx (via @objectstack/spec)
  • content/docs/automation/flows.mdx (via @objectstack/spec)
  • content/docs/automation/hook-bodies.mdx (via packages/spec)
  • content/docs/automation/hooks.mdx (via @objectstack/spec)
  • content/docs/automation/index.mdx (via @objectstack/spec)
  • content/docs/automation/webhooks.mdx (via @objectstack/spec)
  • content/docs/automation/workflows.mdx (via @objectstack/spec)
  • content/docs/concepts/architecture.mdx (via @objectstack/spec)
  • content/docs/concepts/design-principles.mdx (via packages/spec)
  • content/docs/concepts/index.mdx (via @objectstack/spec)
  • content/docs/concepts/metadata-driven.mdx (via @objectstack/spec)
  • content/docs/concepts/metadata-lifecycle.mdx (via packages/spec)
  • content/docs/concepts/north-star.mdx (via @objectstack/spec)
  • content/docs/data-modeling/analytics.mdx (via @objectstack/spec)
  • content/docs/data-modeling/drivers.mdx (via @objectstack/spec)
  • content/docs/data-modeling/external-datasources.mdx (via @objectstack/spec)
  • content/docs/data-modeling/field-types.mdx (via @objectstack/spec)
  • content/docs/data-modeling/fields.mdx (via @objectstack/spec)
  • content/docs/data-modeling/formulas.mdx (via @objectstack/spec)
  • content/docs/data-modeling/index.mdx (via @objectstack/spec)
  • content/docs/data-modeling/objects.mdx (via @objectstack/spec)
  • content/docs/data-modeling/queries.mdx (via @objectstack/spec)
  • content/docs/data-modeling/schema-design.mdx (via @objectstack/spec)
  • content/docs/data-modeling/seed-data.mdx (via @objectstack/spec)
  • content/docs/data-modeling/validation-rules.mdx (via @objectstack/spec)
  • content/docs/data-modeling/validation.mdx (via @objectstack/spec)
  • content/docs/deployment/cli.mdx (via @objectstack/spec)
  • content/docs/deployment/tenancy-modes.mdx (via @objectstack/spec)
  • content/docs/deployment/troubleshooting.mdx (via @objectstack/spec)
  • content/docs/deployment/validating-metadata.mdx (via @objectstack/spec)
  • content/docs/getting-started/build-with-claude-code.mdx (via @objectstack/spec)
  • content/docs/getting-started/common-patterns.mdx (via @objectstack/spec)
  • content/docs/getting-started/examples.mdx (via @objectstack/spec)
  • content/docs/getting-started/quick-reference.mdx (via @objectstack/spec)
  • content/docs/getting-started/quick-start.mdx (via @objectstack/spec)
  • content/docs/getting-started/your-first-project.mdx (via @objectstack/spec)
  • content/docs/kernel/cluster.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/auth-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/cache-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/data-engine.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/index.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/metadata-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/storage-service.mdx (via @objectstack/spec)
  • content/docs/kernel/index.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/data-service.mdx (via @objectstack/spec)
  • content/docs/kernel/runtime-services/email-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/examples.mdx (via @objectstack/spec)
  • content/docs/kernel/runtime-services/index.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/queue-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/sharing-service.mdx (via @objectstack/spec)
  • content/docs/kernel/runtime-services/sms-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/storage-service.mdx (via @objectstack/spec)
  • content/docs/kernel/services-checklist.mdx (via @objectstack/spec)
  • content/docs/kernel/services.mdx (via @objectstack/spec)
  • content/docs/permissions/authorization.mdx (via @objectstack/spec)
  • content/docs/permissions/permission-sets.mdx (via @objectstack/spec)
  • content/docs/permissions/permissions-matrix.mdx (via @objectstack/spec)
  • content/docs/permissions/positions.mdx (via @objectstack/spec)
  • content/docs/permissions/rls.mdx (via @objectstack/spec)
  • content/docs/permissions/sharing-rules.mdx (via @objectstack/spec)
  • content/docs/plugins/adding-a-metadata-type.mdx (via @objectstack/spec)
  • content/docs/plugins/development.mdx (via @objectstack/spec)
  • content/docs/plugins/index.mdx (via @objectstack/spec)
  • content/docs/plugins/packages.mdx (via @objectstack/spec)
  • content/docs/protocol/backward-compatibility.mdx (via @objectstack/spec)
  • content/docs/protocol/diagram.mdx (via packages/spec)
  • content/docs/protocol/kernel/config-resolution.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/http-protocol.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/i18n-standard.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/index.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/lifecycle.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/plugin-spec.mdx (via @objectstack/spec)
  • content/docs/protocol/knowledge.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/index.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/query-syntax.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/schema.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/security.mdx (via packages/spec)
  • content/docs/protocol/objectql/state-machine.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/actions.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/concept.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/index.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/layout-dsl.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/record-alert.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/widget-contract.mdx (via @objectstack/spec)
  • content/docs/releases/implementation-status.mdx (via @objectstack/spec)
  • content/docs/releases/index.mdx (via @objectstack/spec)
  • content/docs/releases/v12.mdx (via @objectstack/spec)
  • content/docs/releases/v13.mdx (via @objectstack/spec)
  • content/docs/releases/v16.mdx (via @objectstack/spec)
  • content/docs/releases/v17.mdx (via @objectstack/spec)
  • content/docs/releases/v9.mdx (via @objectstack/spec)
  • content/docs/ui/actions.mdx (via @objectstack/spec)
  • content/docs/ui/apps.mdx (via @objectstack/spec)
  • content/docs/ui/create-vs-edit-form.mdx (via @objectstack/spec)
  • content/docs/ui/dashboards.mdx (via @objectstack/spec)
  • content/docs/ui/field-grouping-and-order.mdx (via @objectstack/spec)
  • content/docs/ui/forms.mdx (via @objectstack/spec)
  • content/docs/ui/index.mdx (via @objectstack/spec)
  • content/docs/ui/public-data-collection.mdx (via @objectstack/spec)
  • content/docs/ui/setup-app.mdx (via @objectstack/spec)
  • content/docs/ui/translations.mdx (via @objectstack/spec)
  • content/docs/ui/views.mdx (via @objectstack/spec)

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

@github-actions github-actions Bot added documentation Improvements or additions to documentation protocol:data labels Aug 8, 2026
@qq9340100
qq9340100 marked this pull request as ready for review August 8, 2026 15:16
@qq9340100
qq9340100 added this pull request to the merge queue Aug 8, 2026
Merged via the queue into main with commit a911cef Aug 8, 2026
32 checks passed
@qq9340100
qq9340100 deleted the claude/issue-6514-slider-step-ledger branch August 8, 2026 15:32
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:data skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

The data-field step (slider) is the settings step's unenforced twin — record-validator.ts reads min/max and never step

2 participants