From f3e0d6f039901fa315c9c467a3593ec7c275c238 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 13 Sep 2026 10:05:59 +0000 Subject: [PATCH 1/2] wip(spec): rename the four logging duration keys with their unit token Co-authored-by: Claude Claude-Session: https://claude.ai/code/session_015c5G6TmpMKgnusmTpD7Ntt --- packages/spec/src/system/logging.test.ts | 161 ++++++++++++++++++++++- packages/spec/src/system/logging.zod.ts | 103 +++++++++++++-- 2 files changed, 252 insertions(+), 12 deletions(-) diff --git a/packages/spec/src/system/logging.test.ts b/packages/spec/src/system/logging.test.ts index 5397a46fb1f..3a54c8fe27d 100644 --- a/packages/spec/src/system/logging.test.ts +++ b/packages/spec/src/system/logging.test.ts @@ -197,7 +197,10 @@ describe('HttpDestinationConfigSchema', () => { expect(config.url).toBe('https://logs.example.com/v1/logs'); expect(config.method).toBe('POST'); - expect(config.timeout).toBe(30000); + // `timeout` → `timeoutMs` (#17782, ruling A on #15939 executing #14478). + // The 30000 default this pins is unchanged; only the key it is read under + // moved, so the pin follows the key rather than being dropped. + expect(config.timeoutMs).toBe(30000); }); it('should accept authentication', () => { @@ -218,12 +221,14 @@ describe('HttpDestinationConfigSchema', () => { url: 'https://logs.example.com/v1/logs', batch: { maxSize: 500, - flushInterval: 10000, + // `batch.flushInterval` → `batch.flushIntervalMs` (#17782, ruling A on + // #15939 executing #14478). Same 10000 milliseconds, new key. + flushIntervalMs: 10000, }, }); expect(config.batch?.maxSize).toBe(500); - expect(config.batch?.flushInterval).toBe(10000); + expect(config.batch?.flushIntervalMs).toBe(10000); }); }); @@ -468,7 +473,9 @@ describe('LoggingConfigSchema', () => { buffer: { enabled: true, size: 5000, - flushInterval: 2000, + // `buffer.flushInterval` → `buffer.flushIntervalMs` (#17782, ruling A + // on #15939 executing #14478). Same 2000 milliseconds, new key. + flushIntervalMs: 2000, }, }; @@ -492,3 +499,149 @@ describe('LoggingConfigSchema', () => { })).toThrow(); }); }); + +// #15939 ruling A (executing #14478) — the four duration keys on this file that +// named milliseconds in a source JSDoc and nowhere an author or a reference-page +// reader could see it: `.describe()` was absent on all four, so +// `content/docs/references/system/logging.mdx` published a bare number. Each is +// renamed with the unit in the key and the old spelling left as a `retiredKey` +// tombstone — none of the four enclosing objects is `.strict()`, so a bare +// deletion would have stripped the value in silence. +// +// `flushInterval` was declared TWICE on this file, in two different defs with +// two different defaults, so each def is pinned separately below: they are +// different keys and each carries its own prescription. +describe('logging duration keys → *Ms (#17782, #15939, #14478)', () => { + describe('HttpDestinationConfig.batch.flushInterval → flushIntervalMs', () => { + it('REFUSES the retired spelling with a rename naming `flushIntervalMs`', () => { + const result = HttpDestinationConfigSchema.safeParse({ + url: 'https://logs.example.com/v1/logs', + batch: { flushInterval: 5000 }, + }); + expect(result.success).toBe(false); + const issue = result.error!.issues.find((i) => i.path.join('.') === 'batch.flushInterval'); + expect(issue).toBeDefined(); + expect(issue!.message).toMatch( + /`HttpDestinationConfig\.batch\.flushInterval` was renamed.*Rename the key to `flushIntervalMs`/s, + ); + }); + + it('accepts the suffixed key at the magnitude the retired key carried, with the same default', () => { + const parsed = HttpDestinationConfigSchema.parse({ + url: 'https://logs.example.com/v1/logs', + batch: { flushIntervalMs: 10000 }, + }); + expect(parsed.batch?.flushIntervalMs).toBe(10000); + expect(parsed.batch).not.toHaveProperty('flushInterval'); + expect( + HttpDestinationConfigSchema.parse({ + url: 'https://logs.example.com/v1/logs', + batch: {}, + }).batch?.flushIntervalMs, + ).toBe(5000); + }); + + it('publishes the unit in the describe — the text the reference pages render', () => { + const batch = HttpDestinationConfigSchema.shape.batch.unwrap(); + expect(batch.shape.flushIntervalMs.description).toBe('Flush interval in milliseconds'); + }); + }); + + describe('HttpDestinationConfig.retry.initialDelay → initialDelayMs', () => { + it('REFUSES the retired spelling with a rename naming `initialDelayMs`', () => { + const result = HttpDestinationConfigSchema.safeParse({ + url: 'https://logs.example.com/v1/logs', + retry: { initialDelay: 1000 }, + }); + expect(result.success).toBe(false); + const issue = result.error!.issues.find((i) => i.path.join('.') === 'retry.initialDelay'); + expect(issue).toBeDefined(); + expect(issue!.message).toMatch( + /`HttpDestinationConfig\.retry\.initialDelay` was renamed.*Rename the key to `initialDelayMs`/s, + ); + }); + + it('accepts the suffixed key at the magnitude the retired key carried, with the same default', () => { + const parsed = HttpDestinationConfigSchema.parse({ + url: 'https://logs.example.com/v1/logs', + retry: { initialDelayMs: 2500 }, + }); + expect(parsed.retry?.initialDelayMs).toBe(2500); + expect(parsed.retry).not.toHaveProperty('initialDelay'); + expect( + HttpDestinationConfigSchema.parse({ + url: 'https://logs.example.com/v1/logs', + retry: {}, + }).retry?.initialDelayMs, + ).toBe(1000); + }); + + it('publishes the unit in the describe — the text the reference pages render', () => { + const retry = HttpDestinationConfigSchema.shape.retry.unwrap(); + expect(retry.shape.initialDelayMs.description).toBe('Initial retry delay in milliseconds'); + }); + }); + + describe('HttpDestinationConfig.timeout → timeoutMs (the one TOP-LEVEL key of the four)', () => { + it('REFUSES the retired spelling with a rename naming `timeoutMs`', () => { + const result = HttpDestinationConfigSchema.safeParse({ + url: 'https://logs.example.com/v1/logs', + timeout: 30000, + }); + expect(result.success).toBe(false); + const issue = result.error!.issues.find((i) => i.path.join('.') === 'timeout'); + expect(issue).toBeDefined(); + expect(issue!.message).toMatch( + /`HttpDestinationConfig\.timeout` was renamed.*Rename the key to `timeoutMs`/s, + ); + }); + + it('accepts the suffixed key at the magnitude the retired key carried, with the same default', () => { + const parsed = HttpDestinationConfigSchema.parse({ + url: 'https://logs.example.com/v1/logs', + timeoutMs: 45000, + }); + expect(parsed.timeoutMs).toBe(45000); + expect(parsed).not.toHaveProperty('timeout'); + expect( + HttpDestinationConfigSchema.parse({ url: 'https://logs.example.com/v1/logs' }).timeoutMs, + ).toBe(30000); + }); + + it('publishes the unit in the describe — the text the reference pages render', () => { + expect(HttpDestinationConfigSchema.shape.timeoutMs.description) + .toBe('Timeout in milliseconds'); + }); + }); + + describe('LoggingConfig.buffer.flushInterval → flushIntervalMs (a different key from the batch one)', () => { + const base = { name: 'app_logging', label: 'App logging', destinations: [] }; + + it('REFUSES the retired spelling with a rename naming `flushIntervalMs`', () => { + const result = LoggingConfigSchema.safeParse({ + ...base, + buffer: { flushInterval: 1000 }, + }); + expect(result.success).toBe(false); + const issue = result.error!.issues.find((i) => i.path.join('.') === 'buffer.flushInterval'); + expect(issue).toBeDefined(); + expect(issue!.message).toMatch( + /`LoggingConfig\.buffer\.flushInterval` was renamed.*Rename the key to `flushIntervalMs`/s, + ); + }); + + it('accepts the suffixed key at the magnitude the retired key carried, with its OWN default', () => { + const parsed = LoggingConfigSchema.parse({ ...base, buffer: { flushIntervalMs: 2000 } }); + expect(parsed.buffer?.flushIntervalMs).toBe(2000); + expect(parsed.buffer).not.toHaveProperty('flushInterval'); + // 1000 here, 5000 on `HttpDestinationConfig.batch` — the two same-named + // keys never shared a default and do not share one now. + expect(LoggingConfigSchema.parse({ ...base, buffer: {} }).buffer?.flushIntervalMs).toBe(1000); + }); + + it('publishes the unit in the describe — the text the reference pages render', () => { + const buffer = LoggingConfigSchema.shape.buffer.unwrap(); + expect(buffer.shape.flushIntervalMs.description).toBe('Flush interval in milliseconds'); + }); + }); +}); diff --git a/packages/spec/src/system/logging.zod.ts b/packages/spec/src/system/logging.zod.ts index 0c0b949ebd1..9768a4bb599 100644 --- a/packages/spec/src/system/logging.zod.ts +++ b/packages/spec/src/system/logging.zod.ts @@ -23,6 +23,7 @@ import { z } from 'zod'; * Standard RFC 5424 severity levels (simplified) */ import { lazySchema } from '../shared/lazy-schema'; +import { retiredKey } from '../shared/retired-key'; export const LogLevel = z.enum([ 'debug', 'info', @@ -232,6 +233,43 @@ export type FileDestinationConfig = z.input; /** Post-parse shape of {@link FileDestinationConfig} — defaults applied, transforms run (ADR-0122). */ export type FileDestinationConfigParsed = z.infer; +/** + * Prescriptions for the three `HttpDestinationConfig` durations renamed in 17 + * (#17782, ruling A on #15939 executing #14478). + * + * They carry NO `os migrate meta --from 17` sentence, because no ADR-0087 D2 + * conversion covers them: `stack.zod.ts` declares no logging collection and + * neither `HttpDestinationConfigSchema` nor `LoggingConfigSchema` is referenced + * anywhere in `packages/spec/src` outside this file, so no rehydration seam + * replays a conversion over an authored logging document. Naming the command + * would promise an affordance that cannot apply. + */ +const HTTP_BATCH_FLUSH_INTERVAL_RETIRED = + '`HttpDestinationConfig.batch.flushInterval` was renamed to `flushIntervalMs` ' + + '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 published a bare 5000. Rename the key to `flushIntervalMs`; ' + + 'the value (milliseconds) and the 5000 default are unchanged. This is the ' + + 'batch flush on an HTTP log destination — `LoggingConfig.buffer.flushInterval` ' + + 'is a different key with its own rename.'; + +const HTTP_RETRY_INITIAL_DELAY_RETIRED = + '`HttpDestinationConfig.retry.initialDelay` was renamed to `initialDelayMs` 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 published a bare 1000. Rename the key to `initialDelayMs`; ' + + 'the value (milliseconds) and the 1000 default are unchanged.'; + +const HTTP_TIMEOUT_RETIRED = + '`HttpDestinationConfig.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 published a bare 30000. Rename the key to `timeoutMs`; the ' + + 'value (milliseconds) and the 30000 default are unchanged.'; + /** * HTTP Destination Configuration */ @@ -273,9 +311,19 @@ export const HttpDestinationConfigSchema = lazySchema(() => z.object({ maxSize: z.number().int().positive().optional().default(100), /** - * Flush interval in milliseconds + * Flush interval in milliseconds. + * + * Renamed from `flushInterval` (#17782, ruling A on #15939 executing + * #14478): the unit lived in this JSDoc only and the key carried no + * `.describe()` at all — the text `content/docs/references/**` publishes — + * so the reference page showed a bare 5000. Tombstoned rather than deleted + * because this nested object is not `.strict()`. */ - flushInterval: z.number().int().positive().optional().default(5000), + flushIntervalMs: z.number().int().positive().optional().default(5000) + .describe('Flush interval in milliseconds'), + + /** Tombstone for the rename above (#17782, ruling A on #15939). */ + flushInterval: retiredKey(HTTP_BATCH_FLUSH_INTERVAL_RETIRED), }).optional(), /** @@ -288,9 +336,19 @@ export const HttpDestinationConfigSchema = lazySchema(() => z.object({ maxAttempts: z.number().int().positive().optional().default(3), /** - * Initial retry delay in milliseconds + * Initial retry delay in milliseconds. + * + * Renamed from `initialDelay` (#17782, 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 showed a bare 1000. + * Tombstoned rather than deleted because this nested object is not + * `.strict()`. */ - initialDelay: z.number().int().positive().optional().default(1000), + initialDelayMs: z.number().int().positive().optional().default(1000) + .describe('Initial retry delay in milliseconds'), + + /** Tombstone for the rename above (#17782, ruling A on #15939). */ + initialDelay: retiredKey(HTTP_RETRY_INITIAL_DELAY_RETIRED), /** * Backoff multiplier @@ -299,9 +357,18 @@ export const HttpDestinationConfigSchema = lazySchema(() => z.object({ }).optional(), /** - * Timeout in milliseconds + * Timeout in milliseconds. + * + * Renamed from `timeout` (#17782, 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 showed a bare 30000. Tombstoned rather than deleted + * because this object is not `.strict()`. */ - timeout: z.number().int().positive().optional().default(30000), + timeoutMs: z.number().int().positive().optional().default(30000) + .describe('Timeout in milliseconds'), + + /** Tombstone for the rename above (#17782, ruling A on #15939). */ + timeout: retiredKey(HTTP_TIMEOUT_RETIRED), }).describe('HTTP destination configuration')); export type HttpDestinationConfig = z.input; @@ -572,6 +639,16 @@ export type StructuredLogEntry = z.input; * Logging Configuration Schema * Main configuration for the logging system */ +const LOGGING_BUFFER_FLUSH_INTERVAL_RETIRED = + '`LoggingConfig.buffer.flushInterval` was renamed to `flushIntervalMs` 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 published a bare 1000. Rename the key to `flushIntervalMs`; ' + + 'the value (milliseconds) and the 1000 default are unchanged. This is the ' + + 'in-process log buffer — `HttpDestinationConfig.batch.flushInterval` is a ' + + 'different key with its own rename.'; + export const LoggingConfigSchema = lazySchema(() => z.object({ /** * Configuration name @@ -667,9 +744,19 @@ export const LoggingConfigSchema = lazySchema(() => z.object({ size: z.number().int().positive().optional().default(1000), /** - * Flush interval in milliseconds + * Flush interval in milliseconds. + * + * Renamed from `flushInterval` (#17782, 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 showed a bare 1000. + * Tombstoned rather than deleted because this nested object is not + * `.strict()`. */ - flushInterval: z.number().int().positive().optional().default(1000), + flushIntervalMs: z.number().int().positive().optional().default(1000) + .describe('Flush interval in milliseconds'), + + /** Tombstone for the rename above (#17782, ruling A on #15939). */ + flushInterval: retiredKey(LOGGING_BUFFER_FLUSH_INTERVAL_RETIRED), /** * Flush on shutdown From 360d095dbd82c7b499dd76dbcd96ff212f9ca02d Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 13 Sep 2026 10:15:16 +0000 Subject: [PATCH 2/2] feat(spec)!: the four system/logging.zod.ts duration keys carry their unit in the key name Renames HttpDestinationConfig batch.flushInterval / retry.initialDelay / timeout and LoggingConfig buffer.flushInterval to their *Ms spellings, each with a retiredKey() tombstone, one ADR-0087 semantic entry and four RETIRED_KEYS_BY_MAJOR rows. Values and defaults are unchanged. Co-authored-by: Claude Claude-Session: https://claude.ai/code/session_015c5G6TmpMKgnusmTpD7Ntt --- .changeset/17782-logging-duration-units.md | 98 +++++++++++++++++++ content/docs/references/system/logging.mdx | 43 ++++++-- packages/spec/authorable-defaults/system.json | 2 +- packages/spec/authorable-surface/system.json | 3 +- ...pDestinationConfig__batch.flushInterval.ts | 14 +++ ...tpDestinationConfig__retry.initialDelay.ts | 11 +++ ....system__HttpDestinationConfig__timeout.ts | 14 +++ ...em__LoggingConfig__buffer.flushInterval.ts | 13 +++ .../18.logging-durations-unit-in-key.ts | 58 +++++++++++ packages/spec/src/migrations/registry.ts | 98 +++++++++++++++++++ 10 files changed, 345 insertions(+), 9 deletions(-) create mode 100644 .changeset/17782-logging-duration-units.md create mode 100644 packages/spec/src/migrations/entries/retired-keys/18.system__HttpDestinationConfig__batch.flushInterval.ts create mode 100644 packages/spec/src/migrations/entries/retired-keys/18.system__HttpDestinationConfig__retry.initialDelay.ts create mode 100644 packages/spec/src/migrations/entries/retired-keys/18.system__HttpDestinationConfig__timeout.ts create mode 100644 packages/spec/src/migrations/entries/retired-keys/18.system__LoggingConfig__buffer.flushInterval.ts create mode 100644 packages/spec/src/migrations/entries/semantic/18.logging-durations-unit-in-key.ts diff --git a/.changeset/17782-logging-duration-units.md b/.changeset/17782-logging-duration-units.md new file mode 100644 index 00000000000..193e3ba1603 --- /dev/null +++ b/.changeset/17782-logging-duration-units.md @@ -0,0 +1,98 @@ +--- +"@objectstack/spec": minor +--- + +feat(spec)!: the four `system/logging.zod.ts` duration keys carry their unit in the key name (#17782, ruling A on #15939) + + + +**BREAKING** — the HTTP log destination's batch flush, retry backoff start and request deadline, +and the logging buffer's flush, now carry `Ms` in the key name. + +| def | before | after | +|:--|:--|:--| +| `HttpDestinationConfig` | `batch.flushInterval: 5000` | `batch.flushIntervalMs: 5000` | +| `HttpDestinationConfig` | `retry.initialDelay: 1000` | `retry.initialDelayMs: 1000` | +| `HttpDestinationConfig` | `timeout: 30000` | `timeoutMs: 30000` | +| `LoggingConfig` | `buffer.flushInterval: 1000` | `buffer.flushIntervalMs: 1000` | +| values, defaults, bounds | ms; 5000 / 1000 / 30000 / 1000; positive int | **unchanged** | + +## Migration + +```diff + const destination = HttpDestinationConfigSchema.parse({ + url: 'https://logs.example.com/v1/logs', +- batch: { maxSize: 500, flushInterval: 10000 }, +- retry: { maxAttempts: 3, initialDelay: 1000 }, +- timeout: 30000, ++ batch: { maxSize: 500, flushIntervalMs: 10000 }, ++ retry: { maxAttempts: 3, initialDelayMs: 1000 }, ++ timeoutMs: 30000, + }); + + const logging = LoggingConfigSchema.parse({ + name: 'app_logging', + label: 'App logging', + destinations: [], +- buffer: { enabled: true, size: 5000, flushInterval: 2000 }, ++ buffer: { enabled: true, size: 5000, flushIntervalMs: 2000 }, + }); +``` + +Rename the keys. Every value is the same number of milliseconds it always was, and the +5000 / 1000 / 30000 / 1000 defaults are unchanged; nothing else on either def moves. + +## Why + +Each key named milliseconds in a source JSDoc — "Flush interval in milliseconds", "Initial retry +delay in milliseconds", "Timeout in milliseconds" — and the JSDoc above a key is not what +`content/docs/references/**` renders; `.describe()` is, and **none of the four carried one at +all**. Measured by the `check:duration-unit-keys` census on this tree before the change, all four +read `[name: -] [prose: -]`: no unit in the key, and no published prose to supply it either. So +`content/docs/references/system/logging.mdx` printed a bare `5000` / `1000` / `30000` / `1000`, +and nothing on the page decided milliseconds from seconds. Under the #14478 rule, moving the unit +into the describe alone would itself be a violation (unit in prose, none in the name), so each key +is renamed and given the describe it never had in the same stroke. Executes director-seat ruling A +on #15939 (2026-09-11, maintainer 「同意」, decision batch #115), the per-file remediation of the +#14478 rule. + +⚠️ `flushInterval` was declared **twice** on this file, in two different defs and with two +different defaults — 5000 on the HTTP destination's `batch`, 1000 on the logging `buffer`. They are +two keys, not one; each gets its own tombstone, its own registered row, and a prescription that +names its def, so an author who lands on one is not sent to the other. + +The `Ms` suffix is the family's own spelling, counted in key position on this tree: 272 `*Ms:` +declarations in `packages/spec/src` against 75 `*Seconds:`. The only competing unit spellings are +3 `*MS:` and 9 `*Millis:`, and every one of them mirrors a name fixed outside this repo — MongoDB's +`maxCommitTimeMS` and `connectTimeoutMS`, node-postgres's `idleTimeoutMillis` and +`connectionTimeoutMillis` on `PoolConfigSchema` — so unlike the `Ttl`-versus-`TTL` question a +sibling round had to settle, there was no in-repo alternative to choose between. All three target +spellings were already attested as key-position `*.zod.ts` declarations before this change: +`flushIntervalMs` 1 (on `kernel/events/integrations.zod.ts`, at the same 1000 default), +`initialDelayMs` 5, `timeoutMs` 30. + +## The kit + +- a `retiredKey()` tombstone on each of the four old spellings, so `tsc` types it `never` and a + value reaching the parse raises the rename prescription instead of being silently stripped — none + of the four enclosing objects is `.strict()` (`HttpDestinationConfig` itself and its nested + `batch` and `retry`; `LoggingConfig`'s nested `buffer`) +- the ADR-0087 D3 semantic entry `logging-durations-unit-in-key` and four + `RETIRED_KEYS_BY_MAJOR[18]` rows, one per key. No D2 conversion: `stack.zod.ts` declares no + logging collection and neither `LoggingConfigSchema` nor `HttpDestinationConfigSchema` is + referenced anywhere in `packages/spec/src` outside `system/logging.zod.ts`, so the chain has no + rehydration seam that runs on an authored logging document — the same reading + `tenant-schema-cache-ttl-unit-in-key` recorded for its sibling key +- pin tests per key: the refusal carries the rename prescription and names the def, the suffixed + key parses at the magnitude the retired one carried with the same default, and the describe + publishes the unit +- exactly one authorable-surface row pair moves, and it is the one that should: that ratchet records + top-level keys per def (`build-schemas.ts` reads `schema.properties` one level deep), and + `HttpDestinationConfig.timeout` is the only top-level key of the four — + `system/HttpDestinationConfig:timeout` becomes `[RETIRED]` beside a new + `system/HttpDestinationConfig:timeoutMs`, and the `authorable-defaults/` row is renamed with it. + The three nested keys move neither file, which is correct and not an omission +- the pinned objectui checkout is untouched by this rename: at `.objectui-sha` pin + `53ded82bf7a494f54e344e19099dbf00854b8694` it spells `flushInterval` 0 times, `initialDelay` 0, + `HttpDestinationConfig` 0 and `LoggingConfig` 0 across its 6409 tracked files, against lit + controls `useState` 2304 and `timeout` 702 on the same corpus diff --git a/content/docs/references/system/logging.mdx b/content/docs/references/system/logging.mdx index a07a4b6c9bb..499e2a45a31 100644 --- a/content/docs/references/system/logging.mdx +++ b/content/docs/references/system/logging.mdx @@ -109,9 +109,10 @@ HTTP destination configuration | **method** | `Enum<'POST' \| 'PUT'>` | optional (default: `"POST"`) | | | **headers** | `Record` | optional | | | **auth** | `{ type: Enum<'basic' \| 'bearer' \| 'api_key'>; username?: string; password?: string; token?: string; … }` | optional | | -| **batch** | `{ maxSize: integer; flushInterval: integer }` | optional | | -| **retry** | `{ maxAttempts: integer; initialDelay: integer; backoffMultiplier: number }` | optional | | -| **timeout** | `integer` | optional (default: `30000`) | | +| **batch** | `{ maxSize: integer; flushIntervalMs: integer }` | optional | | +| **retry** | `{ maxAttempts: integer; initialDelayMs: integer; backoffMultiplier: number }` | optional | | +| **timeoutMs** | `integer` | optional (default: `30000`) | Timeout in milliseconds | +| **timeout** | `never` | optional | [REMOVED] `HttpDestinationConfig.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 published a bare 30000. Rename the key to `timeoutMs`; the value (milliseconds) and the 30000 default are unchanged. | ### Nested Shape: `HttpDestinationConfig.auth` @@ -124,6 +125,23 @@ HTTP destination configuration | **apiKey** | `string` | optional | | | **apiKeyHeader** | `string` | optional (default: `"X-API-Key"`) | | +### Nested Shape: `HttpDestinationConfig.batch` + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **maxSize** | `integer` | optional (default: `100`) | | +| **flushIntervalMs** | `integer` | optional (default: `5000`) | Flush interval in milliseconds | +| **flushInterval** | `never` | optional | [REMOVED] `HttpDestinationConfig.batch.flushInterval` was renamed to `flushIntervalMs` 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 published a bare 5000. Rename the key to `flushIntervalMs`; the value (milliseconds) and the 5000 default are unchanged. This is the batch flush on an HTTP log destination — `LoggingConfig.buffer.flushInterval` is a different key with its own rename. | + +### Nested Shape: `HttpDestinationConfig.retry` + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **maxAttempts** | `integer` | optional (default: `3`) | | +| **initialDelayMs** | `integer` | optional (default: `1000`) | Initial retry delay in milliseconds | +| **initialDelay** | `never` | optional | [REMOVED] `HttpDestinationConfig.retry.initialDelay` was renamed to `initialDelayMs` 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 published a bare 1000. Rename the key to `initialDelayMs`; the value (milliseconds) and the 1000 default are unchanged. | +| **backoffMultiplier** | `number` | optional (default: `2`) | | + --- @@ -163,9 +181,10 @@ Log destination configuration | **method** | `Enum<'POST' \| 'PUT'>` | optional (default: `"POST"`) | | | **headers** | `Record` | optional | | | **auth** | `{ type: Enum<'basic' \| 'bearer' \| 'api_key'>; username?: string; password?: string; token?: string; … }` | optional | | -| **batch** | `{ maxSize: integer; flushInterval: integer }` | optional | | -| **retry** | `{ maxAttempts: integer; initialDelay: integer; backoffMultiplier: number }` | optional | | -| **timeout** | `integer` | optional (default: `30000`) | | +| **batch** | `{ maxSize: integer; flushIntervalMs: integer }` | optional | | +| **retry** | `{ maxAttempts: integer; initialDelayMs: integer; backoffMultiplier: number }` | optional | | +| **timeoutMs** | `integer` | optional (default: `30000`) | Timeout in milliseconds | +| **timeout** | `never` | optional | [REMOVED] `HttpDestinationConfig.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 published a bare 30000. Rename the key to `timeoutMs`; the value (milliseconds) and the 30000 default are unchanged. | --- @@ -297,7 +316,7 @@ Logging configuration | **enrichment** | `{ staticFields?: Record; dynamicEnrichers?: string[]; addHostname: boolean; addProcessId: boolean; … }` | optional | Log enrichment configuration | | **redact** | `string[]` | optional (has default) | Fields to redact | | **sampling** | `{ enabled: boolean; rate: number; rateByLevel?: Record }` | optional | | -| **buffer** | `{ enabled: boolean; size: integer; flushInterval: integer; flushOnShutdown: boolean }` | optional | | +| **buffer** | `{ enabled: boolean; size: integer; flushIntervalMs: integer; flushOnShutdown: boolean }` | optional | | | **performance** | `{ async: boolean; workers: integer }` | optional | | ### Nested Shape: `LoggingConfig.default` @@ -354,6 +373,16 @@ Log destination configuration | **addCaller** | `boolean` | optional (default: `false`) | | | **addCorrelationIds** | `boolean` | optional (default: `true`) | | +### Nested Shape: `LoggingConfig.buffer` + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **enabled** | `boolean` | optional (default: `true`) | | +| **size** | `integer` | optional (default: `1000`) | | +| **flushIntervalMs** | `integer` | optional (default: `1000`) | Flush interval in milliseconds | +| **flushInterval** | `never` | optional | [REMOVED] `LoggingConfig.buffer.flushInterval` was renamed to `flushIntervalMs` 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 published a bare 1000. Rename the key to `flushIntervalMs`; the value (milliseconds) and the 1000 default are unchanged. This is the in-process log buffer — `HttpDestinationConfig.batch.flushInterval` is a different key with its own rename. | +| **flushOnShutdown** | `boolean` | optional (default: `true`) | | + --- diff --git a/packages/spec/authorable-defaults/system.json b/packages/spec/authorable-defaults/system.json index 6f73de81d9d..b6ded02f55c 100644 --- a/packages/spec/authorable-defaults/system.json +++ b/packages/spec/authorable-defaults/system.json @@ -107,7 +107,7 @@ "system/FileDestinationConfig:append = true", "system/FileDestinationConfig:encoding = \"utf8\"", "system/HttpDestinationConfig:method = \"POST\"", - "system/HttpDestinationConfig:timeout = 30000", + "system/HttpDestinationConfig:timeoutMs = 30000", "system/Job:enabled = true", "system/KeyRotationPolicy:autoRotate = true", "system/KeyRotationPolicy:enabled = false", diff --git a/packages/spec/authorable-surface/system.json b/packages/spec/authorable-surface/system.json index 3d2fc43ca2e..2e54de5817e 100644 --- a/packages/spec/authorable-surface/system.json +++ b/packages/spec/authorable-surface/system.json @@ -460,7 +460,8 @@ "system/HttpDestinationConfig:headers", "system/HttpDestinationConfig:method", "system/HttpDestinationConfig:retry", - "system/HttpDestinationConfig:timeout", + "system/HttpDestinationConfig:timeout [RETIRED]", + "system/HttpDestinationConfig:timeoutMs", "system/HttpDestinationConfig:url", "system/IntervalSchedule:intervalMs", "system/IntervalSchedule:type", diff --git a/packages/spec/src/migrations/entries/retired-keys/18.system__HttpDestinationConfig__batch.flushInterval.ts b/packages/spec/src/migrations/entries/retired-keys/18.system__HttpDestinationConfig__batch.flushInterval.ts new file mode 100644 index 00000000000..a3bb63a207c --- /dev/null +++ b/packages/spec/src/migrations/entries/retired-keys/18.system__HttpDestinationConfig__batch.flushInterval.ts @@ -0,0 +1,14 @@ +// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. + +// #15939 ruling A (per-file remediation of #14478). `batch.flushInterval` said +// "Flush interval in milliseconds" in a source JSDoc and carried no +// `.describe()` at all, so the reference page published a bare 5000. Renamed to +// `flushIntervalMs` — the family's own spelling, 272 key-position `*Ms:` +// declarations in `packages/spec/src` and `flushIntervalMs` already declared on +// `kernel/events/integrations.zod.ts`. 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. ⚠️ Not the same key +// as `system/LoggingConfig:buffer.flushInterval`, which defaults to 1000 and +// has its own row. No D2 conversion: no logging collection on `stack.zod.ts`, +// not a stored row. See `logging-durations-unit-in-key`. +export const entry = 'system/HttpDestinationConfig:batch.flushInterval'; diff --git a/packages/spec/src/migrations/entries/retired-keys/18.system__HttpDestinationConfig__retry.initialDelay.ts b/packages/spec/src/migrations/entries/retired-keys/18.system__HttpDestinationConfig__retry.initialDelay.ts new file mode 100644 index 00000000000..0d5ee6d1e9d --- /dev/null +++ b/packages/spec/src/migrations/entries/retired-keys/18.system__HttpDestinationConfig__retry.initialDelay.ts @@ -0,0 +1,11 @@ +// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. + +// #15939 ruling A (per-file remediation of #14478). `retry.initialDelay` said +// "Initial retry delay in milliseconds" in a source JSDoc and carried no +// `.describe()` at all, so the reference page published a bare 1000. Renamed to +// `initialDelayMs` — already attested as a key-position declaration 5 times on +// this tree. The value and the 1000 default are unchanged. Tombstoned with +// `retiredKey()`: the nested `retry` object is not strict, so a bare deletion +// would silently strip the key. No D2 conversion: no logging collection on +// `stack.zod.ts`, not a stored row. See `logging-durations-unit-in-key`. +export const entry = 'system/HttpDestinationConfig:retry.initialDelay'; diff --git a/packages/spec/src/migrations/entries/retired-keys/18.system__HttpDestinationConfig__timeout.ts b/packages/spec/src/migrations/entries/retired-keys/18.system__HttpDestinationConfig__timeout.ts new file mode 100644 index 00000000000..b25c2406e66 --- /dev/null +++ b/packages/spec/src/migrations/entries/retired-keys/18.system__HttpDestinationConfig__timeout.ts @@ -0,0 +1,14 @@ +// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. + +// #15939 ruling A (per-file remediation of #14478). `timeout` said "Timeout in +// milliseconds" in a source JSDoc and carried no `.describe()` at all, so the +// reference page published a bare 30000. Renamed to `timeoutMs` — already +// attested as a key-position declaration 30 times on this tree. The value and +// the 30000 default are unchanged. Tombstoned with `retiredKey()`: +// `HttpDestinationConfig` is not strict, so a bare deletion would silently +// strip the key. ⚠️ The one TOP-LEVEL key of this card's four, so this is the +// one whose `authorable-surface/` and `authorable-defaults/` rows move — that +// ratchet records `schema.properties` one level deep. No D2 conversion: no +// logging collection on `stack.zod.ts`, not a stored row. See +// `logging-durations-unit-in-key`. +export const entry = 'system/HttpDestinationConfig:timeout'; diff --git a/packages/spec/src/migrations/entries/retired-keys/18.system__LoggingConfig__buffer.flushInterval.ts b/packages/spec/src/migrations/entries/retired-keys/18.system__LoggingConfig__buffer.flushInterval.ts new file mode 100644 index 00000000000..447068b1654 --- /dev/null +++ b/packages/spec/src/migrations/entries/retired-keys/18.system__LoggingConfig__buffer.flushInterval.ts @@ -0,0 +1,13 @@ +// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. + +// #15939 ruling A (per-file remediation of #14478). `buffer.flushInterval` said +// "Flush interval in milliseconds" in a source JSDoc and carried no +// `.describe()` at all, so the reference page published a bare 1000. Renamed to +// `flushIntervalMs`. The value and the 1000 default are unchanged. Tombstoned +// with `retiredKey()`: the nested `buffer` object is not strict, so a bare +// deletion would silently strip the key. ⚠️ Not the same key as +// `system/HttpDestinationConfig:batch.flushInterval`, which defaults to 5000 +// and has its own row — the two spellings were identical and the defaults never +// were. No D2 conversion: no logging collection on `stack.zod.ts`, not a stored +// row. See `logging-durations-unit-in-key`. +export const entry = 'system/LoggingConfig:buffer.flushInterval'; diff --git a/packages/spec/src/migrations/entries/semantic/18.logging-durations-unit-in-key.ts b/packages/spec/src/migrations/entries/semantic/18.logging-durations-unit-in-key.ts new file mode 100644 index 00000000000..9af3b362985 --- /dev/null +++ b/packages/spec/src/migrations/entries/semantic/18.logging-durations-unit-in-key.ts @@ -0,0 +1,58 @@ +// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. + +import type { SemanticMigration } from '../../types.js'; + +export const entry: SemanticMigration = { + id: 'logging-durations-unit-in-key', + surface: 'HttpDestinationConfig `batch.flushInterval` / `retry.initialDelay` / `timeout` and ' + + 'LoggingConfig `buffer.flushInterval` (system/logging.zod.ts)', + replacement: '`batch.flushIntervalMs` (default 5000) / `retry.initialDelayMs` (default 1000) / ' + + '`timeoutMs` (default 30000) on HttpDestinationConfig, and `buffer.flushIntervalMs` ' + + '(default 1000) on LoggingConfig — rename the keys; every value (milliseconds) is unchanged', + reason: + 'Director-seat ruling A on #15939, 2026-09-11, carrying the maintainer\'s 「同意」 (decision ' + + 'batch #115), executing the #14478 rule per file. All four keys named milliseconds in a ' + + 'source JSDoc — "Flush interval in milliseconds", "Initial retry delay in milliseconds", ' + + '"Timeout in milliseconds" — and the JSDoc above a key is not what ' + + '`content/docs/references/**` renders; `.describe()` is, and none of the four carried one at ' + + 'all. Measured by the `check:duration-unit-keys` census on this tree before the change, all ' + + 'four read `[name: -] [prose: -]`: no unit in the key and no published prose to supply it, ' + + 'so `content/docs/references/system/logging.mdx` printed a bare 5000 / 1000 / 30000 / 1000 ' + + 'and nothing on the page decided milliseconds from seconds. Under the #14478 gate, moving ' + + 'the unit into the describe alone is itself a violation (unit in prose, none in the name), ' + + 'so each key is renamed and given the describe it never had in the same stroke. ' + + '⚠️ `flushInterval` was declared TWICE on this file, in two different defs and with two ' + + 'different defaults — 5000 on the HTTP destination\'s batch and 1000 on the logging buffer ' + + '— so they are two keys, each with its own tombstone and its own registered row; the ' + + 'prescriptions name their def so a reader who lands on one is not sent to the other. The ' + + '`Ms` suffix is the family\'s own spelling, counted in key position on this tree: 272 ' + + '`*Ms:` declarations in `packages/spec/src` against 75 `*Seconds:`, and the only competing ' + + 'unit spellings are 3 `*MS:` and 9 `*Millis:` — every one of them a name fixed outside this ' + + 'repo (MongoDB\'s `maxCommitTimeMS` and `connectTimeoutMS`, node-postgres\'s ' + + '`idleTimeoutMillis` and `connectionTimeoutMillis` on `PoolConfigSchema`), so unlike the ' + + '`Ttl`-versus-`TTL` question a sibling round settled there is no in-repo alternative to ' + + 'choose between. All three target spellings were already attested as key-position `*.zod.ts` ' + + 'declarations before this change: `flushIntervalMs` 1 (`kernel/events/integrations.zod.ts`, ' + + 'same 1000 default), `initialDelayMs` 5, `timeoutMs` 30. Tombstoned with `retiredKey()` ' + + 'rather than deleted because none of the four enclosing objects — `HttpDestinationConfig` ' + + 'itself and its nested `batch` and `retry`, and `LoggingConfig`\'s nested `buffer` — is ' + + '`.strict()`, so a bare deletion would have stripped the value in silence. Why a semantic ' + + 'entry and not a D2 conversion: `stack.zod.ts` declares no logging collection and neither ' + + '`LoggingConfigSchema` nor `HttpDestinationConfigSchema` is referenced anywhere in ' + + '`packages/spec/src` outside `system/logging.zod.ts`, so the chain has no rehydration seam ' + + 'that runs on an authored logging document — the same reading ' + + '`tenant-schema-cache-ttl-unit-in-key` recorded for its sibling key. Measured on 4dab2bc5c: ' + + 'no in-repo runtime reads any of the four — outside `packages/spec/src/system/logging.zod.ts` ' + + 'and its test the only occurrences are the generated rows in ' + + '`content/docs/references/system/logging.mdx`, which this rename regenerates; and the pinned ' + + 'objectui checkout — `.objectui-sha` = `53ded82bf7a494f54e344e19099dbf00854b8694` — spells ' + + '`flushInterval` 0 times, `initialDelay` 0, `HttpDestinationConfig` 0 and `LoggingConfig` 0 ' + + 'across its 6409 tracked files, against lit controls `useState` 2304 and `timeout` 702 on ' + + 'the same corpus.', + acceptanceCriteria: + 'Every HTTP log destination spells `batch.flushIntervalMs`, `retry.initialDelayMs` and ' + + '`timeoutMs`, and every logging buffer spells `buffer.flushIntervalMs`; authoring any of the ' + + 'four retired spellings fails to compile and fails to parse with a rename prescription ' + + 'naming the suffixed key and its def; the parsed defaults are 5000 / 1000 / 30000 / 1000 as ' + + 'before; and each published describe names milliseconds.', +}; diff --git a/packages/spec/src/migrations/registry.ts b/packages/spec/src/migrations/registry.ts index 4ab46c7fb5f..7936ebae423 100644 --- a/packages/spec/src/migrations/registry.ts +++ b/packages/spec/src/migrations/registry.ts @@ -8722,6 +8722,60 @@ const step18: MigrationStep = { + 'PluginStartupResult interface — a different type, carrying startTime rather than any ' + 'duration key — which is not a reader of this schema and is unchanged.', }, + { + id: 'logging-durations-unit-in-key', + surface: 'HttpDestinationConfig `batch.flushInterval` / `retry.initialDelay` / `timeout` and ' + + 'LoggingConfig `buffer.flushInterval` (system/logging.zod.ts)', + replacement: '`batch.flushIntervalMs` (default 5000) / `retry.initialDelayMs` (default 1000) / ' + + '`timeoutMs` (default 30000) on HttpDestinationConfig, and `buffer.flushIntervalMs` ' + + '(default 1000) on LoggingConfig — rename the keys; every value (milliseconds) is unchanged', + reason: + 'Director-seat ruling A on #15939, 2026-09-11, carrying the maintainer\'s 「同意」 (decision ' + + 'batch #115), executing the #14478 rule per file. All four keys named milliseconds in a ' + + 'source JSDoc — "Flush interval in milliseconds", "Initial retry delay in milliseconds", ' + + '"Timeout in milliseconds" — and the JSDoc above a key is not what ' + + '`content/docs/references/**` renders; `.describe()` is, and none of the four carried one at ' + + 'all. Measured by the `check:duration-unit-keys` census on this tree before the change, all ' + + 'four read `[name: -] [prose: -]`: no unit in the key and no published prose to supply it, ' + + 'so `content/docs/references/system/logging.mdx` printed a bare 5000 / 1000 / 30000 / 1000 ' + + 'and nothing on the page decided milliseconds from seconds. Under the #14478 gate, moving ' + + 'the unit into the describe alone is itself a violation (unit in prose, none in the name), ' + + 'so each key is renamed and given the describe it never had in the same stroke. ' + + '⚠️ `flushInterval` was declared TWICE on this file, in two different defs and with two ' + + 'different defaults — 5000 on the HTTP destination\'s batch and 1000 on the logging buffer ' + + '— so they are two keys, each with its own tombstone and its own registered row; the ' + + 'prescriptions name their def so a reader who lands on one is not sent to the other. The ' + + '`Ms` suffix is the family\'s own spelling, counted in key position on this tree: 272 ' + + '`*Ms:` declarations in `packages/spec/src` against 75 `*Seconds:`, and the only competing ' + + 'unit spellings are 3 `*MS:` and 9 `*Millis:` — every one of them a name fixed outside this ' + + 'repo (MongoDB\'s `maxCommitTimeMS` and `connectTimeoutMS`, node-postgres\'s ' + + '`idleTimeoutMillis` and `connectionTimeoutMillis` on `PoolConfigSchema`), so unlike the ' + + '`Ttl`-versus-`TTL` question a sibling round settled there is no in-repo alternative to ' + + 'choose between. All three target spellings were already attested as key-position `*.zod.ts` ' + + 'declarations before this change: `flushIntervalMs` 1 (`kernel/events/integrations.zod.ts`, ' + + 'same 1000 default), `initialDelayMs` 5, `timeoutMs` 30. Tombstoned with `retiredKey()` ' + + 'rather than deleted because none of the four enclosing objects — `HttpDestinationConfig` ' + + 'itself and its nested `batch` and `retry`, and `LoggingConfig`\'s nested `buffer` — is ' + + '`.strict()`, so a bare deletion would have stripped the value in silence. Why a semantic ' + + 'entry and not a D2 conversion: `stack.zod.ts` declares no logging collection and neither ' + + '`LoggingConfigSchema` nor `HttpDestinationConfigSchema` is referenced anywhere in ' + + '`packages/spec/src` outside `system/logging.zod.ts`, so the chain has no rehydration seam ' + + 'that runs on an authored logging document — the same reading ' + + '`tenant-schema-cache-ttl-unit-in-key` recorded for its sibling key. Measured on 4dab2bc5c: ' + + 'no in-repo runtime reads any of the four — outside `packages/spec/src/system/logging.zod.ts` ' + + 'and its test the only occurrences are the generated rows in ' + + '`content/docs/references/system/logging.mdx`, which this rename regenerates; and the pinned ' + + 'objectui checkout — `.objectui-sha` = `53ded82bf7a494f54e344e19099dbf00854b8694` — spells ' + + '`flushInterval` 0 times, `initialDelay` 0, `HttpDestinationConfig` 0 and `LoggingConfig` 0 ' + + 'across its 6409 tracked files, against lit controls `useState` 2304 and `timeout` 702 on ' + + 'the same corpus.', + acceptanceCriteria: + 'Every HTTP log destination spells `batch.flushIntervalMs`, `retry.initialDelayMs` and ' + + '`timeoutMs`, and every logging buffer spells `buffer.flushIntervalMs`; authoring any of the ' + + 'four retired spellings fails to compile and fails to parse with a rename prescription ' + + 'naming the suffixed key and its def; the parsed defaults are 5000 / 1000 / 30000 / 1000 as ' + + 'before; and each published describe names milliseconds.', + }, { id: 'memory-persistence-placeholder-refused', surface: 'memory driver config `persistence.path` (file persistence and the `auto` ' + @@ -12980,6 +13034,39 @@ export const RETIRED_KEYS_BY_MAJOR: Readonly> // not a stack collection member, not a stored row. // See `system-failover-health-check-interval-unit-in-key`. 'system/FailoverConfig:healthCheckInterval', + // #15939 ruling A (per-file remediation of #14478). `batch.flushInterval` said + // "Flush interval in milliseconds" in a source JSDoc and carried no + // `.describe()` at all, so the reference page published a bare 5000. Renamed to + // `flushIntervalMs` — the family's own spelling, 272 key-position `*Ms:` + // declarations in `packages/spec/src` and `flushIntervalMs` already declared on + // `kernel/events/integrations.zod.ts`. 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. ⚠️ Not the same key + // as `system/LoggingConfig:buffer.flushInterval`, which defaults to 1000 and + // has its own row. No D2 conversion: no logging collection on `stack.zod.ts`, + // not a stored row. See `logging-durations-unit-in-key`. + 'system/HttpDestinationConfig:batch.flushInterval', + // #15939 ruling A (per-file remediation of #14478). `retry.initialDelay` said + // "Initial retry delay in milliseconds" in a source JSDoc and carried no + // `.describe()` at all, so the reference page published a bare 1000. Renamed to + // `initialDelayMs` — already attested as a key-position declaration 5 times on + // this tree. The value and the 1000 default are unchanged. Tombstoned with + // `retiredKey()`: the nested `retry` object is not strict, so a bare deletion + // would silently strip the key. No D2 conversion: no logging collection on + // `stack.zod.ts`, not a stored row. See `logging-durations-unit-in-key`. + 'system/HttpDestinationConfig:retry.initialDelay', + // #15939 ruling A (per-file remediation of #14478). `timeout` said "Timeout in + // milliseconds" in a source JSDoc and carried no `.describe()` at all, so the + // reference page published a bare 30000. Renamed to `timeoutMs` — already + // attested as a key-position declaration 30 times on this tree. The value and + // the 30000 default are unchanged. Tombstoned with `retiredKey()`: + // `HttpDestinationConfig` is not strict, so a bare deletion would silently + // strip the key. ⚠️ The one TOP-LEVEL key of this card's four, so this is the + // one whose `authorable-surface/` and `authorable-defaults/` rows move — that + // ratchet records `schema.properties` one level deep. No D2 conversion: no + // logging collection on `stack.zod.ts`, not a stored row. See + // `logging-durations-unit-in-key`. + 'system/HttpDestinationConfig:timeout', // #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 @@ -13122,6 +13209,17 @@ export const RETIRED_KEYS_BY_MAJOR: Readonly> // (launch-window convention) and the prescription lives at the major boundary // where `migrate meta` users look. 'system/Job:timeout', + // #15939 ruling A (per-file remediation of #14478). `buffer.flushInterval` said + // "Flush interval in milliseconds" in a source JSDoc and carried no + // `.describe()` at all, so the reference page published a bare 1000. Renamed to + // `flushIntervalMs`. The value and the 1000 default are unchanged. Tombstoned + // with `retiredKey()`: the nested `buffer` object is not strict, so a bare + // deletion would silently strip the key. ⚠️ Not the same key as + // `system/HttpDestinationConfig:batch.flushInterval`, which defaults to 5000 + // and has its own row — the two spellings were identical and the defaults never + // were. No D2 conversion: no logging collection on `stack.zod.ts`, not a stored + // row. See `logging-durations-unit-in-key`. + 'system/LoggingConfig:buffer.flushInterval', // #15679 (stack card 4/6 of #14478) — ruling B. `MetricAggregationConfig.window.size` // said "Window size in seconds" in prose and nothing else. Renamed to // `durationSeconds`, NOT to the gate's mechanical `sizeSeconds`: `size` is