You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
docs(changeset): the IANA time-zone columns changeset declares its accept-set narrowing as BREAKING
`.changeset/platform-iana-timezone-columns.md` described an accept-set
narrowing as "A NON-BREAKING ADDITION" and carried no `**BREAKING**` banner.
The token's only occurrence sat inside those words, with no `**` prefix, so
`check-adr-0087-registration.mjs` (which classifies from the author's own
declaration, `/\*\*BREAKING/i` on the body) read the changeset as
non-breaking — prose and machine agreeing for the same reason.
During the launch window the bump level is not the carrier of breaking-ness
(`check-changeset-no-major.mjs` pushes breaking changes to `minor`), so the
banner and the ADR-0087 disposition are the only signals there are. As it
stood this would have shipped a CHANGELOG telling an upgrading author the
release could not break them, immediately before a write that worked
yesterday is refused.
The body now carries the `**BREAKING**` banner in the shape of the in-repo
precedent, keeps the bump at `minor`, keeps exactly one ADR-0087 disposition
(`not-required (no-migration-prescription)`, still the honest one: the domain
is checked on the written value only, so no consumer has a metadata rewrite
to perform), and states the consumer delta — which spellings stop being
accepted, that every genuine IANA identifier including `UTC` keeps working,
and that stored rows are unaffected, quoted from the published contract text
in `packages/spec/src/data/field.zod.ts`.
Text only: no `.ts`, no docs, no other changeset.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ARYe3yQTQCUFm5qPYNgKaJ
Copy file name to clipboardExpand all lines: .changeset/platform-iana-timezone-columns.md
+46-1Lines changed: 46 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -4,7 +4,50 @@
4
4
5
5
feat(platform-objects): `sys_job.timezone` and `sys_report_schedule.timezone` are validated against the IANA domain (#15872)
6
6
7
-
<!-- adr-0087: not-required (no-migration-prescription) A NON-BREAKING ADDITION of one field property to two existing columns. `valueDomain` is the `min`/`max`/`maxLength` transition-gate class: only a WRITTEN value is judged, a stored value outside a domain declared later is never re-read and survives unrelated edits, so `objectstack migrate meta` has nothing to rewrite. No metadata key, export, config field or stored shape is renamed, retired, re-typed or tombstoned; no column is added, dropped or re-bounded (`maxLength` is unchanged on both, deliberately), so boot schema-sync plans no DDL either. MIGRATION NOTE, stated rather than assumed: a deployment that already stored a non-IANA string in either column keeps it and reads it back unchanged; what changes is that the next WRITE of such a value is refused with the ADR-0114 field error code `value_domain`. For `sys_report_schedule` that refusal is the point — see the behaviour note below. -->
7
+
**BREAKING** accept-set narrowing on two published columns, shipped as `minor`
8
+
under the repo's launch-window convention for breaking changes. Both columns now
9
+
declare `valueDomain: 'iana_time_zone'`, so a value the shipped build stored
10
+
without complaint is refused from this release on. During the launch window the
11
+
bump level is not the carrier of breaking-ness and says nothing about whether a
12
+
release breaks you; this banner is the carrier, and the ADR-0087 disposition at
13
+
the foot of this changeset is the other one.
14
+
15
+
**What stops being accepted.** A write to either column is now refused with the
16
+
ADR-0114 field error code `value_domain` unless the value is a member of the
17
+
IANA/tzdb set, tested with the `Intl.DateTimeFormat` probe. Three classes of
18
+
string that the previous build accepted are outside that set:
19
+
20
+
-**UTC-offset spellings** — `UTC+8`, `GMT+0800`, `+08:00`. They name an offset,
21
+
not a zone, and no offset spelling is an IANA identifier. The tzdb's own
22
+
fixed-offset zones are members and keep working: `Etc/GMT-8` is accepted.
23
+
-**Windows / CLDR display names** — `China Standard Time`,
24
+
`Pacific Standard Time`. That is the Windows time-zone vocabulary, a different
25
+
naming scheme from tzdb, and no member of it is a tzdb identifier.
26
+
-**shape-valid identifiers for zones that do not exist** — `Mars/Olympus`. A
27
+
`Region/City` pattern cannot separate an unassigned identifier from a real
28
+
one; membership can, which is what the domain is for.
29
+
30
+
**What keeps working.** Every genuine IANA identifier, including `UTC` — the
31
+
membership predicate is the `Intl.DateTimeFormat` probe, deliberately not the
32
+
`Intl.supportedValuesOf('timeZone')` enumeration, which omits `UTC`. That
33
+
matters here rather than academically: `'UTC'` is `sys_report_schedule.timezone`'s
34
+
own declared default.
35
+
36
+
**Stored rows are unaffected — only writes are judged.** No upgrade step, no
37
+
backfill, no DDL. In the published words of the contract this declaration is
38
+
governed by (`packages/spec/src/data/field.zod.ts`, the `valueDomain` description):
39
+
40
+
> Checked on the WRITTEN value only (the `min`/`max`/`maxLength` transition-gate
41
+
> class): a stored value outside a domain declared later is never re-read and
42
+
> survives unrelated edits — only a write carrying a non-member is refused, with
43
+
> the field error code `value_domain`.
44
+
45
+
So a deployment already holding `UTC+8` in one of these columns keeps it and
46
+
reads it back unchanged; what changes is the next write. The one thing to know
47
+
before upgrading is therefore an authoring fact, not a data-at-rest one: a
48
+
producer that writes one of the three spellings above starts getting a refusal
49
+
where it previously got a success, and for `sys_report_schedule` that refusal is
50
+
the point — see the behaviour note below.
8
51
9
52
The platform's two oldest IANA time-zone columns predate `valueDomain` and disagreed with each other in three dimensions at once — length (100 vs 64), default (none vs `'UTC'`) and validation (neither). This closes the third: both now declare `valueDomain: 'iana_time_zone'`, the same declaration and the same `Intl.DateTimeFormat` membership probe that `sys_business_unit.timezone` and `sys_organization.timezone` carry (#14238). Four columns, one spelling of "is this a real zone".
10
53
@@ -17,3 +60,5 @@ The platform's two oldest IANA time-zone columns predate `valueDomain` and disag
17
60
18
61
-**the defaults still differ.** A default here is a consumer semantic, not a shape question. `sys_report_schedule` documents and implements a UTC default; `sys_job` has no reader at all, and minting one would change what an unset row means.
19
62
-**the bounds still differ (100 vs 64).**`maxLength` is not only a write bound — it reaches DDL, and narrowing a physical `varchar(100)` is `driver-sql`'s `narrow_varchar` op at severity `error`, category destructive ("narrowing may truncate"). What the column physically holds in a deployment is not readable from the repo, so the convergence is a separate decision and #15872 stays open on it. Note what the domain declaration already costs the wider bound: no member is longer than 32 characters on the current Node baseline, so 100 now admits nothing 64 would not.
63
+
64
+
<!-- adr-0087: not-required (no-migration-prescription) A DECLARED-BREAKING accept-set narrowing on two existing columns that leaves nobody a metadata rewrite to perform. `valueDomain` is the `min`/`max`/`maxLength` transition-gate class: only a WRITTEN value is judged, a stored value outside a domain declared later is never re-read and survives unrelated edits, so `objectstack migrate meta` has nothing to rewrite and there is no tombstone to mint. No metadata key, export, config field or stored shape is renamed, retired, re-typed or tombstoned; no column is added, dropped or re-bounded (`maxLength` is unchanged on both, deliberately), so boot schema-sync plans no DDL either. What an upgrader has to know is a WRITE-PATH fact rather than a stored-shape one, and the body above states it rather than assuming it: a deployment that already stored a non-IANA string in either column keeps it and reads it back unchanged; what changes is that the next WRITE of such a value is refused with the ADR-0114 field error code `value_domain`. The channel that reaches an affected producer is that refusal, at its own write, which is more precise than a ledger line the producer never reads. -->
0 commit comments