@@ -10845,6 +10845,85 @@ const step18: MigrationStep = {
1084510845 + 'one to check by hand rather than by search-and-replace: after the migration a reader '
1084610846 + 'can tell at the authoring site that 300 and 30000 are not the same kind of number.',
1084710847 },
10848+ {
10849+ id: 'system-tracing-otel-exporter-durations-unit-in-key',
10850+ // No backticks in `surface` — build-upgrade-guide.ts renders it inside a
10851+ // code span AND a table cell.
10852+ surface: 'the four tracing-configuration durations whose unit lived in a source JSDoc only: '
10853+ + 'OpenTelemetryCompatibility.exporter.timeout, '
10854+ + 'OpenTelemetryCompatibility.exporter.batch.exportTimeout, '
10855+ + 'OpenTelemetryCompatibility.exporter.batch.scheduledDelay and '
10856+ + 'TracingConfig.performance.exportInterval (system/tracing.zod.ts)',
10857+ replacement: 'timeoutMs, exportTimeoutMs, scheduledDelayMs and exportIntervalMs — rename each '
10858+ + 'key; all four values (milliseconds) and their 10000 / 30000 / 5000 / 5000 defaults are '
10859+ + 'unchanged',
10860+ reason:
10861+ 'Director-seat ruling A on #15939, 2026-09-11, carrying the maintainer\'s 「同意」 (decision '
10862+ + 'batch #115), executing the #14478 rule per file. It follows '
10863+ + 'system-tracing-span-duration-unit-in-key on this same file and does not amend it: that '
10864+ + 'entry retired Span.duration under ruling B, whose population was the describe channel, '
10865+ + 'and these four keys were never in it — they are the JSDoc-only channel #15939 opened, '
10866+ + 'which is why one file carries two rounds. Each key named milliseconds in its JSDoc — '
10867+ + '"Timeout in milliseconds", "Export timeout in milliseconds", "Scheduled delay in '
10868+ + 'milliseconds", "Background export interval in milliseconds" — and the JSDoc above a key '
10869+ + 'is NOT what content/docs/references/** renders; .describe() is. Measured on this tree: '
10870+ + 'all four carried NO .describe() at all, so the published reference row for each was a '
10871+ + 'bare integer with no unit anywhere on the page — a strictly worse channel than the '
10872+ + 'unit-in-prose shape #14478 already refuses, since here the reference reader had no prose '
10873+ + 'to misread. The magnitudes make the guess plausible in both directions: 10000, 30000, '
10874+ + '5000 and 5000 are all defensible as seconds and as milliseconds, and an operator who '
10875+ + 'reads seconds sets an exporter deadline 1000x short. The suffix is the family spelling, '
10876+ + 'counted in key position at 98bd7986fe over packages/spec/src *.ts (reproduce with '
10877+ + 'git grep -hoE on that ref): 281 *Ms declarations over 42 distinct names, timeoutMs 65 of '
10878+ + 'them and intervalMs 14, against 0 key-position timeoutSeconds and 77 *Seconds of any '
10879+ + 'name; the Delay-plus-Ms pairing is likewise already attested on that same ref '
10880+ + '(maxDelayMs 9, initialDelayMs 9, maxRetryDelayMs 5, debounceDelayMs 2, delayMs 2, '
10881+ + 'retryDelayMs 1) with 0 occurrences of any competing exportTimeout, scheduledDelay or '
10882+ + 'exportInterval spelling, suffixed or Seconds. Note this file is milliseconds throughout '
10883+ + 'and its own landed precedent is '
10884+ + 'Span.duration to durationMs, the opposite of the sibling metrics card whose rows were '
10885+ + 'seconds. exporter.timeoutMs and exporter.batch.exportTimeoutMs are deliberately allowed '
10886+ + 'to sit one nesting level apart: the pair pre-exists the rename — the batch sub-object is '
10887+ + 'the OpenTelemetry batch span processor\'s own four knobs (max batch size, max queue '
10888+ + 'size, scheduled delay, export timeout) beside the exporter\'s own request deadline — so '
10889+ + 'renaming either to something more distinctive would depart from the vocabulary the shape '
10890+ + 'mirrors, and the nesting already disambiguates every read point '
10891+ + '(exporter.timeoutMs vs exporter.batch.exportTimeoutMs). All four old spellings are '
10892+ + 'retiredKey() tombstones: neither OpenTelemetryCompatibilitySchema nor TracingConfigSchema '
10893+ + 'nor any object nested inside them is .strict(), so a bare deletion would be a SILENT '
10894+ + 'STRIP (#3733, ADR-0104) — and the stripped value lands on an export deadline and a '
10895+ + 'background export period. Why a semantic entry and not a D2 conversion: the conversion '
10896+ + 'chain walks a normalized STACK, and neither def is an authorable surface — stack.zod.ts '
10897+ + 'declares no tracing collection, no metadata-type binding or manifest embed carries '
10898+ + 'either, and a tracing configuration is never a stored sys_metadata row — so a conversion '
10899+ + 'would be a transform with no seam that ever runs. That is the same disposition '
10900+ + 'system-tracing-span-duration-unit-in-key recorded for the other key on this file. '
10901+ + 'Measured at 98bd7986fe: NO in-repo reader exists outside packages/spec — '
10902+ + 'OpenTelemetryCompatibility, TracingConfig and all three batch key names occur 0 times '
10903+ + 'across the whole tree at that ref excluding packages/spec and content/docs/references, '
10904+ + 'against a lit control of 18920 Schema occurrences on exactly that corpus and ref — both '
10905+ + 'counts from one git grep -o over 98bd7986fe with those two pathspec exclusions — and a '
10906+ + 'dark control of 0; inside packages/spec the '
10907+ + 'only occurrences are tracing.zod.ts, its test, and the generated rows in '
10908+ + 'content/docs/references/system/tracing.mdx, which this rename regenerates. And the '
10909+ + 'pinned objectui checkout — `.objectui-sha` = `53ded82bf7a494f54e344e19099dbf00854b8694` — names none of it: all 37 exports of '
10910+ + 'tracing.zod.ts and each of the four key names occur 0 times across the 6409 files '
10911+ + 'tracked at that sha (the 404 Span and 40 SpanSchema hits are objectui\'s own HTML '
10912+ + 'text-span component, TextSpanSchema, an unrelated name), against two lit controls on '
10913+ + 'that same corpus and sha: 10171 hits for the bare token objectstack, and 3479 for the '
10914+ + 'package specifier @objectstack/spec.',
10915+ acceptanceCriteria:
10916+ 'Every author and reader of an OpenTelemetryCompatibility spells exporter.timeoutMs, '
10917+ + 'exporter.batch.exportTimeoutMs and exporter.batch.scheduledDelayMs, and every one of a '
10918+ + 'TracingConfig spells performance.exportIntervalMs. Authoring any old spelling fails to '
10919+ + 'compile (input type `never`) and fails to parse with the rename prescription naming the '
10920+ + 'suffixed key — not with a generic unrecognized_keys issue, which these non-strict shapes '
10921+ + 'could never have raised anyway. Behaviour is unchanged: the same milliseconds, the same '
10922+ + '10000 / 30000 / 5000 / 5000 defaults and the same int().positive() bounds, and all four '
10923+ + 'published describes now name milliseconds where before there was no describe at all. '
10924+ + 'The authorable-surface and authorable-defaults ledgers move nothing: every one of the '
10925+ + 'four is NESTED, and those artifacts record top-level keys per def only.',
10926+ },
1084810927 {
1084910928 id: 'system-tracing-span-duration-unit-in-key',
1085010929 surface: 'Span.duration, the emitted trace-span length whose name carried no unit '
@@ -13770,6 +13849,42 @@ export const RETIRED_KEYS_BY_MAJOR: Readonly<Record<number, readonly string[]>>
1377013849 // stored metadata row.
1377113850 // See `system-metrics-jsdoc-durations-unit-in-key`.
1377213851 'system/MetricsConfig:retention.period',
13852+ // #15939 ruling A (per-file remediation of #14478). `exporter.batch.exportTimeout`
13853+ // said "Export timeout in milliseconds" in a source JSDoc and carried NO
13854+ // `.describe()` at all, so the published reference page showed a bare 30000.
13855+ // Renamed to `exportTimeoutMs`; the value and the 30000 default are unchanged.
13856+ // Tombstoned with `retiredKey()`: the nested `batch` object is not strict, so a
13857+ // bare deletion would silently strip the key. No D2 conversion: not a stack
13858+ // collection member, not a stored row. See
13859+ // `system-tracing-otel-exporter-durations-unit-in-key`.
13860+ 'system/OpenTelemetryCompatibility:exporter.batch.exportTimeout',
13861+ // #15939 ruling A (per-file remediation of #14478). `exporter.batch.scheduledDelay`
13862+ // said "Scheduled delay in milliseconds" in a source JSDoc and carried NO
13863+ // `.describe()` at all, so the published reference page showed a bare 5000.
13864+ // Renamed to `scheduledDelayMs`, the plain suffix: counted in key position across
13865+ // `packages/spec/src` at `98bd7986fe`, the Delay-plus-Ms pairing is already the
13866+ // family spelling (`maxDelayMs` 9, `initialDelayMs` 9, `maxRetryDelayMs` 5,
13867+ // `debounceDelayMs` 2, `delayMs` 2, `retryDelayMs` 1) and neither
13868+ // `scheduledDelayMs` nor `scheduledDelaySeconds` occurs there at all.
13869+ // The value and the 5000 default are unchanged. Tombstoned with `retiredKey()`:
13870+ // the nested `batch` object is not strict, so a bare deletion would silently
13871+ // strip the key. No D2 conversion: not a stack collection member, not a stored
13872+ // row. See `system-tracing-otel-exporter-durations-unit-in-key`.
13873+ 'system/OpenTelemetryCompatibility:exporter.batch.scheduledDelay',
13874+ // #15939 ruling A (per-file remediation of #14478). `exporter.timeout` said
13875+ // "Timeout in milliseconds" in a source JSDoc and carried NO `.describe()` at
13876+ // all, so the `content/docs/references/**` page published a bare 10000 with no
13877+ // unit anywhere on it. Renamed to `timeoutMs`; the value and the 10000 default
13878+ // are unchanged. It keeps the plain suffix even though its `exporter.batch`
13879+ // sibling becomes `exportTimeoutMs`: the two are the OpenTelemetry exporter's
13880+ // own request deadline and the batch processor's export deadline, two distinct
13881+ // knobs the shape already spelled apart, and the nesting keeps every read point
13882+ // unambiguous. Tombstoned with `retiredKey()`: the nested `exporter` object is
13883+ // not strict, so a bare deletion would silently strip the key and let a 10000 ms
13884+ // default land on an exporter deadline the operator had deliberately changed.
13885+ // No D2 conversion: not a stack collection member, not a stored row. See
13886+ // `system-tracing-otel-exporter-durations-unit-in-key`.
13887+ 'system/OpenTelemetryCompatibility:exporter.timeout',
1377313888 // #15679 (stack card 4/6 of #14478) — ruling B. `QueueConfig.rateLimit.duration`
1377413889 // said "Duration in milliseconds" in prose and nothing else — while
1377513890 // `TaskResult.durationMs`, ninety lines earlier in the SAME file, already spelled
@@ -13909,6 +14024,19 @@ export const RETIRED_KEYS_BY_MAJOR: Readonly<Record<number, readonly string[]>>
1390914024 // connection is host configuration, not a stored metadata row.
1391014025 // See `system-object-storage-durations-unit-in-key`.
1391114026 'system/StorageConnection:timeout',
14027+ // #15939 ruling A (per-file remediation of #14478). `performance.exportInterval`
14028+ // said "Background export interval in milliseconds" in a source JSDoc and
14029+ // carried NO `.describe()` at all, so the published reference page showed a bare
14030+ // 5000. Renamed to `exportIntervalMs`; the value and the 5000 default are
14031+ // unchanged. `intervalMs` is the family spelling already attested 14 times in key
14032+ // position across `packages/spec/src` at `98bd7986fe` (0 `exportIntervalMs` and 0
14033+ // `exportIntervalSeconds` there, so nothing competes), so the suffix lands on a
14034+ // name the surface already uses.
14035+ // Tombstoned with `retiredKey()`: the nested `performance` object is not strict,
14036+ // so a bare deletion would silently strip the key and hand a background exporter
14037+ // its default period. No D2 conversion: not a stack collection member, not a
14038+ // stored row. See `system-tracing-otel-exporter-durations-unit-in-key`.
14039+ 'system/TracingConfig:performance.exportInterval',
1391214040 // #14477 — ADR-0049 enforce-or-remove (maintainer ruling 2026-09-02, ruled A:
1391314041 // retire per family). One of the hour/minute/day-shaped deadline keys of the
1391414042 // incident-response / training / change-management families: declared on the
0 commit comments