From 058c64fc80fbcb6b7e1155e89c66ce9dd3a969f7 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 13 Sep 2026 11:40:58 +0000 Subject: [PATCH 1/6] wip(spec): rename the four tracing.zod.ts JSDoc-only duration keys with their unit Claude-Session: https://claude.ai/code/session_015c5G6TmpMKgnusmTpD7Ntt Co-authored-by: Claude --- ...atibility__exporter.batch.exportTimeout.ts | 11 ++ ...tibility__exporter.batch.scheduledDelay.ts | 13 ++ ...elemetryCompatibility__exporter.timeout.ts | 16 ++ ...acingConfig__performance.exportInterval.ts | 13 ++ ...ing-otel-exporter-durations-unit-in-key.ts | 77 +++++++++ packages/spec/src/migrations/registry.ts | 118 ++++++++++++++ packages/spec/src/system/tracing.test.ts | 153 +++++++++++++++++- packages/spec/src/system/tracing.zod.ts | 82 ++++++++-- 8 files changed, 465 insertions(+), 18 deletions(-) create mode 100644 packages/spec/src/migrations/entries/retired-keys/18.system__OpenTelemetryCompatibility__exporter.batch.exportTimeout.ts create mode 100644 packages/spec/src/migrations/entries/retired-keys/18.system__OpenTelemetryCompatibility__exporter.batch.scheduledDelay.ts create mode 100644 packages/spec/src/migrations/entries/retired-keys/18.system__OpenTelemetryCompatibility__exporter.timeout.ts create mode 100644 packages/spec/src/migrations/entries/retired-keys/18.system__TracingConfig__performance.exportInterval.ts create mode 100644 packages/spec/src/migrations/entries/semantic/18.system-tracing-otel-exporter-durations-unit-in-key.ts diff --git a/packages/spec/src/migrations/entries/retired-keys/18.system__OpenTelemetryCompatibility__exporter.batch.exportTimeout.ts b/packages/spec/src/migrations/entries/retired-keys/18.system__OpenTelemetryCompatibility__exporter.batch.exportTimeout.ts new file mode 100644 index 0000000000..4e2b3593a9 --- /dev/null +++ b/packages/spec/src/migrations/entries/retired-keys/18.system__OpenTelemetryCompatibility__exporter.batch.exportTimeout.ts @@ -0,0 +1,11 @@ +// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. + +// #15939 ruling A (per-file remediation of #14478). `exporter.batch.exportTimeout` +// said "Export timeout in milliseconds" in a source JSDoc and carried NO +// `.describe()` at all, so the published reference page showed a bare 30000. +// Renamed to `exportTimeoutMs`; the value and the 30000 default are unchanged. +// Tombstoned with `retiredKey()`: the nested `batch` object is not strict, so a +// bare deletion would silently strip the key. No D2 conversion: not a stack +// collection member, not a stored row. See +// `system-tracing-otel-exporter-durations-unit-in-key`. +export const entry = 'system/OpenTelemetryCompatibility:exporter.batch.exportTimeout'; diff --git a/packages/spec/src/migrations/entries/retired-keys/18.system__OpenTelemetryCompatibility__exporter.batch.scheduledDelay.ts b/packages/spec/src/migrations/entries/retired-keys/18.system__OpenTelemetryCompatibility__exporter.batch.scheduledDelay.ts new file mode 100644 index 0000000000..d6ee192c9e --- /dev/null +++ b/packages/spec/src/migrations/entries/retired-keys/18.system__OpenTelemetryCompatibility__exporter.batch.scheduledDelay.ts @@ -0,0 +1,13 @@ +// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. + +// #15939 ruling A (per-file remediation of #14478). `exporter.batch.scheduledDelay` +// said "Scheduled delay in milliseconds" in a source JSDoc and carried NO +// `.describe()` at all, so the published reference page showed a bare 5000. +// Renamed to `scheduledDelayMs`, the plain suffix: the Delay-plus-Ms pairing is +// already the family spelling on this tree (`maxDelayMs`, `initialDelayMs`, +// `retryDelayMs`, `delayMs`, `debounceDelayMs`) and no competing form exists. +// The value and the 5000 default are unchanged. Tombstoned with `retiredKey()`: +// the nested `batch` object is not strict, so a bare deletion would silently +// strip the key. No D2 conversion: not a stack collection member, not a stored +// row. See `system-tracing-otel-exporter-durations-unit-in-key`. +export const entry = 'system/OpenTelemetryCompatibility:exporter.batch.scheduledDelay'; diff --git a/packages/spec/src/migrations/entries/retired-keys/18.system__OpenTelemetryCompatibility__exporter.timeout.ts b/packages/spec/src/migrations/entries/retired-keys/18.system__OpenTelemetryCompatibility__exporter.timeout.ts new file mode 100644 index 0000000000..ba6799aadc --- /dev/null +++ b/packages/spec/src/migrations/entries/retired-keys/18.system__OpenTelemetryCompatibility__exporter.timeout.ts @@ -0,0 +1,16 @@ +// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. + +// #15939 ruling A (per-file remediation of #14478). `exporter.timeout` said +// "Timeout in milliseconds" in a source JSDoc and carried NO `.describe()` at +// all, so the `content/docs/references/**` page published a bare 10000 with no +// unit anywhere on it. Renamed to `timeoutMs`; the value and the 10000 default +// are unchanged. It keeps the plain suffix even though its `exporter.batch` +// sibling becomes `exportTimeoutMs`: the two are the OpenTelemetry exporter's +// own request deadline and the batch processor's export deadline, two distinct +// knobs the shape already spelled apart, and the nesting keeps every read point +// unambiguous. Tombstoned with `retiredKey()`: the nested `exporter` object is +// not strict, so a bare deletion would silently strip the key and let a 10000 ms +// default land on an exporter deadline the operator had deliberately changed. +// No D2 conversion: not a stack collection member, not a stored row. See +// `system-tracing-otel-exporter-durations-unit-in-key`. +export const entry = 'system/OpenTelemetryCompatibility:exporter.timeout'; diff --git a/packages/spec/src/migrations/entries/retired-keys/18.system__TracingConfig__performance.exportInterval.ts b/packages/spec/src/migrations/entries/retired-keys/18.system__TracingConfig__performance.exportInterval.ts new file mode 100644 index 0000000000..fd57ee2934 --- /dev/null +++ b/packages/spec/src/migrations/entries/retired-keys/18.system__TracingConfig__performance.exportInterval.ts @@ -0,0 +1,13 @@ +// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. + +// #15939 ruling A (per-file remediation of #14478). `performance.exportInterval` +// said "Background export interval in milliseconds" in a source JSDoc and +// carried NO `.describe()` at all, so the published reference page showed a bare +// 5000. Renamed to `exportIntervalMs`; the value and the 5000 default are +// unchanged. `intervalMs` is the family spelling already attested 14 times in key +// position on this tree, so the suffix lands on a name the surface uses. +// Tombstoned with `retiredKey()`: the nested `performance` object is not strict, +// so a bare deletion would silently strip the key and hand a background exporter +// its default period. No D2 conversion: not a stack collection member, not a +// stored row. See `system-tracing-otel-exporter-durations-unit-in-key`. +export const entry = 'system/TracingConfig:performance.exportInterval'; diff --git a/packages/spec/src/migrations/entries/semantic/18.system-tracing-otel-exporter-durations-unit-in-key.ts b/packages/spec/src/migrations/entries/semantic/18.system-tracing-otel-exporter-durations-unit-in-key.ts new file mode 100644 index 0000000000..ebad002c86 --- /dev/null +++ b/packages/spec/src/migrations/entries/semantic/18.system-tracing-otel-exporter-durations-unit-in-key.ts @@ -0,0 +1,77 @@ +// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. + +import type { SemanticMigration } from '../../types.js'; + +export const entry: SemanticMigration = { + id: 'system-tracing-otel-exporter-durations-unit-in-key', + // No backticks in `surface` — build-upgrade-guide.ts renders it inside a + // code span AND a table cell. + surface: 'the four tracing-configuration durations whose unit lived in a source JSDoc only: ' + + 'OpenTelemetryCompatibility.exporter.timeout, ' + + 'OpenTelemetryCompatibility.exporter.batch.exportTimeout, ' + + 'OpenTelemetryCompatibility.exporter.batch.scheduledDelay and ' + + 'TracingConfig.performance.exportInterval (system/tracing.zod.ts)', + replacement: 'timeoutMs, exportTimeoutMs, scheduledDelayMs and exportIntervalMs — rename each ' + + 'key; all four values (milliseconds) and their 10000 / 30000 / 5000 / 5000 defaults are ' + + 'unchanged', + reason: + 'Director-seat ruling A on #15939, 2026-09-11, carrying the maintainer\'s 「同意」 (decision ' + + 'batch #115), executing the #14478 rule per file. It follows ' + + 'system-tracing-span-duration-unit-in-key on this same file and does not amend it: that ' + + 'entry retired Span.duration under ruling B, whose population was the describe channel, ' + + 'and these four keys were never in it — they are the JSDoc-only channel #15939 opened, ' + + 'which is why one file carries two rounds. Each key named milliseconds in its JSDoc — ' + + '"Timeout in milliseconds", "Export timeout in milliseconds", "Scheduled delay in ' + + 'milliseconds", "Background export interval in milliseconds" — and the JSDoc above a key ' + + 'is NOT what content/docs/references/** renders; .describe() is. Measured on this tree: ' + + 'all four carried NO .describe() at all, so the published reference row for each was a ' + + 'bare integer with no unit anywhere on the page — a strictly worse channel than the ' + + 'unit-in-prose shape #14478 already refuses, since here the reference reader had no prose ' + + 'to misread. The magnitudes make the guess plausible in both directions: 10000, 30000, ' + + '5000 and 5000 are all defensible as seconds and as milliseconds, and an operator who ' + + 'reads seconds sets an exporter deadline 1000x short. The suffix is the family spelling, ' + + 'counted in key position across packages/spec/src: 281 *Ms declarations over 42 distinct ' + + 'names, timeoutMs 65 of them and intervalMs 14, against 0 key-position timeoutSeconds; ' + + 'the Delay-plus-Ms pairing is likewise already attested (maxDelayMs, initialDelayMs, ' + + 'retryDelayMs, delayMs, debounceDelayMs) with no competing scheduledDelay spelling ' + + 'anywhere. Note this file is milliseconds throughout and its own landed precedent is ' + + 'Span.duration to durationMs, the opposite of the sibling metrics card whose rows were ' + + 'seconds. exporter.timeoutMs and exporter.batch.exportTimeoutMs are deliberately allowed ' + + 'to sit one nesting level apart: the pair pre-exists the rename — the batch sub-object is ' + + 'the OpenTelemetry batch span processor\'s own four knobs (max batch size, max queue ' + + 'size, scheduled delay, export timeout) beside the exporter\'s own request deadline — so ' + + 'renaming either to something more distinctive would depart from the vocabulary the shape ' + + 'mirrors, and the nesting already disambiguates every read point ' + + '(exporter.timeoutMs vs exporter.batch.exportTimeoutMs). All four old spellings are ' + + 'retiredKey() tombstones: neither OpenTelemetryCompatibilitySchema nor TracingConfigSchema ' + + 'nor any object nested inside them is .strict(), so a bare deletion would be a SILENT ' + + 'STRIP (#3733, ADR-0104) — and the stripped value lands on an export deadline and a ' + + 'background export period. Why a semantic entry and not a D2 conversion: the conversion ' + + 'chain walks a normalized STACK, and neither def is an authorable surface — stack.zod.ts ' + + 'declares no tracing collection, no metadata-type binding or manifest embed carries ' + + 'either, and a tracing configuration is never a stored sys_metadata row — so a conversion ' + + 'would be a transform with no seam that ever runs. That is the same disposition ' + + 'system-tracing-span-duration-unit-in-key recorded for the other key on this file. ' + + 'Measured on 98bd7986fe: NO in-repo reader exists outside packages/spec — ' + + 'OpenTelemetryCompatibility, TracingConfig and all three batch key names occur 0 times ' + + 'across the whole tree excluding packages/spec and the generated reference pages, against ' + + 'a lit control of 20875 Schema occurrences on the same corpus; inside packages/spec the ' + + 'only occurrences are tracing.zod.ts, its test, and the generated rows in ' + + 'content/docs/references/system/tracing.mdx, which this rename regenerates. And the ' + + 'pinned objectui checkout — `.objectui-sha` = `53ded82bf7a494f54e344e19099dbf00854b8694` — names none of it: all 37 exports of ' + + 'tracing.zod.ts and each of the four key names occur 0 times across its 6409 tracked ' + + 'files (the 404 Span and 40 SpanSchema hits are objectui\'s own HTML text-span component, ' + + 'TextSpanSchema, an unrelated name), against lit controls objectstack 10171 and ' + + '@objectstack/spec 3479 on the same corpus.', + acceptanceCriteria: + 'Every author and reader of an OpenTelemetryCompatibility spells exporter.timeoutMs, ' + + 'exporter.batch.exportTimeoutMs and exporter.batch.scheduledDelayMs, and every one of a ' + + 'TracingConfig spells performance.exportIntervalMs. Authoring any old spelling fails to ' + + 'compile (input type `never`) and fails to parse with the rename prescription naming the ' + + 'suffixed key — not with a generic unrecognized_keys issue, which these non-strict shapes ' + + 'could never have raised anyway. Behaviour is unchanged: the same milliseconds, the same ' + + '10000 / 30000 / 5000 / 5000 defaults and the same int().positive() bounds, and all four ' + + 'published describes now name milliseconds where before there was no describe at all. ' + + 'The authorable-surface and authorable-defaults ledgers move nothing: every one of the ' + + 'four is NESTED, and those artifacts record top-level keys per def only.', +}; diff --git a/packages/spec/src/migrations/registry.ts b/packages/spec/src/migrations/registry.ts index 9cfa60fb60..bf555fca83 100644 --- a/packages/spec/src/migrations/registry.ts +++ b/packages/spec/src/migrations/registry.ts @@ -10544,6 +10544,79 @@ const step18: MigrationStep = { + 'one to check by hand rather than by search-and-replace: after the migration a reader ' + 'can tell at the authoring site that 300 and 30000 are not the same kind of number.', }, + { + id: 'system-tracing-otel-exporter-durations-unit-in-key', + // No backticks in `surface` — build-upgrade-guide.ts renders it inside a + // code span AND a table cell. + surface: 'the four tracing-configuration durations whose unit lived in a source JSDoc only: ' + + 'OpenTelemetryCompatibility.exporter.timeout, ' + + 'OpenTelemetryCompatibility.exporter.batch.exportTimeout, ' + + 'OpenTelemetryCompatibility.exporter.batch.scheduledDelay and ' + + 'TracingConfig.performance.exportInterval (system/tracing.zod.ts)', + replacement: 'timeoutMs, exportTimeoutMs, scheduledDelayMs and exportIntervalMs — rename each ' + + 'key; all four values (milliseconds) and their 10000 / 30000 / 5000 / 5000 defaults are ' + + 'unchanged', + reason: + 'Director-seat ruling A on #15939, 2026-09-11, carrying the maintainer\'s 「同意」 (decision ' + + 'batch #115), executing the #14478 rule per file. It follows ' + + 'system-tracing-span-duration-unit-in-key on this same file and does not amend it: that ' + + 'entry retired Span.duration under ruling B, whose population was the describe channel, ' + + 'and these four keys were never in it — they are the JSDoc-only channel #15939 opened, ' + + 'which is why one file carries two rounds. Each key named milliseconds in its JSDoc — ' + + '"Timeout in milliseconds", "Export timeout in milliseconds", "Scheduled delay in ' + + 'milliseconds", "Background export interval in milliseconds" — and the JSDoc above a key ' + + 'is NOT what content/docs/references/** renders; .describe() is. Measured on this tree: ' + + 'all four carried NO .describe() at all, so the published reference row for each was a ' + + 'bare integer with no unit anywhere on the page — a strictly worse channel than the ' + + 'unit-in-prose shape #14478 already refuses, since here the reference reader had no prose ' + + 'to misread. The magnitudes make the guess plausible in both directions: 10000, 30000, ' + + '5000 and 5000 are all defensible as seconds and as milliseconds, and an operator who ' + + 'reads seconds sets an exporter deadline 1000x short. The suffix is the family spelling, ' + + 'counted in key position across packages/spec/src: 281 *Ms declarations over 42 distinct ' + + 'names, timeoutMs 65 of them and intervalMs 14, against 0 key-position timeoutSeconds; ' + + 'the Delay-plus-Ms pairing is likewise already attested (maxDelayMs, initialDelayMs, ' + + 'retryDelayMs, delayMs, debounceDelayMs) with no competing scheduledDelay spelling ' + + 'anywhere. Note this file is milliseconds throughout and its own landed precedent is ' + + 'Span.duration to durationMs, the opposite of the sibling metrics card whose rows were ' + + 'seconds. exporter.timeoutMs and exporter.batch.exportTimeoutMs are deliberately allowed ' + + 'to sit one nesting level apart: the pair pre-exists the rename — the batch sub-object is ' + + 'the OpenTelemetry batch span processor\'s own four knobs (max batch size, max queue ' + + 'size, scheduled delay, export timeout) beside the exporter\'s own request deadline — so ' + + 'renaming either to something more distinctive would depart from the vocabulary the shape ' + + 'mirrors, and the nesting already disambiguates every read point ' + + '(exporter.timeoutMs vs exporter.batch.exportTimeoutMs). All four old spellings are ' + + 'retiredKey() tombstones: neither OpenTelemetryCompatibilitySchema nor TracingConfigSchema ' + + 'nor any object nested inside them is .strict(), so a bare deletion would be a SILENT ' + + 'STRIP (#3733, ADR-0104) — and the stripped value lands on an export deadline and a ' + + 'background export period. Why a semantic entry and not a D2 conversion: the conversion ' + + 'chain walks a normalized STACK, and neither def is an authorable surface — stack.zod.ts ' + + 'declares no tracing collection, no metadata-type binding or manifest embed carries ' + + 'either, and a tracing configuration is never a stored sys_metadata row — so a conversion ' + + 'would be a transform with no seam that ever runs. That is the same disposition ' + + 'system-tracing-span-duration-unit-in-key recorded for the other key on this file. ' + + 'Measured on 98bd7986fe: NO in-repo reader exists outside packages/spec — ' + + 'OpenTelemetryCompatibility, TracingConfig and all three batch key names occur 0 times ' + + 'across the whole tree excluding packages/spec and the generated reference pages, against ' + + 'a lit control of 20875 Schema occurrences on the same corpus; inside packages/spec the ' + + 'only occurrences are tracing.zod.ts, its test, and the generated rows in ' + + 'content/docs/references/system/tracing.mdx, which this rename regenerates. And the ' + + 'pinned objectui checkout — `.objectui-sha` = `53ded82bf7a494f54e344e19099dbf00854b8694` — names none of it: all 37 exports of ' + + 'tracing.zod.ts and each of the four key names occur 0 times across its 6409 tracked ' + + 'files (the 404 Span and 40 SpanSchema hits are objectui\'s own HTML text-span component, ' + + 'TextSpanSchema, an unrelated name), against lit controls objectstack 10171 and ' + + '@objectstack/spec 3479 on the same corpus.', + acceptanceCriteria: + 'Every author and reader of an OpenTelemetryCompatibility spells exporter.timeoutMs, ' + + 'exporter.batch.exportTimeoutMs and exporter.batch.scheduledDelayMs, and every one of a ' + + 'TracingConfig spells performance.exportIntervalMs. Authoring any old spelling fails to ' + + 'compile (input type `never`) and fails to parse with the rename prescription naming the ' + + 'suffixed key — not with a generic unrecognized_keys issue, which these non-strict shapes ' + + 'could never have raised anyway. Behaviour is unchanged: the same milliseconds, the same ' + + '10000 / 30000 / 5000 / 5000 defaults and the same int().positive() bounds, and all four ' + + 'published describes now name milliseconds where before there was no describe at all. ' + + 'The authorable-surface and authorable-defaults ledgers move nothing: every one of the ' + + 'four is NESTED, and those artifacts record top-level keys per def only.', + }, { id: 'system-tracing-span-duration-unit-in-key', surface: 'Span.duration, the emitted trace-span length whose name carried no unit ' @@ -13355,6 +13428,40 @@ export const RETIRED_KEYS_BY_MAJOR: Readonly> // declares no `metrics` collection and an aggregation config is not a stored // metadata row. See `system-metrics-window-durations-unit-in-key`. 'system/MetricAggregationConfig:window.size', + // #15939 ruling A (per-file remediation of #14478). `exporter.batch.exportTimeout` + // said "Export timeout in milliseconds" in a source JSDoc and carried NO + // `.describe()` at all, so the published reference page showed a bare 30000. + // Renamed to `exportTimeoutMs`; the value and the 30000 default are unchanged. + // Tombstoned with `retiredKey()`: the nested `batch` object is not strict, so a + // bare deletion would silently strip the key. No D2 conversion: not a stack + // collection member, not a stored row. See + // `system-tracing-otel-exporter-durations-unit-in-key`. + 'system/OpenTelemetryCompatibility:exporter.batch.exportTimeout', + // #15939 ruling A (per-file remediation of #14478). `exporter.batch.scheduledDelay` + // said "Scheduled delay in milliseconds" in a source JSDoc and carried NO + // `.describe()` at all, so the published reference page showed a bare 5000. + // Renamed to `scheduledDelayMs`, the plain suffix: the Delay-plus-Ms pairing is + // already the family spelling on this tree (`maxDelayMs`, `initialDelayMs`, + // `retryDelayMs`, `delayMs`, `debounceDelayMs`) and no competing form exists. + // The value and the 5000 default are unchanged. Tombstoned with `retiredKey()`: + // the nested `batch` object is not strict, so a bare deletion would silently + // strip the key. No D2 conversion: not a stack collection member, not a stored + // row. See `system-tracing-otel-exporter-durations-unit-in-key`. + 'system/OpenTelemetryCompatibility:exporter.batch.scheduledDelay', + // #15939 ruling A (per-file remediation of #14478). `exporter.timeout` said + // "Timeout in milliseconds" in a source JSDoc and carried NO `.describe()` at + // all, so the `content/docs/references/**` page published a bare 10000 with no + // unit anywhere on it. Renamed to `timeoutMs`; the value and the 10000 default + // are unchanged. It keeps the plain suffix even though its `exporter.batch` + // sibling becomes `exportTimeoutMs`: the two are the OpenTelemetry exporter's + // own request deadline and the batch processor's export deadline, two distinct + // knobs the shape already spelled apart, and the nesting keeps every read point + // unambiguous. Tombstoned with `retiredKey()`: the nested `exporter` object is + // not strict, so a bare deletion would silently strip the key and let a 10000 ms + // default land on an exporter deadline the operator had deliberately changed. + // No D2 conversion: not a stack collection member, not a stored row. See + // `system-tracing-otel-exporter-durations-unit-in-key`. + 'system/OpenTelemetryCompatibility:exporter.timeout', // #15679 (stack card 4/6 of #14478) — ruling B. `QueueConfig.rateLimit.duration` // said "Duration in milliseconds" in prose and nothing else — while // `TaskResult.durationMs`, ninety lines earlier in the SAME file, already spelled @@ -13466,6 +13573,17 @@ export const RETIRED_KEYS_BY_MAJOR: Readonly> // connection is host configuration, not a stored metadata row. // See `system-object-storage-durations-unit-in-key`. 'system/StorageConnection:timeout', + // #15939 ruling A (per-file remediation of #14478). `performance.exportInterval` + // said "Background export interval in milliseconds" in a source JSDoc and + // carried NO `.describe()` at all, so the published reference page showed a bare + // 5000. Renamed to `exportIntervalMs`; the value and the 5000 default are + // unchanged. `intervalMs` is the family spelling already attested 14 times in key + // position on this tree, so the suffix lands on a name the surface uses. + // Tombstoned with `retiredKey()`: the nested `performance` object is not strict, + // so a bare deletion would silently strip the key and hand a background exporter + // its default period. No D2 conversion: not a stack collection member, not a + // stored row. See `system-tracing-otel-exporter-durations-unit-in-key`. + 'system/TracingConfig:performance.exportInterval', // #14477 — ADR-0049 enforce-or-remove (maintainer ruling 2026-09-02, ruled A: // retire per family). One of the hour/minute/day-shaped deadline keys of the // incident-response / training / change-management families: declared on the diff --git a/packages/spec/src/system/tracing.test.ts b/packages/spec/src/system/tracing.test.ts index 0b1c814c78..be6e5a9c0b 100644 --- a/packages/spec/src/system/tracing.test.ts +++ b/packages/spec/src/system/tracing.test.ts @@ -412,8 +412,8 @@ describe('OpenTelemetryCompatibilitySchema', () => { batch: { maxBatchSize: 1024, maxQueueSize: 4096, - exportTimeout: 60000, - scheduledDelay: 10000, + exportTimeoutMs: 60000, + scheduledDelayMs: 10000, }, }, resource: { @@ -434,7 +434,7 @@ describe('OpenTelemetryCompatibilitySchema', () => { }, }); - expect(config.exporter.timeout).toBe(10000); + expect(config.exporter.timeoutMs).toBe(10000); expect(config.exporter.compression).toBe('none'); }); }); @@ -542,11 +542,148 @@ describe('Span.duration carries its unit (#15679)', () => { expect(SpanSchema.safeParse({ ...base, durationMs: -1 }).success).toBe(false); }); - it('leaves the OTel exporter `timeout` alone — its describe names no unit, so it is outside the population', () => { - const config = OpenTelemetryCompatibilitySchema.parse({ - exporter: { type: 'console' }, - resource: { serviceName: 'test' }, +}); + +// #17785, ruling A on #15939 (per-file remediation of #14478). The four tracing +// durations whose unit lived in a source JSDoc only — and whose `.describe()` +// did not exist at all, so the published reference row was a bare integer. +// +// This block REPLACES, and relocates, the guard that used to close the #15679 +// describe above: `it('leaves the OTel exporter timeout alone — its describe +// names no unit, so it is outside the population')`. That pin was written to +// catch exactly this sweep, so it succeeds by failing: its key, its "names no +// unit" clause and its "outside the population" clause all go false here. It is +// replaced by the three-part shape #15679 itself set on this file — a refusal +// pin asserting the issue CODE and the prescription (never a bare `toThrow()`), +// an acceptance pin at the same magnitude and the same default, and a describe +// pin proving the unit now reaches the published channel — and moved out of a +// describe headed `Span.duration carries its unit`, which is not its subject. +// +// These shapes are NOT `.strict()`, so `unrecognized_keys` was never the +// alternative: a bare deletion would have been a silent strip that lands a +// default on an exporter deadline. +describe('the OTel exporter and performance durations carry their unit (#17785)', () => { + const otelBase = { + exporter: { type: 'console' as const }, + resource: { serviceName: 'test' }, + }; + const tracingBase = { name: 'test_tracing', label: 'Test Tracing' }; + + /** + * `describe()` is what `content/docs/references/**` publishes; JSDoc is not. + * + * Descends by `shape`, unwrapping wrappers (`.optional()`, `.default()`) only + * to reach a CHILD — never on the leaf, whose `description` `.describe()` set + * on the outermost node and which an unwrap would discard. + */ + const shapeOf = (node: unknown): Record | undefined => { + let cur = node as { shape?: Record; def?: { innerType?: unknown } }; + while (cur && !cur.shape && cur.def?.innerType) cur = cur.def.innerType as typeof cur; + return cur?.shape; + }; + const describeOf = (schema: unknown, path: readonly string[]): string | undefined => { + let cursor: unknown = schema; + for (const segment of path) { + const shape = shapeOf(cursor); + if (!shape) return undefined; + cursor = shape[segment]; + } + return (cursor as { description?: string } | undefined)?.description; + }; + + it.each([ + ['exporter.timeout', 'timeoutMs', 10000], + ['exporter.batch.exportTimeout', 'exportTimeoutMs', 30000], + ['exporter.batch.scheduledDelay', 'scheduledDelayMs', 5000], + ] as const)('REFUSES the retired `%s` with the rename in the message', (retired, renamed) => { + const leaf = retired.split('.').pop()!; + const body = retired.startsWith('exporter.batch') + ? { ...otelBase, exporter: { ...otelBase.exporter, batch: { [leaf]: 1234 } } } + : { ...otelBase, exporter: { ...otelBase.exporter, [leaf]: 1234 } }; + const result = OpenTelemetryCompatibilitySchema.safeParse(body); + expect(result.success).toBe(false); + const issue = result.error!.issues.find((i) => i.path.join('.') === `${retired}`); + expect(issue).toBeDefined(); + expect(issue!.code).not.toBe('unrecognized_keys'); + expect(issue!.message).toContain( + `\`OpenTelemetryCompatibility.${retired}\` was renamed to \`${renamed}\``, + ); + }); + + it('REFUSES the retired `performance.exportInterval` with the rename in the message', () => { + const result = TracingConfigSchema.safeParse({ + ...tracingBase, + performance: { exportInterval: 1234 }, + }); + expect(result.success).toBe(false); + const issue = result.error!.issues + .find((i) => i.path.join('.') === 'performance.exportInterval'); + expect(issue).toBeDefined(); + expect(issue!.code).not.toBe('unrecognized_keys'); + expect(issue!.message).toContain( + '`TracingConfig.performance.exportInterval` was renamed to `exportIntervalMs`', + ); + }); + + it('accepts the suffixed keys at the same magnitudes, and still applies the same defaults', () => { + const authored = OpenTelemetryCompatibilitySchema.parse({ + ...otelBase, + exporter: { + ...otelBase.exporter, + timeoutMs: 10000, + batch: { exportTimeoutMs: 60000, scheduledDelayMs: 10000 }, + }, + }); + expect(authored.exporter.timeoutMs).toBe(10000); + expect(authored.exporter.batch?.exportTimeoutMs).toBe(60000); + expect(authored.exporter.batch?.scheduledDelayMs).toBe(10000); + + // The defaults the rename must not move: 10000 / 30000 / 5000. + const defaulted = OpenTelemetryCompatibilitySchema.parse({ + ...otelBase, + exporter: { ...otelBase.exporter, batch: {} }, }); - expect(config.exporter.timeout).toBe(10000); + expect(defaulted.exporter.timeoutMs).toBe(10000); + expect(defaulted.exporter.batch?.exportTimeoutMs).toBe(30000); + expect(defaulted.exporter.batch?.scheduledDelayMs).toBe(5000); + + const tracing = TracingConfigSchema.parse({ ...tracingBase, performance: {} }); + expect(tracing.performance?.exportIntervalMs).toBe(5000); + expect( + TracingConfigSchema.parse({ + ...tracingBase, + performance: { exportIntervalMs: 250 }, + }).performance?.exportIntervalMs, + ).toBe(250); + }); + + it('still refuses a non-positive value on each renamed key', () => { + expect(OpenTelemetryCompatibilitySchema.safeParse({ + ...otelBase, + exporter: { ...otelBase.exporter, timeoutMs: 0 }, + }).success).toBe(false); + expect(OpenTelemetryCompatibilitySchema.safeParse({ + ...otelBase, + exporter: { ...otelBase.exporter, batch: { exportTimeoutMs: -1 } }, + }).success).toBe(false); + expect(OpenTelemetryCompatibilitySchema.safeParse({ + ...otelBase, + exporter: { ...otelBase.exporter, batch: { scheduledDelayMs: 0 } }, + }).success).toBe(false); + expect(TracingConfigSchema.safeParse({ + ...tracingBase, + performance: { exportIntervalMs: -1 }, + }).success).toBe(false); + }); + + it('publishes the unit in the `describe()` the reference pages render', () => { + expect(describeOf(OpenTelemetryCompatibilitySchema, ['exporter', 'timeoutMs'])) + .toBe('Exporter request timeout in milliseconds'); + expect(describeOf(OpenTelemetryCompatibilitySchema, ['exporter', 'batch', 'exportTimeoutMs'])) + .toBe('Batch export timeout in milliseconds'); + expect(describeOf(OpenTelemetryCompatibilitySchema, ['exporter', 'batch', 'scheduledDelayMs'])) + .toBe('Delay between scheduled batch exports, in milliseconds'); + expect(describeOf(TracingConfigSchema, ['performance', 'exportIntervalMs'])) + .toBe('Background span-export interval in milliseconds'); }); }); diff --git a/packages/spec/src/system/tracing.zod.ts b/packages/spec/src/system/tracing.zod.ts index 976315c6c7..81c8fb8bcd 100644 --- a/packages/spec/src/system/tracing.zod.ts +++ b/packages/spec/src/system/tracing.zod.ts @@ -536,9 +536,25 @@ export const OpenTelemetryCompatibilitySchema = lazySchema(() => z.object({ headers: z.record(z.string(), z.string()).optional().describe('HTTP headers'), /** - * Timeout in milliseconds - */ - timeout: z.number().int().positive().optional().default(10000), + * Per-export request deadline, in milliseconds. + * + * Renamed from `timeout` (#17785, ruling A on #15939 executing #14478): + * the unit lived in this JSDoc only and the key carried no `.describe()` + * at all, so the reference page published a bare 10000. Tombstoned rather + * than deleted because this nested object is not `.strict()`. + */ + timeoutMs: z.number().int().positive().optional().default(10000) + .describe('Exporter request timeout in milliseconds'), + + /** Tombstone for the rename above (#17785, ruling A on #15939). */ + timeout: retiredKey( + '`OpenTelemetryCompatibility.exporter.timeout` was renamed to `timeoutMs` in ' + + '@objectstack/spec 18 — the unit of a duration-shaped number lives in the key name, ' + + 'not only in the describe prose. Its unit (milliseconds) lived in a source JSDoc ' + + 'only and the key carried no describe at all, so the reference-page reader got a ' + + 'bare 10000 and could not tell it from 10000 seconds. Rename the key to `timeoutMs`; ' + + 'the value (milliseconds) and the 10000 default are unchanged.', + ), /** * Compression @@ -560,14 +576,44 @@ export const OpenTelemetryCompatibilitySchema = lazySchema(() => z.object({ maxQueueSize: z.number().int().positive().optional().default(2048), /** - * Export timeout in milliseconds + * Batch-processor export deadline, in milliseconds. + * + * Renamed from `exportTimeout` (#17785, ruling A on #15939 executing + * #14478): the unit lived in this JSDoc only and the key carried no + * `.describe()` at all. Tombstoned rather than deleted because this + * nested object is not `.strict()`. */ - exportTimeout: z.number().int().positive().optional().default(30000), + exportTimeoutMs: z.number().int().positive().optional().default(30000) + .describe('Batch export timeout in milliseconds'), /** - * Scheduled delay in milliseconds + * Delay between two scheduled batch exports, in milliseconds. + * + * Renamed from `scheduledDelay` (#17785, ruling A on #15939 executing + * #14478): the unit lived in this JSDoc only and the key carried no + * `.describe()` at all. Tombstoned rather than deleted because this + * nested object is not `.strict()`. */ - scheduledDelay: z.number().int().positive().optional().default(5000), + scheduledDelayMs: z.number().int().positive().optional().default(5000) + .describe('Delay between scheduled batch exports, in milliseconds'), + + /** Tombstones for the two renames above (#17785, ruling A on #15939). */ + exportTimeout: retiredKey( + '`OpenTelemetryCompatibility.exporter.batch.exportTimeout` was renamed to ' + + '`exportTimeoutMs` in @objectstack/spec 18 — the unit of a duration-shaped number ' + + 'lives in the key name, not only in the describe prose. Its unit (milliseconds) ' + + 'lived in a source JSDoc only and the key carried no describe at all, so the ' + + 'reference-page reader got a bare 30000. Rename the key to `exportTimeoutMs`; the ' + + 'value (milliseconds) and the 30000 default are unchanged.', + ), + scheduledDelay: retiredKey( + '`OpenTelemetryCompatibility.exporter.batch.scheduledDelay` was renamed to ' + + '`scheduledDelayMs` in @objectstack/spec 18 — the unit of a duration-shaped number ' + + 'lives in the key name, not only in the describe prose. Its unit (milliseconds) ' + + 'lived in a source JSDoc only and the key carried no describe at all, so the ' + + 'reference-page reader got a bare 5000. Rename the key to `scheduledDelayMs`; the ' + + 'value (milliseconds) and the 5000 default are unchanged.', + ), }).optional(), }).describe('Exporter configuration'), @@ -718,9 +764,25 @@ export const TracingConfigSchema = lazySchema(() => z.object({ asyncExport: z.boolean().optional().default(true), /** - * Background export interval in milliseconds - */ - exportInterval: z.number().int().positive().optional().default(5000), + * Background span-export interval, in milliseconds. + * + * Renamed from `exportInterval` (#17785, ruling A on #15939 executing + * #14478): the unit lived in this JSDoc only and the key carried no + * `.describe()` at all. Tombstoned rather than deleted because this + * nested object is not `.strict()`. + */ + exportIntervalMs: z.number().int().positive().optional().default(5000) + .describe('Background span-export interval in milliseconds'), + + /** Tombstone for the rename above (#17785, ruling A on #15939). */ + exportInterval: retiredKey( + '`TracingConfig.performance.exportInterval` was renamed to `exportIntervalMs` in ' + + '@objectstack/spec 18 — the unit of a duration-shaped number lives in the key name, ' + + 'not only in the describe prose. Its unit (milliseconds) lived in a source JSDoc ' + + 'only and the key carried no describe at all, so the reference-page reader got a ' + + 'bare 5000. Rename the key to `exportIntervalMs`; the value (milliseconds) and the ' + + '5000 default are unchanged.', + ), }).optional(), }).describe('Tracing configuration')); From 96e730f0d2389a5ee7d8eea7a531428870d1fd2b Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 13 Sep 2026 11:58:01 +0000 Subject: [PATCH 2/6] feat(spec)!: the four `system/tracing.zod.ts` duration keys carry their unit in the key name MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ruling A on #15939 (2026-09-11, maintainer 「同意」, decision batch #115), per-file remediation of the #14478 rule — the seventh and last card. `exporter.timeout`, `exporter.batch.exportTimeout`, `exporter.batch.scheduledDelay` and `performance.exportInterval` each named milliseconds in a source JSDoc and carried no `.describe()` at all, so the published reference row was a bare integer with no unit anywhere on it. Each is renamed with the `Ms` suffix, tombstoned with `retiredKey()`, and registered as an ADR-0087 D3 semantic entry plus a `RETIRED_KEYS_BY_MAJOR[18]` row. All four are nested, so the authorable-surface / authorable-defaults ratchets move nothing. Claude-Session: https://claude.ai/code/session_015c5G6TmpMKgnusmTpD7Ntt Co-authored-by: Claude --- ...85-tracing-otel-exporter-duration-units.md | 98 +++++++++++++++++++ content/docs/references/system/tracing.mdx | 15 ++- packages/spec/src/system/tracing.test.ts | 6 +- 3 files changed, 113 insertions(+), 6 deletions(-) create mode 100644 .changeset/17785-tracing-otel-exporter-duration-units.md diff --git a/.changeset/17785-tracing-otel-exporter-duration-units.md b/.changeset/17785-tracing-otel-exporter-duration-units.md new file mode 100644 index 0000000000..ab053b6829 --- /dev/null +++ b/.changeset/17785-tracing-otel-exporter-duration-units.md @@ -0,0 +1,98 @@ +--- +"@objectstack/spec": minor +--- + +feat(spec)!: the four `system/tracing.zod.ts` duration keys carry their unit in the key name (#17785, ruling A on #15939) + + + +**BREAKING** — the OTel exporter deadline, the batch processor's two knobs and the background +span-export period now carry `Ms` in the key name. + +| | before | after | +|:--|:--|:--| +| `OpenTelemetryCompatibility.exporter` | `timeout: 10000` | `timeoutMs: 10000` | +| `OpenTelemetryCompatibility.exporter.batch` | `exportTimeout: 30000` | `exportTimeoutMs: 30000` | +| `OpenTelemetryCompatibility.exporter.batch` | `scheduledDelay: 5000` | `scheduledDelayMs: 5000` | +| `TracingConfig.performance` | `exportInterval: 5000` | `exportIntervalMs: 5000` | +| values, defaults, bounds | ms; 10000 / 30000 / 5000 / 5000; `int().positive()` | **unchanged** | + +## Migration + +```diff + const otel = OpenTelemetryCompatibilitySchema.parse({ + exporter: { + type: 'otlp_grpc', +- timeout: 10000, ++ timeoutMs: 10000, + batch: { +- exportTimeout: 30000, +- scheduledDelay: 5000, ++ exportTimeoutMs: 30000, ++ scheduledDelayMs: 5000, + }, + }, + resource: { serviceName: 'api-server' }, + }); + + const tracing = TracingConfigSchema.parse({ + name: 'default_tracing', + label: 'Default Tracing', +- performance: { exportInterval: 5000 }, ++ performance: { exportIntervalMs: 5000 }, + }); +``` + +Rename the keys. Every value is the same number of milliseconds it always was, the +10000 / 30000 / 5000 / 5000 defaults are unchanged, and nothing else on either def moves. + +## Why + +Each key named milliseconds in a source JSDoc — "Timeout in milliseconds", "Export timeout in +milliseconds", "Scheduled delay in milliseconds", "Background export interval in milliseconds" — +and the JSDoc above a key is not what `content/docs/references/**` renders; `.describe()` is. +Measured on this tree: all four carried **no `.describe()` at all**, so the published reference +row for each was a bare integer with no unit anywhere on the page. That is a strictly worse +channel than the unit-in-prose shape #14478 already refuses — here the reference reader had no +prose to misread. All four magnitudes read plausibly in both units (10000, 30000, 5000, 5000), +and an operator who reads seconds sets an exporter deadline 1000x short. Executes director-seat +ruling A on #15939 (2026-09-11, maintainer 「同意」, decision batch #115), the per-file +remediation of the #14478 rule, and closes the last of that ruling's seven cards. + +The suffix is the family's own spelling, counted in key position across `packages/spec/src`: +281 `*Ms` declarations over 42 distinct names, `timeoutMs` 65 of them and `intervalMs` 14, +against **0** key-position `timeoutSeconds`. The Delay-plus-`Ms` pairing is likewise already +attested (`maxDelayMs`, `initialDelayMs`, `retryDelayMs`, `delayMs`, `debounceDelayMs`) with no +competing `scheduledDelay` spelling anywhere. This file is milliseconds throughout and its own +landed precedent is `Span.duration → durationMs` (#15679) — the opposite of the sibling metrics +card, whose rows were seconds. + +`exporter.timeoutMs` and `exporter.batch.exportTimeoutMs` deliberately sit one nesting level +apart. The pair pre-exists the rename: the `batch` sub-object is the OpenTelemetry batch span +processor's own four knobs (max batch size, max queue size, scheduled delay, export timeout) +beside the exporter's own request deadline. Renaming either to something more distinctive would +depart from the vocabulary this shape mirrors, and the nesting already disambiguates every read +point — `exporter.timeoutMs` versus `exporter.batch.exportTimeoutMs`. + +## The kit + +- a `retiredKey()` tombstone on each old spelling, so `tsc` types it `never` and a value + reaching the parse raises the rename prescription instead of being silently stripped. Neither + `OpenTelemetryCompatibilitySchema` nor `TracingConfigSchema` nor any object nested inside them + is `.strict()`, so `unrecognized_keys` was never the alternative — a bare deletion would have + landed a default on an exporter deadline and a background export period +- the ADR-0087 D3 semantic entry `system-tracing-otel-exporter-durations-unit-in-key` and four + `RETIRED_KEYS_BY_MAJOR[18]` rows. No D2 conversion: `stack.zod.ts` declares no tracing + collection, no metadata-type binding or manifest embed carries either def, and a tracing + configuration is never a stored `sys_metadata` row — so the chain has no seam that runs on + them, the same reading `system-tracing-span-duration-unit-in-key` recorded for the other key + on this file +- pin tests: a refusal pin per row asserting the issue **code** (never a bare `toThrow()`) and + the FROM → TO prescription, an acceptance pin at each retired key's magnitude with the same + default, a bounds pin, and a describe pin proving the unit now reaches the published channel +- the `authorable-surface` / `authorable-defaults` ratchets move **nothing**, and that is the + correct outcome rather than an omission: those artifacts record top-level keys per def + (`build-schemas.ts` reads `schema.properties` one level deep) and every one of these four is + nested +- `Span.duration → durationMs`'s own entry is untouched — a predecessor's scoped record stays + true, and this round's entry opens by saying how it relates to it diff --git a/content/docs/references/system/tracing.mdx b/content/docs/references/system/tracing.mdx index 7061571d30..f082b457df 100644 --- a/content/docs/references/system/tracing.mdx +++ b/content/docs/references/system/tracing.mdx @@ -52,9 +52,10 @@ OpenTelemetry compatibility configuration | **endpoint** | `string` | optional | Exporter endpoint | | **protocol** | `string` | optional | Protocol version | | **headers** | `Record` | optional | HTTP headers | -| **timeout** | `integer` | optional (default: `10000`) | | +| **timeoutMs** | `integer` | optional (default: `10000`) | Exporter request timeout in milliseconds | +| **timeout** | `never` | optional | [REMOVED] `OpenTelemetryCompatibility.exporter.timeout` was renamed to `timeoutMs` in @objectstack/spec 18 — the unit of a duration-shaped number lives in the key name, not only in the describe prose. Its unit (milliseconds) lived in a source JSDoc only and the key carried no describe at all, so the reference-page reader got a bare 10000 and could not tell it from 10000 seconds. Rename the key to `timeoutMs`; the value (milliseconds) and the 10000 default are unchanged. | | **compression** | `Enum<'none' \| 'gzip'>` | optional (default: `"none"`) | | -| **batch** | `{ maxBatchSize: integer; maxQueueSize: integer; exportTimeout: integer; scheduledDelay: integer }` | optional | | +| **batch** | `{ maxBatchSize: integer; maxQueueSize: integer; exportTimeoutMs: integer; scheduledDelayMs: integer }` | optional | | ### Nested Shape: `OpenTelemetryCompatibility.resource` @@ -467,7 +468,7 @@ Tracing configuration | **spanLimits** | `{ maxAttributes?: integer; maxEvents?: integer; maxLinks?: integer; maxAttributeValueLength?: integer }` | optional | | | **traceIdGenerator** | `Enum<'random' \| 'uuid' \| 'custom'>` | optional (default: `"random"`) | | | **customTraceIdGeneratorId** | `string` | optional | Custom generator identifier | -| **performance** | `{ asyncExport?: boolean; exportInterval?: integer }` | optional | | +| **performance** | `{ asyncExport?: boolean; exportIntervalMs?: integer }` | optional | | ### Nested Shape: `TracingConfig.sampling` @@ -491,6 +492,14 @@ Tracing configuration | **instrumentation** | `{ autoInstrumentation?: boolean; libraries?: string[]; disabledLibraries?: string[] }` | optional | | | **semanticConventionsVersion** | `string` | optional | Semantic conventions version | +### Nested Shape: `TracingConfig.performance` + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **asyncExport** | `boolean` | optional (default: `true`) | | +| **exportIntervalMs** | `integer` | optional (default: `5000`) | Background span-export interval in milliseconds | +| **exportInterval** | `never` | optional | [REMOVED] `TracingConfig.performance.exportInterval` was renamed to `exportIntervalMs` in @objectstack/spec 18 — the unit of a duration-shaped number lives in the key name, not only in the describe prose. Its unit (milliseconds) lived in a source JSDoc only and the key carried no describe at all, so the reference-page reader got a bare 5000. Rename the key to `exportIntervalMs`; the value (milliseconds) and the 5000 default are unchanged. | + --- diff --git a/packages/spec/src/system/tracing.test.ts b/packages/spec/src/system/tracing.test.ts index be6e5a9c0b..29d2db8c7c 100644 --- a/packages/spec/src/system/tracing.test.ts +++ b/packages/spec/src/system/tracing.test.ts @@ -592,9 +592,9 @@ describe('the OTel exporter and performance durations carry their unit (#17785)' }; it.each([ - ['exporter.timeout', 'timeoutMs', 10000], - ['exporter.batch.exportTimeout', 'exportTimeoutMs', 30000], - ['exporter.batch.scheduledDelay', 'scheduledDelayMs', 5000], + ['exporter.timeout', 'timeoutMs'], + ['exporter.batch.exportTimeout', 'exportTimeoutMs'], + ['exporter.batch.scheduledDelay', 'scheduledDelayMs'], ] as const)('REFUSES the retired `%s` with the rename in the message', (retired, renamed) => { const leaf = retired.split('.').pop()!; const body = retired.startsWith('exporter.batch') From c3d53951a940fdb10a2aa2d3559bc9031bacbc41 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 13 Sep 2026 12:06:42 +0000 Subject: [PATCH 3/6] docs(spec): anchor every corpus count in the tracing migration prose to its ref A count in a migration entry ships to consumers through the upgrade guide and is permanent published prose, so a number a future reader cannot reproduce is the same defect class this epic exists to remove. Every family count now names `98bd7986fe` and its corpus, and the objectui readings name the pinned sha. Claude-Session: https://claude.ai/code/session_015c5G6TmpMKgnusmTpD7Ntt Co-authored-by: Claude --- ...tibility__exporter.batch.scheduledDelay.ts | 8 ++-- ...acingConfig__performance.exportInterval.ts | 4 +- ...ing-otel-exporter-durations-unit-in-key.ts | 28 +++++++------ packages/spec/src/migrations/registry.ts | 40 +++++++++++-------- 4 files changed, 48 insertions(+), 32 deletions(-) diff --git a/packages/spec/src/migrations/entries/retired-keys/18.system__OpenTelemetryCompatibility__exporter.batch.scheduledDelay.ts b/packages/spec/src/migrations/entries/retired-keys/18.system__OpenTelemetryCompatibility__exporter.batch.scheduledDelay.ts index d6ee192c9e..664a14f8ee 100644 --- a/packages/spec/src/migrations/entries/retired-keys/18.system__OpenTelemetryCompatibility__exporter.batch.scheduledDelay.ts +++ b/packages/spec/src/migrations/entries/retired-keys/18.system__OpenTelemetryCompatibility__exporter.batch.scheduledDelay.ts @@ -3,9 +3,11 @@ // #15939 ruling A (per-file remediation of #14478). `exporter.batch.scheduledDelay` // said "Scheduled delay in milliseconds" in a source JSDoc and carried NO // `.describe()` at all, so the published reference page showed a bare 5000. -// Renamed to `scheduledDelayMs`, the plain suffix: the Delay-plus-Ms pairing is -// already the family spelling on this tree (`maxDelayMs`, `initialDelayMs`, -// `retryDelayMs`, `delayMs`, `debounceDelayMs`) and no competing form exists. +// Renamed to `scheduledDelayMs`, the plain suffix: counted in key position across +// `packages/spec/src` at `98bd7986fe`, the Delay-plus-Ms pairing is already the +// family spelling (`maxDelayMs` 9, `initialDelayMs` 9, `maxRetryDelayMs` 5, +// `debounceDelayMs` 2, `delayMs` 2, `retryDelayMs` 1) and neither +// `scheduledDelayMs` nor `scheduledDelaySeconds` occurs there at all. // The value and the 5000 default are unchanged. Tombstoned with `retiredKey()`: // the nested `batch` object is not strict, so a bare deletion would silently // strip the key. No D2 conversion: not a stack collection member, not a stored diff --git a/packages/spec/src/migrations/entries/retired-keys/18.system__TracingConfig__performance.exportInterval.ts b/packages/spec/src/migrations/entries/retired-keys/18.system__TracingConfig__performance.exportInterval.ts index fd57ee2934..f3c86e014d 100644 --- a/packages/spec/src/migrations/entries/retired-keys/18.system__TracingConfig__performance.exportInterval.ts +++ b/packages/spec/src/migrations/entries/retired-keys/18.system__TracingConfig__performance.exportInterval.ts @@ -5,7 +5,9 @@ // carried NO `.describe()` at all, so the published reference page showed a bare // 5000. Renamed to `exportIntervalMs`; the value and the 5000 default are // unchanged. `intervalMs` is the family spelling already attested 14 times in key -// position on this tree, so the suffix lands on a name the surface uses. +// position across `packages/spec/src` at `98bd7986fe` (0 `exportIntervalMs` and 0 +// `exportIntervalSeconds` there, so nothing competes), so the suffix lands on a +// name the surface already uses. // Tombstoned with `retiredKey()`: the nested `performance` object is not strict, // so a bare deletion would silently strip the key and hand a background exporter // its default period. No D2 conversion: not a stack collection member, not a diff --git a/packages/spec/src/migrations/entries/semantic/18.system-tracing-otel-exporter-durations-unit-in-key.ts b/packages/spec/src/migrations/entries/semantic/18.system-tracing-otel-exporter-durations-unit-in-key.ts index ebad002c86..6b6fb747f0 100644 --- a/packages/spec/src/migrations/entries/semantic/18.system-tracing-otel-exporter-durations-unit-in-key.ts +++ b/packages/spec/src/migrations/entries/semantic/18.system-tracing-otel-exporter-durations-unit-in-key.ts @@ -30,11 +30,14 @@ export const entry: SemanticMigration = { + 'to misread. The magnitudes make the guess plausible in both directions: 10000, 30000, ' + '5000 and 5000 are all defensible as seconds and as milliseconds, and an operator who ' + 'reads seconds sets an exporter deadline 1000x short. The suffix is the family spelling, ' - + 'counted in key position across packages/spec/src: 281 *Ms declarations over 42 distinct ' - + 'names, timeoutMs 65 of them and intervalMs 14, against 0 key-position timeoutSeconds; ' - + 'the Delay-plus-Ms pairing is likewise already attested (maxDelayMs, initialDelayMs, ' - + 'retryDelayMs, delayMs, debounceDelayMs) with no competing scheduledDelay spelling ' - + 'anywhere. Note this file is milliseconds throughout and its own landed precedent is ' + + 'counted in key position at 98bd7986fe over packages/spec/src *.ts (reproduce with ' + + 'git grep -hoE on that ref): 281 *Ms declarations over 42 distinct names, timeoutMs 65 of ' + + 'them and intervalMs 14, against 0 key-position timeoutSeconds and 77 *Seconds of any ' + + 'name; the Delay-plus-Ms pairing is likewise already attested on that same ref ' + + '(maxDelayMs 9, initialDelayMs 9, maxRetryDelayMs 5, debounceDelayMs 2, delayMs 2, ' + + 'retryDelayMs 1) with 0 occurrences of any competing exportTimeout, scheduledDelay or ' + + 'exportInterval spelling, suffixed or Seconds. Note this file is milliseconds throughout ' + + 'and its own landed precedent is ' + 'Span.duration to durationMs, the opposite of the sibling metrics card whose rows were ' + 'seconds. exporter.timeoutMs and exporter.batch.exportTimeoutMs are deliberately allowed ' + 'to sit one nesting level apart: the pair pre-exists the rename — the batch sub-object is ' @@ -52,17 +55,18 @@ export const entry: SemanticMigration = { + 'either, and a tracing configuration is never a stored sys_metadata row — so a conversion ' + 'would be a transform with no seam that ever runs. That is the same disposition ' + 'system-tracing-span-duration-unit-in-key recorded for the other key on this file. ' - + 'Measured on 98bd7986fe: NO in-repo reader exists outside packages/spec — ' + + 'Measured at 98bd7986fe: NO in-repo reader exists outside packages/spec — ' + 'OpenTelemetryCompatibility, TracingConfig and all three batch key names occur 0 times ' - + 'across the whole tree excluding packages/spec and the generated reference pages, against ' - + 'a lit control of 20875 Schema occurrences on the same corpus; inside packages/spec the ' + + 'across the whole tree at that ref excluding packages/spec and the generated reference ' + + 'pages, against a lit control of 20875 Schema occurrences on that same corpus and ref ' + + 'and a dark control of 0; inside packages/spec the ' + 'only occurrences are tracing.zod.ts, its test, and the generated rows in ' + 'content/docs/references/system/tracing.mdx, which this rename regenerates. And the ' + 'pinned objectui checkout — `.objectui-sha` = `53ded82bf7a494f54e344e19099dbf00854b8694` — names none of it: all 37 exports of ' - + 'tracing.zod.ts and each of the four key names occur 0 times across its 6409 tracked ' - + 'files (the 404 Span and 40 SpanSchema hits are objectui\'s own HTML text-span component, ' - + 'TextSpanSchema, an unrelated name), against lit controls objectstack 10171 and ' - + '@objectstack/spec 3479 on the same corpus.', + + 'tracing.zod.ts and each of the four key names occur 0 times across the 6409 files ' + + 'tracked at that sha (the 404 Span and 40 SpanSchema hits are objectui\'s own HTML ' + + 'text-span component, TextSpanSchema, an unrelated name), against lit controls ' + + 'objectstack 10171 and @objectstack/spec 3479 on that same corpus and sha.', acceptanceCriteria: 'Every author and reader of an OpenTelemetryCompatibility spells exporter.timeoutMs, ' + 'exporter.batch.exportTimeoutMs and exporter.batch.scheduledDelayMs, and every one of a ' diff --git a/packages/spec/src/migrations/registry.ts b/packages/spec/src/migrations/registry.ts index bf555fca83..d13b6af358 100644 --- a/packages/spec/src/migrations/registry.ts +++ b/packages/spec/src/migrations/registry.ts @@ -10572,11 +10572,14 @@ const step18: MigrationStep = { + 'to misread. The magnitudes make the guess plausible in both directions: 10000, 30000, ' + '5000 and 5000 are all defensible as seconds and as milliseconds, and an operator who ' + 'reads seconds sets an exporter deadline 1000x short. The suffix is the family spelling, ' - + 'counted in key position across packages/spec/src: 281 *Ms declarations over 42 distinct ' - + 'names, timeoutMs 65 of them and intervalMs 14, against 0 key-position timeoutSeconds; ' - + 'the Delay-plus-Ms pairing is likewise already attested (maxDelayMs, initialDelayMs, ' - + 'retryDelayMs, delayMs, debounceDelayMs) with no competing scheduledDelay spelling ' - + 'anywhere. Note this file is milliseconds throughout and its own landed precedent is ' + + 'counted in key position at 98bd7986fe over packages/spec/src *.ts (reproduce with ' + + 'git grep -hoE on that ref): 281 *Ms declarations over 42 distinct names, timeoutMs 65 of ' + + 'them and intervalMs 14, against 0 key-position timeoutSeconds and 77 *Seconds of any ' + + 'name; the Delay-plus-Ms pairing is likewise already attested on that same ref ' + + '(maxDelayMs 9, initialDelayMs 9, maxRetryDelayMs 5, debounceDelayMs 2, delayMs 2, ' + + 'retryDelayMs 1) with 0 occurrences of any competing exportTimeout, scheduledDelay or ' + + 'exportInterval spelling, suffixed or Seconds. Note this file is milliseconds throughout ' + + 'and its own landed precedent is ' + 'Span.duration to durationMs, the opposite of the sibling metrics card whose rows were ' + 'seconds. exporter.timeoutMs and exporter.batch.exportTimeoutMs are deliberately allowed ' + 'to sit one nesting level apart: the pair pre-exists the rename — the batch sub-object is ' @@ -10594,17 +10597,18 @@ const step18: MigrationStep = { + 'either, and a tracing configuration is never a stored sys_metadata row — so a conversion ' + 'would be a transform with no seam that ever runs. That is the same disposition ' + 'system-tracing-span-duration-unit-in-key recorded for the other key on this file. ' - + 'Measured on 98bd7986fe: NO in-repo reader exists outside packages/spec — ' + + 'Measured at 98bd7986fe: NO in-repo reader exists outside packages/spec — ' + 'OpenTelemetryCompatibility, TracingConfig and all three batch key names occur 0 times ' - + 'across the whole tree excluding packages/spec and the generated reference pages, against ' - + 'a lit control of 20875 Schema occurrences on the same corpus; inside packages/spec the ' + + 'across the whole tree at that ref excluding packages/spec and the generated reference ' + + 'pages, against a lit control of 20875 Schema occurrences on that same corpus and ref ' + + 'and a dark control of 0; inside packages/spec the ' + 'only occurrences are tracing.zod.ts, its test, and the generated rows in ' + 'content/docs/references/system/tracing.mdx, which this rename regenerates. And the ' + 'pinned objectui checkout — `.objectui-sha` = `53ded82bf7a494f54e344e19099dbf00854b8694` — names none of it: all 37 exports of ' - + 'tracing.zod.ts and each of the four key names occur 0 times across its 6409 tracked ' - + 'files (the 404 Span and 40 SpanSchema hits are objectui\'s own HTML text-span component, ' - + 'TextSpanSchema, an unrelated name), against lit controls objectstack 10171 and ' - + '@objectstack/spec 3479 on the same corpus.', + + 'tracing.zod.ts and each of the four key names occur 0 times across the 6409 files ' + + 'tracked at that sha (the 404 Span and 40 SpanSchema hits are objectui\'s own HTML ' + + 'text-span component, TextSpanSchema, an unrelated name), against lit controls ' + + 'objectstack 10171 and @objectstack/spec 3479 on that same corpus and sha.', acceptanceCriteria: 'Every author and reader of an OpenTelemetryCompatibility spells exporter.timeoutMs, ' + 'exporter.batch.exportTimeoutMs and exporter.batch.scheduledDelayMs, and every one of a ' @@ -13440,9 +13444,11 @@ export const RETIRED_KEYS_BY_MAJOR: Readonly> // #15939 ruling A (per-file remediation of #14478). `exporter.batch.scheduledDelay` // said "Scheduled delay in milliseconds" in a source JSDoc and carried NO // `.describe()` at all, so the published reference page showed a bare 5000. - // Renamed to `scheduledDelayMs`, the plain suffix: the Delay-plus-Ms pairing is - // already the family spelling on this tree (`maxDelayMs`, `initialDelayMs`, - // `retryDelayMs`, `delayMs`, `debounceDelayMs`) and no competing form exists. + // Renamed to `scheduledDelayMs`, the plain suffix: counted in key position across + // `packages/spec/src` at `98bd7986fe`, the Delay-plus-Ms pairing is already the + // family spelling (`maxDelayMs` 9, `initialDelayMs` 9, `maxRetryDelayMs` 5, + // `debounceDelayMs` 2, `delayMs` 2, `retryDelayMs` 1) and neither + // `scheduledDelayMs` nor `scheduledDelaySeconds` occurs there at all. // The value and the 5000 default are unchanged. Tombstoned with `retiredKey()`: // the nested `batch` object is not strict, so a bare deletion would silently // strip the key. No D2 conversion: not a stack collection member, not a stored @@ -13578,7 +13584,9 @@ export const RETIRED_KEYS_BY_MAJOR: Readonly> // carried NO `.describe()` at all, so the published reference page showed a bare // 5000. Renamed to `exportIntervalMs`; the value and the 5000 default are // unchanged. `intervalMs` is the family spelling already attested 14 times in key - // position on this tree, so the suffix lands on a name the surface uses. + // position across `packages/spec/src` at `98bd7986fe` (0 `exportIntervalMs` and 0 + // `exportIntervalSeconds` there, so nothing competes), so the suffix lands on a + // name the surface already uses. // Tombstoned with `retiredKey()`: the nested `performance` object is not strict, // so a bare deletion would silently strip the key and hand a background exporter // its default period. No D2 conversion: not a stack collection member, not a From 37f29a4a013b77af412405b0b7fe0b5b661e1d10 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 13 Sep 2026 13:57:25 +0000 Subject: [PATCH 4/6] docs(spec): reword the tracing entry's lit-control counts out of check:cli-command-ids MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `check-cli-command-ids` matches a bin name that is the FIRST thing inside a quoted run and is followed by one or two bare lowercase words. The count prose broke its string concatenation so that a line began `'objectstack 10171 and`, which that pair reads as a quoted CLI invocation resolving to no command path. Reworded, not dropped: both counts and the measurement sha stay, and the two control tokens are still named. The new spelling is out on either half of the rule alone — the token is neither first in its quoted run nor followed by a bare lowercase word — so a later reflow of the concatenation cannot reintroduce the match. The mirror in `migrations/registry.ts` is regenerated, not edited. Claude-Session: https://claude.ai/code/session_015c5G6TmpMKgnusmTpD7Ntt Co-authored-by: Claude --- .../18.system-tracing-otel-exporter-durations-unit-in-key.ts | 5 +++-- packages/spec/src/migrations/registry.ts | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/spec/src/migrations/entries/semantic/18.system-tracing-otel-exporter-durations-unit-in-key.ts b/packages/spec/src/migrations/entries/semantic/18.system-tracing-otel-exporter-durations-unit-in-key.ts index 6b6fb747f0..1b4c988cfd 100644 --- a/packages/spec/src/migrations/entries/semantic/18.system-tracing-otel-exporter-durations-unit-in-key.ts +++ b/packages/spec/src/migrations/entries/semantic/18.system-tracing-otel-exporter-durations-unit-in-key.ts @@ -65,8 +65,9 @@ export const entry: SemanticMigration = { + 'pinned objectui checkout — `.objectui-sha` = `53ded82bf7a494f54e344e19099dbf00854b8694` — names none of it: all 37 exports of ' + 'tracing.zod.ts and each of the four key names occur 0 times across the 6409 files ' + 'tracked at that sha (the 404 Span and 40 SpanSchema hits are objectui\'s own HTML ' - + 'text-span component, TextSpanSchema, an unrelated name), against lit controls ' - + 'objectstack 10171 and @objectstack/spec 3479 on that same corpus and sha.', + + 'text-span component, TextSpanSchema, an unrelated name), against two lit controls on ' + + 'that same corpus and sha: 10171 hits for the bare token objectstack, and 3479 for the ' + + 'package specifier @objectstack/spec.', acceptanceCriteria: 'Every author and reader of an OpenTelemetryCompatibility spells exporter.timeoutMs, ' + 'exporter.batch.exportTimeoutMs and exporter.batch.scheduledDelayMs, and every one of a ' diff --git a/packages/spec/src/migrations/registry.ts b/packages/spec/src/migrations/registry.ts index d13b6af358..1674ff68cd 100644 --- a/packages/spec/src/migrations/registry.ts +++ b/packages/spec/src/migrations/registry.ts @@ -10607,8 +10607,9 @@ const step18: MigrationStep = { + 'pinned objectui checkout — `.objectui-sha` = `53ded82bf7a494f54e344e19099dbf00854b8694` — names none of it: all 37 exports of ' + 'tracing.zod.ts and each of the four key names occur 0 times across the 6409 files ' + 'tracked at that sha (the 404 Span and 40 SpanSchema hits are objectui\'s own HTML ' - + 'text-span component, TextSpanSchema, an unrelated name), against lit controls ' - + 'objectstack 10171 and @objectstack/spec 3479 on that same corpus and sha.', + + 'text-span component, TextSpanSchema, an unrelated name), against two lit controls on ' + + 'that same corpus and sha: 10171 hits for the bare token objectstack, and 3479 for the ' + + 'package specifier @objectstack/spec.', acceptanceCriteria: 'Every author and reader of an OpenTelemetryCompatibility spells exporter.timeoutMs, ' + 'exporter.batch.exportTimeoutMs and exporter.batch.scheduledDelayMs, and every one of a ' From efbf3005086c21958957f8da3cc5ecd600fedc6a Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 13 Sep 2026 14:14:43 +0000 Subject: [PATCH 5/6] docs(spec): correct the tracing entry's lit control and name the released package version Two corrections from the in-seat contract review of PR #18016. The ADR-0087 entry's lit control said 20875 Schema occurrences on a corpus it described as the tree excluding `packages/spec` AND the generated reference pages. 20875 is the count with `content/docs/references` still in; the corpus the sentence names reads 18920. Both numbers now come from one `git grep -o` over `98bd7986fe` carrying both pathspec exclusions, and the sentence names that corpus exactly. The four tombstone prescriptions said `@objectstack/spec 18`. That conflates two axes: the prose names the released PACKAGE version, which is 17 (this ships in 17.5.0), while `RETIRED_KEYS_BY_MAJOR[18]` names the PROTOCOL major. A reader told "18" would look in a v18 that does not carry it. The tree spells it 17 in 265 places against 14, and this file's own `Span.duration` precedent says 17. `registry.ts` and the reference page are regenerated, not edited. Claude-Session: https://claude.ai/code/session_015c5G6TmpMKgnusmTpD7Ntt Co-authored-by: Claude --- content/docs/references/system/tracing.mdx | 4 ++-- ....system-tracing-otel-exporter-durations-unit-in-key.ts | 7 ++++--- packages/spec/src/migrations/registry.ts | 7 ++++--- packages/spec/src/system/tracing.zod.ts | 8 ++++---- 4 files changed, 14 insertions(+), 12 deletions(-) diff --git a/content/docs/references/system/tracing.mdx b/content/docs/references/system/tracing.mdx index f082b457df..24b2b5572a 100644 --- a/content/docs/references/system/tracing.mdx +++ b/content/docs/references/system/tracing.mdx @@ -53,7 +53,7 @@ OpenTelemetry compatibility configuration | **protocol** | `string` | optional | Protocol version | | **headers** | `Record` | optional | HTTP headers | | **timeoutMs** | `integer` | optional (default: `10000`) | Exporter request timeout in milliseconds | -| **timeout** | `never` | optional | [REMOVED] `OpenTelemetryCompatibility.exporter.timeout` was renamed to `timeoutMs` in @objectstack/spec 18 — the unit of a duration-shaped number lives in the key name, not only in the describe prose. Its unit (milliseconds) lived in a source JSDoc only and the key carried no describe at all, so the reference-page reader got a bare 10000 and could not tell it from 10000 seconds. Rename the key to `timeoutMs`; the value (milliseconds) and the 10000 default are unchanged. | +| **timeout** | `never` | optional | [REMOVED] `OpenTelemetryCompatibility.exporter.timeout` was renamed to `timeoutMs` in @objectstack/spec 17 — the unit of a duration-shaped number lives in the key name, not only in the describe prose. Its unit (milliseconds) lived in a source JSDoc only and the key carried no describe at all, so the reference-page reader got a bare 10000 and could not tell it from 10000 seconds. Rename the key to `timeoutMs`; the value (milliseconds) and the 10000 default are unchanged. | | **compression** | `Enum<'none' \| 'gzip'>` | optional (default: `"none"`) | | | **batch** | `{ maxBatchSize: integer; maxQueueSize: integer; exportTimeoutMs: integer; scheduledDelayMs: integer }` | optional | | @@ -498,7 +498,7 @@ Tracing configuration | :--- | :--- | :--- | :--- | | **asyncExport** | `boolean` | optional (default: `true`) | | | **exportIntervalMs** | `integer` | optional (default: `5000`) | Background span-export interval in milliseconds | -| **exportInterval** | `never` | optional | [REMOVED] `TracingConfig.performance.exportInterval` was renamed to `exportIntervalMs` in @objectstack/spec 18 — the unit of a duration-shaped number lives in the key name, not only in the describe prose. Its unit (milliseconds) lived in a source JSDoc only and the key carried no describe at all, so the reference-page reader got a bare 5000. Rename the key to `exportIntervalMs`; the value (milliseconds) and the 5000 default are unchanged. | +| **exportInterval** | `never` | optional | [REMOVED] `TracingConfig.performance.exportInterval` was renamed to `exportIntervalMs` in @objectstack/spec 17 — the unit of a duration-shaped number lives in the key name, not only in the describe prose. Its unit (milliseconds) lived in a source JSDoc only and the key carried no describe at all, so the reference-page reader got a bare 5000. Rename the key to `exportIntervalMs`; the value (milliseconds) and the 5000 default are unchanged. | --- diff --git a/packages/spec/src/migrations/entries/semantic/18.system-tracing-otel-exporter-durations-unit-in-key.ts b/packages/spec/src/migrations/entries/semantic/18.system-tracing-otel-exporter-durations-unit-in-key.ts index 1b4c988cfd..4f65e36916 100644 --- a/packages/spec/src/migrations/entries/semantic/18.system-tracing-otel-exporter-durations-unit-in-key.ts +++ b/packages/spec/src/migrations/entries/semantic/18.system-tracing-otel-exporter-durations-unit-in-key.ts @@ -57,9 +57,10 @@ export const entry: SemanticMigration = { + 'system-tracing-span-duration-unit-in-key recorded for the other key on this file. ' + 'Measured at 98bd7986fe: NO in-repo reader exists outside packages/spec — ' + 'OpenTelemetryCompatibility, TracingConfig and all three batch key names occur 0 times ' - + 'across the whole tree at that ref excluding packages/spec and the generated reference ' - + 'pages, against a lit control of 20875 Schema occurrences on that same corpus and ref ' - + 'and a dark control of 0; inside packages/spec the ' + + 'across the whole tree at that ref excluding packages/spec and content/docs/references, ' + + 'against a lit control of 18920 Schema occurrences on exactly that corpus and ref — both ' + + 'counts from one git grep -o over 98bd7986fe with those two pathspec exclusions — and a ' + + 'dark control of 0; inside packages/spec the ' + 'only occurrences are tracing.zod.ts, its test, and the generated rows in ' + 'content/docs/references/system/tracing.mdx, which this rename regenerates. And the ' + 'pinned objectui checkout — `.objectui-sha` = `53ded82bf7a494f54e344e19099dbf00854b8694` — names none of it: all 37 exports of ' diff --git a/packages/spec/src/migrations/registry.ts b/packages/spec/src/migrations/registry.ts index 1674ff68cd..9b916ca4d7 100644 --- a/packages/spec/src/migrations/registry.ts +++ b/packages/spec/src/migrations/registry.ts @@ -10599,9 +10599,10 @@ const step18: MigrationStep = { + 'system-tracing-span-duration-unit-in-key recorded for the other key on this file. ' + 'Measured at 98bd7986fe: NO in-repo reader exists outside packages/spec — ' + 'OpenTelemetryCompatibility, TracingConfig and all three batch key names occur 0 times ' - + 'across the whole tree at that ref excluding packages/spec and the generated reference ' - + 'pages, against a lit control of 20875 Schema occurrences on that same corpus and ref ' - + 'and a dark control of 0; inside packages/spec the ' + + 'across the whole tree at that ref excluding packages/spec and content/docs/references, ' + + 'against a lit control of 18920 Schema occurrences on exactly that corpus and ref — both ' + + 'counts from one git grep -o over 98bd7986fe with those two pathspec exclusions — and a ' + + 'dark control of 0; inside packages/spec the ' + 'only occurrences are tracing.zod.ts, its test, and the generated rows in ' + 'content/docs/references/system/tracing.mdx, which this rename regenerates. And the ' + 'pinned objectui checkout — `.objectui-sha` = `53ded82bf7a494f54e344e19099dbf00854b8694` — names none of it: all 37 exports of ' diff --git a/packages/spec/src/system/tracing.zod.ts b/packages/spec/src/system/tracing.zod.ts index 81c8fb8bcd..632a43965e 100644 --- a/packages/spec/src/system/tracing.zod.ts +++ b/packages/spec/src/system/tracing.zod.ts @@ -549,7 +549,7 @@ export const OpenTelemetryCompatibilitySchema = lazySchema(() => z.object({ /** Tombstone for the rename above (#17785, ruling A on #15939). */ timeout: retiredKey( '`OpenTelemetryCompatibility.exporter.timeout` was renamed to `timeoutMs` in ' - + '@objectstack/spec 18 — the unit of a duration-shaped number lives in the key name, ' + + '@objectstack/spec 17 — the unit of a duration-shaped number lives in the key name, ' + 'not only in the describe prose. Its unit (milliseconds) lived in a source JSDoc ' + 'only and the key carried no describe at all, so the reference-page reader got a ' + 'bare 10000 and could not tell it from 10000 seconds. Rename the key to `timeoutMs`; ' @@ -600,7 +600,7 @@ export const OpenTelemetryCompatibilitySchema = lazySchema(() => z.object({ /** Tombstones for the two renames above (#17785, ruling A on #15939). */ exportTimeout: retiredKey( '`OpenTelemetryCompatibility.exporter.batch.exportTimeout` was renamed to ' - + '`exportTimeoutMs` in @objectstack/spec 18 — the unit of a duration-shaped number ' + + '`exportTimeoutMs` in @objectstack/spec 17 — the unit of a duration-shaped number ' + 'lives in the key name, not only in the describe prose. Its unit (milliseconds) ' + 'lived in a source JSDoc only and the key carried no describe at all, so the ' + 'reference-page reader got a bare 30000. Rename the key to `exportTimeoutMs`; the ' @@ -608,7 +608,7 @@ export const OpenTelemetryCompatibilitySchema = lazySchema(() => z.object({ ), scheduledDelay: retiredKey( '`OpenTelemetryCompatibility.exporter.batch.scheduledDelay` was renamed to ' - + '`scheduledDelayMs` in @objectstack/spec 18 — the unit of a duration-shaped number ' + + '`scheduledDelayMs` in @objectstack/spec 17 — the unit of a duration-shaped number ' + 'lives in the key name, not only in the describe prose. Its unit (milliseconds) ' + 'lived in a source JSDoc only and the key carried no describe at all, so the ' + 'reference-page reader got a bare 5000. Rename the key to `scheduledDelayMs`; the ' @@ -777,7 +777,7 @@ export const TracingConfigSchema = lazySchema(() => z.object({ /** Tombstone for the rename above (#17785, ruling A on #15939). */ exportInterval: retiredKey( '`TracingConfig.performance.exportInterval` was renamed to `exportIntervalMs` in ' - + '@objectstack/spec 18 — the unit of a duration-shaped number lives in the key name, ' + + '@objectstack/spec 17 — the unit of a duration-shaped number lives in the key name, ' + 'not only in the describe prose. Its unit (milliseconds) lived in a source JSDoc ' + 'only and the key carried no describe at all, so the reference-page reader got a ' + 'bare 5000. Rename the key to `exportIntervalMs`; the value (milliseconds) and the ' From 1de92aba380312d8f83d0b16b384249fbb6776ca Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 13 Sep 2026 15:21:49 +0000 Subject: [PATCH 6/6] chore(spec): regenerate migrations/registry.ts after merging origin/main MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `registry.ts` is generated from `src/migrations/entries/` and is deliberately NOT_DRIVER_MANAGED, so it conflicts textually on every pair of parallel registrations — #18007 (#17783, metrics) landed 9 rows and 4 semantic entries while this branch carried 5 of its own. The entries directory itself merged clean at 579 rows with zero conflict markers, which is what that directory exists for, so the conflict is resolved by regeneration, never by hand. Set difference over the entry set, by exact path: 0 lost / 13 gained against this branch's pre-merge head, 0 lost / 5 gained against origin/main. The generator's own line reads 215 semantic, 186 retired-key, 178 retired-def. Claude-Session: https://claude.ai/code/session_015c5G6TmpMKgnusmTpD7Ntt Co-authored-by: Claude --- packages/spec/src/migrations/registry.ts | 128 +++++++++++++++++++++++ 1 file changed, 128 insertions(+) diff --git a/packages/spec/src/migrations/registry.ts b/packages/spec/src/migrations/registry.ts index c7a7d1be24..c0dfb672c5 100644 --- a/packages/spec/src/migrations/registry.ts +++ b/packages/spec/src/migrations/registry.ts @@ -10845,6 +10845,85 @@ const step18: MigrationStep = { + 'one to check by hand rather than by search-and-replace: after the migration a reader ' + 'can tell at the authoring site that 300 and 30000 are not the same kind of number.', }, + { + id: 'system-tracing-otel-exporter-durations-unit-in-key', + // No backticks in `surface` — build-upgrade-guide.ts renders it inside a + // code span AND a table cell. + surface: 'the four tracing-configuration durations whose unit lived in a source JSDoc only: ' + + 'OpenTelemetryCompatibility.exporter.timeout, ' + + 'OpenTelemetryCompatibility.exporter.batch.exportTimeout, ' + + 'OpenTelemetryCompatibility.exporter.batch.scheduledDelay and ' + + 'TracingConfig.performance.exportInterval (system/tracing.zod.ts)', + replacement: 'timeoutMs, exportTimeoutMs, scheduledDelayMs and exportIntervalMs — rename each ' + + 'key; all four values (milliseconds) and their 10000 / 30000 / 5000 / 5000 defaults are ' + + 'unchanged', + reason: + 'Director-seat ruling A on #15939, 2026-09-11, carrying the maintainer\'s 「同意」 (decision ' + + 'batch #115), executing the #14478 rule per file. It follows ' + + 'system-tracing-span-duration-unit-in-key on this same file and does not amend it: that ' + + 'entry retired Span.duration under ruling B, whose population was the describe channel, ' + + 'and these four keys were never in it — they are the JSDoc-only channel #15939 opened, ' + + 'which is why one file carries two rounds. Each key named milliseconds in its JSDoc — ' + + '"Timeout in milliseconds", "Export timeout in milliseconds", "Scheduled delay in ' + + 'milliseconds", "Background export interval in milliseconds" — and the JSDoc above a key ' + + 'is NOT what content/docs/references/** renders; .describe() is. Measured on this tree: ' + + 'all four carried NO .describe() at all, so the published reference row for each was a ' + + 'bare integer with no unit anywhere on the page — a strictly worse channel than the ' + + 'unit-in-prose shape #14478 already refuses, since here the reference reader had no prose ' + + 'to misread. The magnitudes make the guess plausible in both directions: 10000, 30000, ' + + '5000 and 5000 are all defensible as seconds and as milliseconds, and an operator who ' + + 'reads seconds sets an exporter deadline 1000x short. The suffix is the family spelling, ' + + 'counted in key position at 98bd7986fe over packages/spec/src *.ts (reproduce with ' + + 'git grep -hoE on that ref): 281 *Ms declarations over 42 distinct names, timeoutMs 65 of ' + + 'them and intervalMs 14, against 0 key-position timeoutSeconds and 77 *Seconds of any ' + + 'name; the Delay-plus-Ms pairing is likewise already attested on that same ref ' + + '(maxDelayMs 9, initialDelayMs 9, maxRetryDelayMs 5, debounceDelayMs 2, delayMs 2, ' + + 'retryDelayMs 1) with 0 occurrences of any competing exportTimeout, scheduledDelay or ' + + 'exportInterval spelling, suffixed or Seconds. Note this file is milliseconds throughout ' + + 'and its own landed precedent is ' + + 'Span.duration to durationMs, the opposite of the sibling metrics card whose rows were ' + + 'seconds. exporter.timeoutMs and exporter.batch.exportTimeoutMs are deliberately allowed ' + + 'to sit one nesting level apart: the pair pre-exists the rename — the batch sub-object is ' + + 'the OpenTelemetry batch span processor\'s own four knobs (max batch size, max queue ' + + 'size, scheduled delay, export timeout) beside the exporter\'s own request deadline — so ' + + 'renaming either to something more distinctive would depart from the vocabulary the shape ' + + 'mirrors, and the nesting already disambiguates every read point ' + + '(exporter.timeoutMs vs exporter.batch.exportTimeoutMs). All four old spellings are ' + + 'retiredKey() tombstones: neither OpenTelemetryCompatibilitySchema nor TracingConfigSchema ' + + 'nor any object nested inside them is .strict(), so a bare deletion would be a SILENT ' + + 'STRIP (#3733, ADR-0104) — and the stripped value lands on an export deadline and a ' + + 'background export period. Why a semantic entry and not a D2 conversion: the conversion ' + + 'chain walks a normalized STACK, and neither def is an authorable surface — stack.zod.ts ' + + 'declares no tracing collection, no metadata-type binding or manifest embed carries ' + + 'either, and a tracing configuration is never a stored sys_metadata row — so a conversion ' + + 'would be a transform with no seam that ever runs. That is the same disposition ' + + 'system-tracing-span-duration-unit-in-key recorded for the other key on this file. ' + + 'Measured at 98bd7986fe: NO in-repo reader exists outside packages/spec — ' + + 'OpenTelemetryCompatibility, TracingConfig and all three batch key names occur 0 times ' + + 'across the whole tree at that ref excluding packages/spec and content/docs/references, ' + + 'against a lit control of 18920 Schema occurrences on exactly that corpus and ref — both ' + + 'counts from one git grep -o over 98bd7986fe with those two pathspec exclusions — and a ' + + 'dark control of 0; inside packages/spec the ' + + 'only occurrences are tracing.zod.ts, its test, and the generated rows in ' + + 'content/docs/references/system/tracing.mdx, which this rename regenerates. And the ' + + 'pinned objectui checkout — `.objectui-sha` = `53ded82bf7a494f54e344e19099dbf00854b8694` — names none of it: all 37 exports of ' + + 'tracing.zod.ts and each of the four key names occur 0 times across the 6409 files ' + + 'tracked at that sha (the 404 Span and 40 SpanSchema hits are objectui\'s own HTML ' + + 'text-span component, TextSpanSchema, an unrelated name), against two lit controls on ' + + 'that same corpus and sha: 10171 hits for the bare token objectstack, and 3479 for the ' + + 'package specifier @objectstack/spec.', + acceptanceCriteria: + 'Every author and reader of an OpenTelemetryCompatibility spells exporter.timeoutMs, ' + + 'exporter.batch.exportTimeoutMs and exporter.batch.scheduledDelayMs, and every one of a ' + + 'TracingConfig spells performance.exportIntervalMs. Authoring any old spelling fails to ' + + 'compile (input type `never`) and fails to parse with the rename prescription naming the ' + + 'suffixed key — not with a generic unrecognized_keys issue, which these non-strict shapes ' + + 'could never have raised anyway. Behaviour is unchanged: the same milliseconds, the same ' + + '10000 / 30000 / 5000 / 5000 defaults and the same int().positive() bounds, and all four ' + + 'published describes now name milliseconds where before there was no describe at all. ' + + 'The authorable-surface and authorable-defaults ledgers move nothing: every one of the ' + + 'four is NESTED, and those artifacts record top-level keys per def only.', + }, { id: 'system-tracing-span-duration-unit-in-key', surface: 'Span.duration, the emitted trace-span length whose name carried no unit ' @@ -13770,6 +13849,42 @@ export const RETIRED_KEYS_BY_MAJOR: Readonly> // stored metadata row. // See `system-metrics-jsdoc-durations-unit-in-key`. 'system/MetricsConfig:retention.period', + // #15939 ruling A (per-file remediation of #14478). `exporter.batch.exportTimeout` + // said "Export timeout in milliseconds" in a source JSDoc and carried NO + // `.describe()` at all, so the published reference page showed a bare 30000. + // Renamed to `exportTimeoutMs`; the value and the 30000 default are unchanged. + // Tombstoned with `retiredKey()`: the nested `batch` object is not strict, so a + // bare deletion would silently strip the key. No D2 conversion: not a stack + // collection member, not a stored row. See + // `system-tracing-otel-exporter-durations-unit-in-key`. + 'system/OpenTelemetryCompatibility:exporter.batch.exportTimeout', + // #15939 ruling A (per-file remediation of #14478). `exporter.batch.scheduledDelay` + // said "Scheduled delay in milliseconds" in a source JSDoc and carried NO + // `.describe()` at all, so the published reference page showed a bare 5000. + // Renamed to `scheduledDelayMs`, the plain suffix: counted in key position across + // `packages/spec/src` at `98bd7986fe`, the Delay-plus-Ms pairing is already the + // family spelling (`maxDelayMs` 9, `initialDelayMs` 9, `maxRetryDelayMs` 5, + // `debounceDelayMs` 2, `delayMs` 2, `retryDelayMs` 1) and neither + // `scheduledDelayMs` nor `scheduledDelaySeconds` occurs there at all. + // The value and the 5000 default are unchanged. Tombstoned with `retiredKey()`: + // the nested `batch` object is not strict, so a bare deletion would silently + // strip the key. No D2 conversion: not a stack collection member, not a stored + // row. See `system-tracing-otel-exporter-durations-unit-in-key`. + 'system/OpenTelemetryCompatibility:exporter.batch.scheduledDelay', + // #15939 ruling A (per-file remediation of #14478). `exporter.timeout` said + // "Timeout in milliseconds" in a source JSDoc and carried NO `.describe()` at + // all, so the `content/docs/references/**` page published a bare 10000 with no + // unit anywhere on it. Renamed to `timeoutMs`; the value and the 10000 default + // are unchanged. It keeps the plain suffix even though its `exporter.batch` + // sibling becomes `exportTimeoutMs`: the two are the OpenTelemetry exporter's + // own request deadline and the batch processor's export deadline, two distinct + // knobs the shape already spelled apart, and the nesting keeps every read point + // unambiguous. Tombstoned with `retiredKey()`: the nested `exporter` object is + // not strict, so a bare deletion would silently strip the key and let a 10000 ms + // default land on an exporter deadline the operator had deliberately changed. + // No D2 conversion: not a stack collection member, not a stored row. See + // `system-tracing-otel-exporter-durations-unit-in-key`. + 'system/OpenTelemetryCompatibility:exporter.timeout', // #15679 (stack card 4/6 of #14478) — ruling B. `QueueConfig.rateLimit.duration` // said "Duration in milliseconds" in prose and nothing else — while // `TaskResult.durationMs`, ninety lines earlier in the SAME file, already spelled @@ -13909,6 +14024,19 @@ export const RETIRED_KEYS_BY_MAJOR: Readonly> // connection is host configuration, not a stored metadata row. // See `system-object-storage-durations-unit-in-key`. 'system/StorageConnection:timeout', + // #15939 ruling A (per-file remediation of #14478). `performance.exportInterval` + // said "Background export interval in milliseconds" in a source JSDoc and + // carried NO `.describe()` at all, so the published reference page showed a bare + // 5000. Renamed to `exportIntervalMs`; the value and the 5000 default are + // unchanged. `intervalMs` is the family spelling already attested 14 times in key + // position across `packages/spec/src` at `98bd7986fe` (0 `exportIntervalMs` and 0 + // `exportIntervalSeconds` there, so nothing competes), so the suffix lands on a + // name the surface already uses. + // Tombstoned with `retiredKey()`: the nested `performance` object is not strict, + // so a bare deletion would silently strip the key and hand a background exporter + // its default period. No D2 conversion: not a stack collection member, not a + // stored row. See `system-tracing-otel-exporter-durations-unit-in-key`. + 'system/TracingConfig:performance.exportInterval', // #14477 — ADR-0049 enforce-or-remove (maintainer ruling 2026-09-02, ruled A: // retire per family). One of the hour/minute/day-shaped deadline keys of the // incident-response / training / change-management families: declared on the