Skip to content

Commit e10e2c7

Browse files
committed
docs(changeset): the seven data/ ui/ ai/ integration/ duration renames, plus the auto-arm sibling (#15680)
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G4138K1EG7kQ81FNba5Kp4
1 parent 3a56ae0 commit e10e2c7

4 files changed

Lines changed: 187 additions & 0 deletions
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
---
2+
"@objectstack/cli": patch
3+
---
4+
5+
fix(cli): `explain` names the renamed `dashboard.refreshIntervalSeconds` (#14478)
6+
7+
The dashboard key catalogue `os explain` prints lists
8+
`refreshIntervalSeconds` instead of `refreshInterval`, following the
9+
`@objectstack/spec` rename of the authored key (the unit now lives in the key
10+
name). Same key, same seconds; no other command output and no public surface of
11+
this package changes.
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
---
2+
"@objectstack/spec": minor
3+
---
4+
5+
feat(spec)!: the last seven `data/` · `ui/` · `ai/` · `integration/` duration keys carry their unit in the key name (#15680, ruling B on #14478)
6+
7+
<!-- adr-0087: registered dashboard-refresh-interval-to-refresh-interval-seconds, connector-health-and-trigger-durations-unit-in-key, memory-persistence-auto-save-interval-to-ms, turso-config-timeout-to-timeout-ms, ai-conversation-analytics-duration-unit-in-key, data-nosql-query-options-timeout-unit-in-key -->
8+
9+
**BREAKING** — eight published duration keys are renamed and tombstoned. Shipped
10+
as `minor` under the repo's launch-window convention for breaking changes; the
11+
hand-migration prescriptions are registered under protocol major 18. Maintainer
12+
ruling B on #14478 (2026-09-02, decision batch #43, 「同意」).
13+
14+
`check:duration-unit-keys` makes a duration-shaped `z.number()` carry its unit in
15+
the key NAME, never only in its `.describe()` prose, and grandfathers no existing
16+
offender. Card 1/6 (#15676) landed the rule's two structural exemptions, card 2/6
17+
(#15677) cleared `api/`, card 3/6 (#15678) cleared `kernel/` and card 4/6
18+
(#15679) cleared `system/`. This card clears the remainder, and is the first
19+
where the gate itself reads **`zero offenders`** and exits `0`.
20+
21+
⚠️ That is green **for the gate's currently declared population**
22+
(`packages/spec/src/**`), not for the epic. Card 6/6 widens the population and has
23+
already measured an offender outside this subtree, so the gate is expected to go
24+
red again by design. This changeset does not claim #14478 is finished.
25+
26+
## FROM → TO
27+
28+
| key | replacement | unit |
29+
|:--|:--|:--|
30+
| `dashboard.refreshInterval` | `refreshIntervalSeconds` | seconds |
31+
| `CircuitBreakerConfig.monitoringWindow` | `monitoringWindowMs` | milliseconds |
32+
| `ConnectorTrigger.interval` | `intervalSeconds` | seconds |
33+
| `FilePersistenceConfig.autoSaveInterval` | `autoSaveIntervalMs` | milliseconds |
34+
| `AutoPersistenceConfig.autoSaveInterval` | `autoSaveIntervalMs` | milliseconds |
35+
| `TursoConfig.timeout` | `timeoutMs` | milliseconds |
36+
| `NoSQLQueryOptions.timeout` | `timeoutMs` | milliseconds |
37+
| `ConversationAnalytics.duration` | `durationSeconds` | seconds |
38+
39+
**Every value is unchanged** — only key names move. The two keys that carried a
40+
default keep it (`CircuitBreakerConfig.monitoringWindowMs` still defaults to
41+
60000, `FilePersistenceConfig.autoSaveIntervalMs` to 2000); the other six declare
42+
none. Bounds move with their keys, so `autoSaveIntervalMs` still refuses anything
43+
under 100 on both persistence arms, `NoSQLQueryOptions.timeoutMs` and
44+
`TursoConfig.timeoutMs` still refuse a zero or negative integer, and
45+
`ConversationAnalytics.durationSeconds` still refuses a negative length. Every old
46+
spelling is a `retiredKey()` tombstone, so it fails `tsc` at the authoring site
47+
(input type `never`) and fails the parse with the rename prescription rather than
48+
a bare unrecognized-key error.
49+
50+
`dashboard`'s three rename-hint aliases — `refresh`, `autoRefresh`, `pollInterval`
51+
— were repointed to `refreshIntervalSeconds` in the same edit. A hint left naming
52+
the tombstone would have prescribed a key the shape refuses, which is the one
53+
failure this rename could have introduced silently; a pin asserts all three.
54+
55+
## ⚠️ `dashboard.refreshInterval` crosses a repository boundary
56+
57+
This is the only rename in the whole stack whose consumer is in **another
58+
repository**, so its reader could not move in this PR the way every other reader
59+
in this card did. objectui's dashboard renderer reads the key, multiplies by
60+
1000 to drive a `setInterval`, and republishes it as an authoring input the
61+
console offers. Those sites move in a follow-up objectui card, sequenced behind
62+
a release that actually ships this rename.
63+
64+
Until that lands the renderer sees an absent key and simply does not start its
65+
refresh timer — a dashboard still renders, and still refreshes when the user
66+
asks. The ADR-0087 conversion in this changeset is what keeps stored dashboards
67+
and `os migrate meta` correct in the meantime.
68+
69+
## ⚠️ An eighth key moves that the gate did not list
70+
71+
`AutoPersistenceConfig.autoSaveInterval` is not a gate offender: its `.describe()`
72+
named no unit at all, and the predicate judges prose against name.
73+
74+
It moves anyway because it is not a second key. `persistence: { type: 'auto' }`
75+
resolves to the same Node.js file adapter as `type: 'file'`, and this value is
76+
forwarded to the same `FileSystemPersistenceAdapter` field, in the same
77+
milliseconds, under the same `min(100)` bound. Renaming one arm and not the other
78+
would have left one value with two spellings across sibling arms of one union,
79+
and the driver reading both — the consumer-side dialect Prime Directive #12
80+
forbids. Its describe now names the unit too, and a pin asserts the refusal on
81+
the arm the gate never listed, so a later reader cannot "restore" the bare
82+
spelling as an over-application of the rule.
83+
84+
## Dispositions — four D2 conversions, two semantic entries
85+
86+
Judged per key from `stack.zod.ts`'s collection roots rather than defaulted, and
87+
unlike card 4/6 this card's answer is split.
88+
89+
**D2 conversions** (six keys). `dashboards:`, `connectors:` and `datasources:`
90+
are each a stack collection whose members are stored whole as `sys_metadata`
91+
rows, so the conversion chain has a seam that sees them:
92+
`dashboard-refresh-interval-to-refresh-interval-seconds`,
93+
`connector-health-and-trigger-durations-unit-in-key` (both connector keys in one
94+
pass, emitting separately),
95+
`memory-persistence-auto-save-interval-to-ms` (both persistence arms) and
96+
`turso-config-timeout-to-timeout-ms`. The two datasource conversions are
97+
driver-aware for the reason `datasource-config-driver-key-aliases` records: a
98+
bare `config.timeout` under another driver is that driver's own key and must not
99+
be touched.
100+
101+
**Semantic entries** (two keys). `ConversationAnalytics` is computed at runtime
102+
and handed to a consumer, and `NoSQLQueryOptions` is a per-call driver argument
103+
reached only through `AggregationPipeline.options`. Neither is a stack collection
104+
member or a stored row, so the chain has no seam — the disposition every
105+
runtime-emitted measurement in this stack has taken.
106+
107+
All eight are registered by exact key in `RETIRED_KEYS_BY_MAJOR`.
108+
109+
## A retirement tombstone is no longer read as a secret
110+
111+
`refusedCredentialKeys` derives a driver's refused inline credentials by finding
112+
`z.never()` keys in its config contract. A `retiredKey()` tombstone is also a
113+
`z.never()`, and until this card no driver contract carried one — so "never ⇒
114+
credential" held by accident of population rather than by construction. The first
115+
tombstone to arrive (`TursoConfig.timeout`) made the derivation answer that a
116+
millisecond budget was a secret: it was redacted off the datasource read path and
117+
dragged a non-credential name into the fallback list every unrecognised driver is
118+
scrubbed by.
119+
120+
The derivation now skips keys carrying the `[REMOVED] ` prefix `retiredKey()`
121+
itself stamps. The exclusion is deliberately **negative** — skip declared
122+
tombstones — rather than positive (keep only keys marked `format: 'password'`),
123+
even though every credential slot in every builtin contract does carry that
124+
marker today: under-redacting is the dangerous direction, so a future credential
125+
key whose author forgets the marker is still scrubbed, and only a key that has
126+
explicitly declared itself retired may drop out. Both directions are pinned.
127+
128+
## Keys deliberately left alone
129+
130+
`TursoConfig.sync.intervalSeconds` and `CircuitBreakerConfig.resetTimeoutMs`
131+
already carried their unit — they are the same-shape neighbours that made the
132+
bare `timeout` and `monitoringWindow` collisions visible, and pins assert they
133+
did not move. `NoSQLQueryOptions.batchSize` is a COUNT of documents and every
134+
number on `ConversationAnalytics` other than the duration is a count of messages,
135+
tokens or events: a count has no unit to carry. The turso schema shipped by
136+
`@objectstack/driver-turso` is a separate declaration outside this gate's
137+
declared population and is not touched here; card 6/6 owns it, so the two
138+
declarations disagree by design until that lands.
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
---
2+
"@objectstack/driver-memory": minor
3+
---
4+
5+
feat(driver-memory)!: the file-persistence auto-save interval names its unit (#15680, ruling B on #14478)
6+
7+
**BREAKING** — `InMemoryDriverOptions.persistence.autoSaveInterval` and
8+
`FileSystemPersistenceAdapter`'s `autoSaveInterval` constructor option are both
9+
renamed to **`autoSaveIntervalMs`**, following the `@objectstack/spec` rename of
10+
the authored keys on both persistence arms.
11+
12+
Same value, same milliseconds, same 2000 default, same `setInterval` cadence. The
13+
option was always milliseconds — it is passed straight to `setInterval` — and the
14+
spec's `min(100)` bound is what made the bare name dangerous rather than untidy:
15+
100 reads as a plausible number of seconds, so an author who guessed the unit
16+
wrong cleared the bound, was refused nowhere, and saved a thousand times more
17+
often than intended.
18+
19+
Both persistence arms move together: `type: 'auto'` resolves to this same file
20+
adapter and forwards the same field, so this package reads exactly one spelling
21+
rather than two.
22+
23+
```diff
24+
- new InMemoryDriver({ persistence: { type: 'file', autoSaveInterval: 5000 } })
25+
+ new InMemoryDriver({ persistence: { type: 'file', autoSaveIntervalMs: 5000 } })
26+
```
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
---
2+
"@objectstack/platform-objects": patch
3+
---
4+
5+
fix(platform-objects): the dashboard metadata-form bundles follow the `refreshIntervalSeconds` rename (#14478)
6+
7+
The `metadataForms.dashboard` translation bundles key the auto-refresh field as
8+
`refreshIntervalSeconds`, following the `@objectstack/spec` rename of the
9+
authored key. Regenerated with `node scripts/check-i18n-bundles.mjs --write`; the
10+
hand-written `zh-CN` / `ja-JP` / `es-ES` label and help text were carried across
11+
the rename unchanged, because the field still means what it meant and each help
12+
text already named the unit.

0 commit comments

Comments
 (0)