Skip to content

docs(data-modeling): stop crediting field format with validation - #19847

Merged
objectstack-fleet[bot] merged 1 commit into
mainfrom
claude/issue-19764-field-format-doc-rows
Sep 24, 2026
Merged

objectstack-fleet[bot] merged 1 commit into
mainfrom
claude/issue-19764-field-format-doc-rows

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #19764
Clause-②: no

Two hand-written data-modeling pages credited a field's format key with validation. The write-time record validator keys its email / url / phone shape checks on the field type and reads a field's format zero times; three rows also declared a format default that does not exist. Every rewritten row now names only behaviour a reader delivers.

Refs read: objectstack 245e161a (base 71ef2219), objectui pin 87af769e9a3e (the .objectui-sha on main when this was worked).

Rows: old text, new text, the reader that makes the new text true

# Row Old New Reader
1 field-types.mdx ### text, format "Validation format pattern" Display hint, not validation; the server runs no check from it. Lists the word set the UI resolver maps (phone/tel/telephone, email, url/uri/link, currency/money, percent/percentage); any other word renders as plain text; to reject malformed values use the field type or a format validation rule objectui packages/fields/src/index.tsx:2915 FORMAT_TO_RENDERER, :2929 TEXTUAL_BASE_TYPES, :2943-2944 promotion; pinned by packages/plugin-grid/src/__tests__/formatHintedColumnRenderer-8920.test.tsx:135. No-server-check: packages/objectql/src/validation/record-validator.ts reads def.format 0 times. Spec agreement: packages/spec/src/data/field.zod.ts:1090 describe
2 field-types.mdx ### phone, format "Phone format pattern" Row removed No reader: the resolver promotes only textual base types (:2943), and objectui packages/fields/src/widgets/PhoneField.tsx mentions format 0 times; record-validator :752 checks t === 'phone' with a fixed PHONE_RE (:108)
3 validation-rules.mdx ### text, format "Validates against format pattern (e.g., regex)" Not validated; a regex here is accepted and ignored; on text it is a display hint (links to the gallery); to constrain shape use the email/url/phone type or a format validation rule Same as row 1; the real regex enforcer is the format validation rule, packages/objectql/src/validation/rule-validator.ts:2765 checkFormat, documented at content/docs/data-modeling/validation.mdx:146
4 validation-rules.mdx ### email, format default email "Validates a basic local@domain shape" Row replaced by maxLength / minLength; the Default constraints line adds that the check keys on type: 'email' and a field-level format is not read record-validator :746 (t === 'email'), :91 EMAIL_RE; bounds :693 BOUNDED_STRING_FIELD_TYPES branch, :696 / :699; email is in that set (field.zod.ts:136)
5 validation-rules.mdx ### url, format default url "Validates URL format (protocol required)" Same shape as row 4, keyed on type: 'url' record-validator :749, :107 URL_RE; bounds as row 4
6 validation-rules.mdx ### phone, format default phone "Validates a permissive phone-number character set" Same shape as row 4, keyed on type: 'phone', plus a pointer to a format validation rule with a regex for a stricter shape record-validator :752, :108 PHONE_RE; bounds as row 4; checkFormat as row 3
7 (beyond the six) validation-rules.mdx Quick Validation Summary, text Key Constraints "maxLength, minLength, format, valueDomain" "maxLength, minLength, valueDomain (format is a display hint, not a constraint)" As rows 1 and 3

