Skip to content

Commit 196b217

Browse files
committed
chore(changeset): minor + BREAKING for @objectstack/objectql - a system write's readonly value is judged for its shape
Claude-Session: https://claude.ai/code/session_017ErfyP2Rx7XWHJA27QjyUi Co-authored-by: Claude <noreply@anthropic.com>
1 parent 760678e commit 196b217

1 file changed

Lines changed: 51 additions & 0 deletions

File tree

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
---
2+
'@objectstack/objectql': minor
3+
---
4+
5+
fix(objectql)!: a system write's readonly value is judged for its shape — a seed's `'yesterday'` on a readonly datetime is refused with the sentence any other field gets, never stored (#21663)
6+
7+
**BREAKING** — a write that keeps a readonly value now has that value's SHAPE
8+
checked. The static readonly strip still exempts a system write (seed replay,
9+
migration, `isSystem` plugin code, a `before*` hook's stamp) and still drops a
10+
non-system caller's readonly value; what changed is that the value the
11+
exemption keeps is no longer stored unjudged. Before, the record validator
12+
skipped every readonly field, so under `isSystem` a malformed readonly value
13+
reached the driver verbatim — a seed's `run_at: 'yesterday'` on a readonly
14+
`datetime`, an unresolved `cel` envelope from a seeder that skips its
15+
resolution, an authored `created_at` the seed now keeps — while the same value
16+
on a non-readonly field was refused.
17+
18+
Now it is refused the same way: `VALIDATION_FAILED` (400 at an HTTP boundary),
19+
the same field code and the same sentence a non-readonly field gets
20+
(`Run At must be a valid datetime (ISO-8601)`), and a seed counts the row as a
21+
seed error. This holds on insert, on the dry run (`ObjectQL.validate`), and on
22+
both update paths, where the readonly values left after the strip are judged.
23+
24+
Which checks a readonly value reaches — its type's shape, never a constraint:
25+
26+
- refused: a `date` / `datetime` / `time` the platform does not read, a
27+
non-number on a number-typed field, a non-boolean on a boolean, a non-array on
28+
a multi-value field, a filter-operator object, and an ADR-0104 reference /
29+
media / structured-JSON shape under the object's own posture (warn-first, as
30+
on any other field, until the deployment's evidence enforces it);
31+
- NOT checked, exactly as before: option membership, `maxLength` /
32+
`minLength`, `valueDomain`, `min` / `max` / `scale` / `precision`, the email /
33+
url / phone formats, and `required`. Option membership stays out on purpose:
34+
`sys_activity.type` is a readonly `select` whose options are the built-in set
35+
of an open vocabulary, and an author-contributed value there is stored.
36+
37+
A numeric string on a readonly number field is now written as its number, and a
38+
lone scalar on a readonly multi-value field as a one-member list, as on any
39+
other field — the door reads the value the same way it judges it.
40+
41+
**What moves for consumers.** A seed, migration or `isSystem` write that puts a
42+
malformed value in a readonly field — or a hook that stamps one — is refused
43+
where it was stored. Fix the value at its producer: write an ISO-8601 instant
44+
(or a `Date`) into a readonly `datetime`, resolve a `cel` value before the
45+
write, and stamp numbers and booleans as such. Rows already stored are never
46+
re-read or rewritten. `validateRecord`, as exported, is unchanged: the readonly
47+
scope is the engine write path's own.
48+
49+
Clause-②: no (narrowing)
50+
51+
<!-- adr-0087: not-required (no-migration-prescription) a write-time refusal of a malformed value in a readonly field, judged by the same per-type shape checks a non-readonly field already gets. No authorable key, spelling, export or stored shape moves: the field schema is unchanged, the published validateRecord signature is unchanged, no stored row is read or rewritten, and which value a producer meant to write is not something a ledger entry can rewrite. The other categories are closed on facts: the package publishes (not unpublished); no ADR-0087 id covers this behaviour (not already-registered); and the change is a write-path verdict, not a declaration (not runtime-interface-only or type-surface-only). -->

0 commit comments

Comments
 (0)