diff --git a/.changeset/value-domain-note-settings-door-repointed.md b/.changeset/value-domain-note-settings-door-repointed.md new file mode 100644 index 0000000000..7ef1135207 --- /dev/null +++ b/.changeset/value-domain-note-settings-door-repointed.md @@ -0,0 +1,27 @@ +--- +"@objectstack/spec": patch +--- + +docs(spec): the `field.valueDomain` liveness note stops claiming the settings door is "unchanged until then" + +The `valueDomain` row of the published `liveness/field.json` ledger ended on a sentence written +while the re-point was still in the future: + +> The settings door (`service-settings/value-domains.ts`) re-points onto the shared predicate in +> its own follow-up card and is unchanged until then. + +Both halves of the 2026-09-02 ruling have since landed — the settings half (#15434) and the engine +half (#15316) — and the engine half rewrote this note wholesale while carrying that sentence +forward verbatim. "Unchanged until then" therefore described a state that no longer existed: the +door it names had already re-pointed, one commit earlier. + +The sentence now says what is true of that door, read off its source rather than off a PR title: +its second copy of all three definitions is deleted, `firstRejectedDomainMember` asks +`isValueDomainMember` — the same call `record-validator.ts` makes — and what remains on that side +is the door's own business (which declarations it agrees to enforce, how a multi-value carrier is +walked, the fragments the env-override log line needs). A re-added local table reddens +`value-domains.shared-predicate.pin.test.ts`. + +Ledger-note text only. The row's `status` is untouched — it tracks the engine write path, and +`liveness/state-counts.md` is derived by `gen:liveness-counts` from the row states, none of which +move here (`check:liveness` reports the counts file current). diff --git a/packages/spec/liveness/field.json b/packages/spec/liveness/field.json index 1efc74511d..65dbcab6fc 100644 --- a/packages/spec/liveness/field.json +++ b/packages/spec/liveness/field.json @@ -225,7 +225,7 @@ "status": "live", "verifiedAt": "2026-09-04", "evidence": "packages/objectql/src/validation/record-validator.ts#validateOne (the write-path seam, beside `maxLength`'s: `if (def.valueDomain !== undefined && VALUE_DOMAIN_FIELD_TYPES.has(t) && !isValueDomainMember(def.valueDomain, s)) return fail('value_domain', { valueDomain: def.valueDomain }, ...)` \u2014 a non-member WRITTEN to a `text` field declaring a domain is refused with the ADR-0114 code `value_domain` and `constraint.valueDomain`); packages/spec/src/data/field.zod.ts#VALUE_DOMAIN_FIELD_TYPES (the parse-time applicability door: the key is accepted on `text` only and refused with a located `custom` issue at [valueDomain] on every other type — the same superRefine mechanism `maxLength` / `minLength` use); packages/spec/src/shared/value-domain.zod.ts#isValueDomainMember (the ONE membership predicate the write path will call — shared with the settings door)", - "note": "The write path enforces it since 2026-09-04 (#15161, the engine half of the maintainer ruling 2026-09-02 option A on #14168; the spec half declared the slot, the closed vocabulary, the shared predicate, the ADR-0114 catalog member and its four-locale templates). WRITTEN VALUE ONLY, the `min`/`max`/`maxLength` transition-gate class: a stored value outside a domain declared later is never re-read and survives unrelated edits, and an absent/empty value is the field's `required` handling, not this check \u2014 both pinned in packages/objectql/src/validation/record-validator.value-domain.test.ts, together with the per-domain matrix (iso_3166_alpha2 admits CH and refuses ZZ; iana_time_zone admits UTC and refuses Mars/Olympus; iso_4217_currency admits CHF and refuses chf). The applicability door is one constant read by both seams \u2014 the schema refuses the key outside VALUE_DOMAIN_FIELD_TYPES at parse and the validator judges exactly that set, so the two cannot drift into two opinions (the #11875 discipline; the subset relation to BOUNDED_STRING_FIELD_TYPES, which the enforcement branch rides on, is pinned in the same file). The settings door (`service-settings/value-domains.ts`) re-points onto the shared predicate in its own follow-up card and is unchanged until then." + "note": "The write path enforces it since 2026-09-04 (#15161, the engine half of the maintainer ruling 2026-09-02 option A on #14168; the spec half declared the slot, the closed vocabulary, the shared predicate, the ADR-0114 catalog member and its four-locale templates). WRITTEN VALUE ONLY, the `min`/`max`/`maxLength` transition-gate class: a stored value outside a domain declared later is never re-read and survives unrelated edits, and an absent/empty value is the field's `required` handling, not this check \u2014 both pinned in packages/objectql/src/validation/record-validator.value-domain.test.ts, together with the per-domain matrix (iso_3166_alpha2 admits CH and refuses ZZ; iana_time_zone admits UTC and refuses Mars/Olympus; iso_4217_currency admits CHF and refuses chf). The applicability door is one constant read by both seams \u2014 the schema refuses the key outside VALUE_DOMAIN_FIELD_TYPES at parse and the validator judges exactly that set, so the two cannot drift into two opinions (the #11875 discipline; the subset relation to BOUNDED_STRING_FIELD_TYPES, which the enforcement branch rides on, is pinned in the same file). The settings door (`service-settings/value-domains.ts`) has re-pointed onto that same predicate (#15434, the services half of the same ruling): its second copy of all three definitions is deleted and `firstRejectedDomainMember` asks `isValueDomainMember`, so a value Settings admits is the value a field admits and vice versa; what is left on that side is the door's own business (which declarations it agrees to enforce, how a multi-value carrier is walked, the fragments the env-override log line needs), and a re-added local table reddens packages/services/service-settings/src/value-domains.shared-predicate.pin.test.ts." }, "rows": { "status": "live",