Six was a floor. Instrument for the census: git grep -nE for a backticked format, for format: 'email|url|phone|tel', and for Field.text({ ... format over content/docs/** minus references/ and releases/ (14 files hit). Control: backticked maxLength hits 14 times in validation-rules.mdx. Rows in the two pages: the six plus row 7 above. Autonumber: neither page documents the format reading on autonumber (both document autonumberFormat), so that meaning is untouched and nothing here contradicts field.zod.ts:1091.

The spec wins where they meet. These rows now agree with the landed format describe (field.zod.ts:1090-1094): no vocabulary, no server check off autonumber, a display hint the UI owns, and constrain values through type or a format validation rule.

Changeset

Docs-only. content/docs/** ships in no package's files[], so this is skip-changeset territory. Per the dispatch, no label write from this seat.

Verification (at 245e161a)

node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands derived 40 commands for this diff. All 40 exit 0. Four first exited 3 with PREREQUISITE NOT MET, which is not a measurement: check:doc-formula-expressions, check:doc-security-posture, check:skill-examples and check:docs-transcript-drift. After building @objectstack/spec, the @objectstack/lint closure, @objectstack/formula and @objectstack/client-react, they exited 0 when re-run. --ran reconciliation: "40 derived famil(ies) accounted for — 40 run, 0 NOT-MEASURED (a DERIVED zero — all 40 recorded an exit code and none of them is 3)". It includes check:doc-anchors (0) and check:nul-bytes (0). NOT MEASURED locally: the CI-only lanes the tool lists outside the 40, including Build Docs and the type-check lanes.

Acceptance notes

  • content/docs/api/error-catalog.mdx:201 (INVALID_FORMAT Fix line) says to match "the field's format constraint". That is the same false claim on another page. Out of this card's file surface, so it is not edited here. Class (b); dedupe words: INVALID_FORMAT, error-catalog, field format constraint.
  • content/docs/ui/forms.mdx:229 lists format among "object schema validators". It is ambiguous: it may name the format validation rule, which is real. Noted only.
  • textarea is in the resolver's TEXTUAL_BASE_TYPES, but its tables list no format row. No false claim, so nothing was added.

Generated by Claude Code

The write-time record validator keys its email/url/phone shape checks on
the field `type` and never reads a field's `format`. Six hand-written rows
(plus the quick-summary `text` row) said otherwise, and three declared a
`format` default that does not exist.

- `text` rows now say what the key is: a display hint read by the UI's
  cell-renderer resolver for a small word set, with no server-side check.
- The `phone` gallery row promised a pattern nothing implements; removed.
- `email` / `url` / `phone` validation tables list the bounds the
  validator does enforce and say the shape check keys on `type`.

Claude-Session: https://claude.ai/code/session_01VDtqoecgES7ScQYGbFVDRv
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 245e161ad0c526f2cecd97811fafc1987cf9a992

① Derived judgments

  • "the server runs no check from it" / "No write-time check reads it — a regex here is accepted and ignored" / "a field-level format key is not read": packages/objectql/src/validation/record-validator.ts contains def.format 0 times; :628 const t = def.type;; shape checks keyed on type at :746 (t === 'email'), :749 (url), :752 (phone). Spec agrees: packages/spec/src/data/field.zod.ts:1090-1094 describe ("On any other field type the server does not act on it … runs no check from it"; "any string parses on any field type"). True.
  • Word set and renderer effect on text: objectui at pin 87af769e9a3e (.objectui-sha on origin/main), packages/fields/src/index.tsx:2915-2927 FORMAT_TO_RENDERER = exactly phone/tel/telephone → phone, email → email, url/uri/link → url, currency/money → currency, percent/percentage → percent; :2929 TEXTUAL_BASE_TYPES includes text; :2941-2944 promotion only when word is mapped and base type textual, else falls back to the type ("any other word renders as plain text"). tel: anchor :2031, mailto: anchor :1945, UrlCellRenderer :1970. Pinned end-to-end by packages/plugin-grid/src/__tests__/formatHintedColumnRenderer-8920.test.tsx:135 (work_phone: { type: 'text', format: 'phone' }). True.
  • Removed phone-type format row: promotion applies to textual base types only (:2943); packages/fields/src/widgets/PhoneField.tsx at pin mentions format 0 times; validator uses fixed PHONE_RE :108. No reader; removal is right.
  • email/url/phone tables now list maxLength/minLength: BOUNDED_STRING_FIELD_TYPES (field.zod.ts:136 includes email, url, phone) branch at record-validator :693-699 enforces both bounds. True. Invented format defaults removed: FieldSchema.shape.format is z.string().optional() with no .default() (field.zod.ts:1090). True.
  • "format validation rule with a regex": packages/spec/src/data/validation.zod.ts:213 regex: z.string().optional(); packages/objectql/src/validation/rule-validator.ts:2765 checkFormat; link target #format-validation heading exists (content/docs/data-modeling/validation.mdx:146). True.
  • Regex descriptions kept verbatim (EMAIL_RE :91 local@domain-with-dot, URL_RE :107 protocol required, PHONE_RE :108 [+()\-\s\d.]{5,}). Pre-existing, still true.
  • Quick Summary row: text constraints now maxLength, minLength, valueDomain with format marked a display hint. Consistent with the above.
  • autonumber's format meaning (field.zod.ts:1091) not contradicted: neither page's touched rows concern autonumber.
  • No accept set or public surface touched; Clause-②: no is right. No generated/release/governed path; no packages/spec edit (fenced; landed with [finding] field.format is one z.string() key carrying THREE value vocabularies — the engine reads it as an autonumber pattern, objectui as a date display style, and its describe names a third that nothing honours #19679 / PR fix(spec): describe format by the readers that exist, not by email/phone #19763).

② Semver level

Docs-only content/docs/** (two files); not in any files[]; no .changeset/*.md. No changeset is correct; skip-changeset is the route.

③ Boundary flags

  • none crossed. The filer's own correction (format IS a live display hint on textual fields) is honoured: the text rows state the reader rather than deleting the key. Out-of-scope carriers the dev recorded (content/docs/api/error-catalog.mdx:201, content/docs/ui/forms.mdx:229) are outside the card's file surface and untouched.

Implemented-by: claude/issue-19764-field-format-doc-rows
Reviewed-by: session_01VDtqoecgES7ScQYGbFVDRv

VERDICT: PASS

Isolated at-tier reviewer, adopted by the domain:devx#1 seat. Reviewed with the seat's own ACCEPT excluded from its inputs.


Generated by Claude Code

@objectstack-fleet objectstack-fleet Bot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 24, 2026
@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review September 24, 2026 15:13
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Sep 24, 2026
Merged via the queue into main with commit a666e94 Sep 24, 2026
37 of 38 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-19764-field-format-doc-rows branch September 24, 2026 16:02
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Sep 28, 2026
…(field type or a format rule) (objectstack-ai#19878)

Fixes objectstack-ai#19848
Clause-②: no

## What changed

`content/docs/api/error-catalog.mdx` only.

1. **The `INVALID_FORMAT` entry.** Its **Fix** line told authors to
match "the field's `format` constraint". No write-time check reads a
field-level `format` key, so following that advice changes nothing. The
entry now says what actually decides:
- **No route emits the top-level `INVALID_FORMAT` today.** The entry now
says so and tells clients to branch on `VALIDATION_FAILED` +
`fields[].code`. That is the same shape the page's `INVALID_REFERENCE`
entry already uses.
- **The field `type`.** The built-in email / url / phone checks key on
`type` and answer `invalid_email` / `invalid_url` / `invalid_phone`.
Date and time parse failures answer `invalid_date` / `invalid_time`.
- **A `format` validation rule** (a different key: its `regex` or its
named `format` `email` | `url` | `phone` | `json`) answers field-level
`invalid_format`. The link goes to
`/docs/data-modeling/validation#format-validation`, the anchor PR objectstack-ai#19847
uses.
- Field-level `invalid_format` is also emitted for a missed declared
`pattern` outside record metadata: a settings value, or a request body a
route parses with Zod.
- The Fix line now names the field `type` or the `format` validation
rule as the things to change, and says a field-level `format` key runs
no write-time check on any field type.
2. **A bounded in-place fix in the same file (declared here).** The
`VALIDATION_ERROR` JSON example showed an email miss as `"code":
"invalid_format"`. The Zod mapper answers `invalid_email` for that miss.
See the Acceptance notes.

The wording follows PR objectstack-ai#19847 (still open at the time of writing; this
PR depends on none of its files) and the spec's `format` describe:
"keyed on `type`", "a field-level `format` key is not read", "a `format`
validation rule".

## Evidence (all at base `2bbb4623`)

| Claim | Where |
|:---|:---|
| Record validator never reads field `format`: `def.format` 0 hits,
same-file control `def.type` 7 |
`packages/objectql/src/validation/record-validator.ts` |
| email / url / phone checks key on `type`, emit `invalid_email` /
`invalid_url` / `invalid_phone` | `record-validator.ts:746-754` |
| date / time parse failures emit `invalid_date` / `invalid_time` |
`record-validator.ts:839`, `:858` |
| A `format` validation rule (regex or named format) emits field-level
`invalid_format` |
`packages/objectql/src/validation/rule-validator.ts:2776-2790` (check),
`:2822` (`formatViolation`) |
| Settings `pattern` miss emits field-level `invalid_format` |
`packages/services/service-settings/src/settings-service.ts:2042` |
| Zod-parsed routes: email to `invalid_email`, url to `invalid_url`,
other format/regex to `invalid_format` |
`packages/spec/src/api/zod-issues-to-fields.ts:82-85` |
| Top-level `INVALID_FORMAT` has no producer: `git grep INVALID_FORMAT`
outside tests and `dist` hits only the enum member
`packages/spec/src/api/errors.zod.ts:57`, the ADR note and the unpinned
baseline | `scripts/error-status-unpinned-baseline.json:15` ("documented
with an HTTP status that NO producer ... declares"); ADR-0114 line 37
records the six field-shaped top-level members as a known wart |
| Spec contract on the field key |
`packages/spec/src/data/field.zod.ts:1090-1094` (the `format` describe:
"the write-time record validator's built-in email, url and phone checks
key on `type`, never on this key") |

## Verification (final head `40758ef8`)

- `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack
--commands` derived **41** commands. I ran all 41 on `40758ef8`: 41
exited 0.
- Reconciliation: `--ran` printed `41 derived famil(ies) accounted for —
41 run, 0 NOT-MEASURED (a DERIVED zero — all 41 recorded an exit code
and none of them is 3)`.
- On the first pass (`d56a2a7f`), four gates exited **3 (PREREQUISITE
NOT MET)**: `check:doc-formula-expressions`,
`check:doc-security-posture`, `check:skill-examples` and
`check:docs-transcript-drift`. The lint, formula and client packages had
not been built yet. After those builds all four re-ran with exit 0.
- `pnpm --filter @objectstack/spec exec vitest run --maxWorkers=2
src/api/error-catalog-docs.test.ts` (the test that reads this page
against the wire face): `Test Files 1 passed (1) · Tests 5 passed (5)`
on `40758ef8`.
- Not measured, and owned by CI: the families `dispatch-gates` lists
outside its derived total, and the path-scheduled `Build Docs` / `Test
Core` jobs.

## Changeset

Docs-only. `content/docs/**` is not in any package's `files[]`, so this
PR publishes nothing and falls under `skip-changeset`. Per the dispatch,
this seat writes no labels.

## Acceptance notes

- **Bounded in-place fix (the `VALIDATION_ERROR` example
`invalid_format` → `invalid_email`).** All four exemption conditions
hold:
- same defect class (a docs line that says `format` where the real check
keys on the email type);
  - a mechanical, pinned form (`zod-issues-to-fields.ts:83`);
  - the file is this card's claimed file;
  - the same gate family.

It lies outside the claim's declared "(the `INVALID_FORMAT` entry)"
sub-surface. The claim's file surface needs this entry added.
- **`content/docs/ui/forms.mdx:229`** (`400 VALIDATION_FAILED` · "object
schema validators fail (`required`, `format`, `length`, …)"): read, not
edited. It lists kinds of constraint in the `When` column and gives no
fix, so it does not tell anyone to edit a field `format` key. It does
not carry the same false meaning. Not listed as a defect.
- **Sibling entries on the same page (a finding, not fixed here):**
`VALUE_TOO_LONG` and `VALUE_TOO_SHORT` also have no producer (`git grep`
outside tests/`dist`: 0 hits each; control `'VALIDATION_FAILED'`: 70).
Both appear in `scripts/error-status-unpinned-baseline.json`. The page
still documents them as live causes. The record validator answers
field-level `max_length` / `min_length` under `VALIDATION_FAILED`
instead. This is reported to the seat for filing and is out of scope for
this card.

---
_Generated by [Claude
Code](https://claude.ai/code/session_01VDtqoecgES7ScQYGbFVDRv)_

---------

Co-authored-by: Claude <noreply@anthropic.com>
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/s skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

1 participant