Skip to content

Commit 9999954

Browse files
os-salesclaude
andcommitted
wip(spec): strip issue ids from tombstones, declare the gate population, regen docs (#14478)
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G4138K1EG7kQ81FNba5Kp4
1 parent f5141c1 commit 9999954

12 files changed

Lines changed: 34 additions & 15 deletions

File tree

.changeset/metadata-database-loader-ttl-ms.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
feat(metadata)!: `DatabaseLoaderOptions.cache.ttl``cache.ttlMs` — the read-through cache TTL carries its unit in the key name (#14478)
66

7-
<!-- adr-0087: not-required (already-registered metadata-manager-config-cache-ttl-unit-in-key) The authorable key this option mirrors — `MetadataManagerConfig.cache.databaseLoader.ttl` → `ttlMs` — is registered by the `@objectstack/spec` changeset of the same change; this package's exported `DatabaseLoaderCacheOptions` interface follows that key one-to-one and has no separate metadata surface to register. -->
7+
<!-- adr-0087: registered metadata-manager-config-cache-ttl-unit-in-key -->
88

99
**BREAKING** rename on the exported `DatabaseLoaderOptions.cache` shape
1010
(`DatabaseLoaderCacheOptions.ttl``ttlMs`), shipped as `minor` under the

content/docs/references/data/driver.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ const result = DriverCapabilitiesSchema.parse(data);
136136
| :--- | :--- | :--- | :--- |
137137
| **transaction** | `any` | optional | Transaction handle |
138138
| **timeoutMs** | `number` | optional | Operation timeout in milliseconds |
139-
| **timeout** | `never` | optional | [REMOVED] `DriverOptions.timeout` was removed in @objectstack/spec 17 (#14478) — its unit (milliseconds) lived only in the description. Rename the key to `timeoutMs`; the value (milliseconds) is unchanged. |
139+
| **timeout** | `never` | optional | [REMOVED] `DriverOptions.timeout` was removed in @objectstack/spec 17 — its unit (milliseconds) lived only in the description. Rename the key to `timeoutMs`; the value (milliseconds) is unchanged. |
140140
| **skipCache** | `boolean` | optional | Bypass cache |
141141
| **traceContext** | `Record<string, string>` | optional | OpenTelemetry context or request ID |
142142
| **tenantId** | `string` | optional | Tenant Isolation identifier |

content/docs/references/kernel/metadata-loader.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ const result = MetadataFallbackStrategySchema.parse(data);
6262
| :--- | :--- | :--- | :--- |
6363
| **enabled** | `boolean` | optional (default: `true`) | Enable caching |
6464
| **ttlSeconds** | `integer` | optional (default: `3600`) | Cache TTL in seconds |
65-
| **ttl** | `never` | optional | [REMOVED] `cache.ttl` was removed from `MetadataManagerConfig` in @objectstack/spec 17 (#14478) — its unit (seconds) lived only in the description, while the nested `cache.databaseLoader.ttl` spelled the same word in milliseconds, so one key name meant two magnitudes 1000× apart. Rename the key to `ttlSeconds`; the value (seconds) is unchanged. |
65+
| **ttl** | `never` | optional | [REMOVED] `cache.ttl` was removed from `MetadataManagerConfig` in @objectstack/spec 17 — its unit (seconds) lived only in the description, while the nested `cache.databaseLoader.ttl` spelled the same word in milliseconds, so one key name meant two magnitudes 1000× apart. Rename the key to `ttlSeconds`; the value (seconds) is unchanged. |
6666
| **maxSize** | `integer` | optional | Max cache size in bytes |
6767
| **databaseLoader** | `{ enabled: boolean; maxSize: integer; ttlMs: integer }` | optional | DatabaseLoader read-through cache |
6868

content/docs/references/system/job.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ const result = CronScheduleSchema.parse(data);
5959
| **handler** | `string` || Handler function name (must match a key in `defineStack({ functions })`) |
6060
| **retryPolicy** | `{ maxRetries?: integer; backoffMs?: integer; backoffMultiplier?: number; maxRetryDelayMs?: integer; … }` | optional | Retry policy: failed runs (including timeouts) are retried with exponential backoff (delay = min(backoffMs * backoffMultiplier^(retry-1), maxRetryDelayMs), optionally jittered) up to maxRetries retries after the initial attempt. Omit the block for a single attempt; declaring it without `maxRetries` also means no retry since 17.0.0 — state a count to opt in. |
6161
| **timeoutMs** | `integer` | optional | Per-attempt time limit in milliseconds; an over-limit run is recorded with execution status "timeout". The in-flight handler is abandoned, not forcibly cancelled. Omit for no time limit. |
62-
| **timeout** | `never` | optional | [REMOVED] `job.timeout` was removed in @objectstack/spec 17 (#14478) — its unit (milliseconds) lived only in the description while the sibling `retryPolicy.backoffMs` spells its own, so the same number read as two conventions on one surface. Rename the key to `timeoutMs`; the value (milliseconds) is unchanged. Run `os migrate meta --from 17` to list the mechanical edits for existing sources; apply them by hand. |
62+
| **timeout** | `never` | optional | [REMOVED] `job.timeout` was removed in @objectstack/spec 17 — its unit (milliseconds) lived only in the description while the sibling `retryPolicy.backoffMs` spells its own, so the same number read as two conventions on one surface. Rename the key to `timeoutMs`; the value (milliseconds) is unchanged. Run `os migrate meta --from 17` to list the mechanical edits for existing sources; apply them by hand. |
6363
| **enabled** | `boolean` | optional (default: `true`) | Whether the job is enabled |
6464
| **_lock** | `Enum<'none' \| 'no-overlay' \| 'no-delete' \| 'full'>` | optional | Item-level lock — controls overlay & delete (ADR-0010). |
6565
| **_lockReason** | `string` | optional | Human-readable reason shown when a write is refused by _lock. |

content/docs/references/system/metadata-persistence.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -255,7 +255,7 @@ Metadata file format
255255
| :--- | :--- | :--- | :--- |
256256
| **enabled** | `boolean` | optional (default: `true`) | Enable caching |
257257
| **ttlSeconds** | `integer` | optional (default: `3600`) | Cache TTL in seconds |
258-
| **ttl** | `never` | optional | [REMOVED] `cache.ttl` was removed from `MetadataManagerConfig` in @objectstack/spec 17 (#14478) — its unit (seconds) lived only in the description, while the nested `cache.databaseLoader.ttl` spelled the same word in milliseconds, so one key name meant two magnitudes 1000× apart. Rename the key to `ttlSeconds`; the value (seconds) is unchanged. |
258+
| **ttl** | `never` | optional | [REMOVED] `cache.ttl` was removed from `MetadataManagerConfig` in @objectstack/spec 17 — its unit (seconds) lived only in the description, while the nested `cache.databaseLoader.ttl` spelled the same word in milliseconds, so one key name meant two magnitudes 1000× apart. Rename the key to `ttlSeconds`; the value (seconds) is unchanged. |
259259
| **maxSize** | `integer` | optional | Max cache size in bytes |
260260
| **databaseLoader** | `{ enabled: boolean; maxSize: integer; ttlMs: integer }` | optional | DatabaseLoader read-through cache |
261261

content/docs/references/system/tenant.mdx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ const result = DatabaseLevelIsolationStrategySchema.parse(data);
6060
| **poolSize** | `integer` | optional (default: `10`) | Connection pool size |
6161
| **maxActivePools** | `integer` | optional (default: `100`) | Max active pools |
6262
| **idleTimeoutSeconds** | `integer` | optional (default: `300`) | Idle pool timeout in seconds |
63-
| **idleTimeout** | `never` | optional | [REMOVED] `connectionPool.idleTimeout` was removed from `DatabaseLevelIsolationStrategy` in @objectstack/spec 17 (#14478) — its unit (seconds) lived in a source comment only and the published description named none, so a reader of the reference page could not tell 300 seconds from 300 milliseconds. Rename the key to `idleTimeoutSeconds`; the value (seconds) is unchanged. |
63+
| **idleTimeout** | `never` | optional | [REMOVED] `connectionPool.idleTimeout` was removed from `DatabaseLevelIsolationStrategy` in @objectstack/spec 17 — its unit (seconds) lived in a source comment only and the published description named none, so a reader of the reference page could not tell 300 seconds from 300 milliseconds. Rename the key to `idleTimeoutSeconds`; the value (seconds) is unchanged. |
6464
| **usePooler** | `boolean` | optional (default: `true`) | Use connection pooler |
6565

6666
### Nested Shape: `DatabaseLevelIsolationStrategy.backup`
@@ -334,7 +334,7 @@ This schema accepts one of the following structures:
334334
| **poolSize** | `integer` | optional (default: `10`) | Connection pool size |
335335
| **maxActivePools** | `integer` | optional (default: `100`) | Max active pools |
336336
| **idleTimeoutSeconds** | `integer` | optional (default: `300`) | Idle pool timeout in seconds |
337-
| **idleTimeout** | `never` | optional | [REMOVED] `connectionPool.idleTimeout` was removed from `DatabaseLevelIsolationStrategy` in @objectstack/spec 17 (#14478) — its unit (seconds) lived in a source comment only and the published description named none, so a reader of the reference page could not tell 300 seconds from 300 milliseconds. Rename the key to `idleTimeoutSeconds`; the value (seconds) is unchanged. |
337+
| **idleTimeout** | `never` | optional | [REMOVED] `connectionPool.idleTimeout` was removed from `DatabaseLevelIsolationStrategy` in @objectstack/spec 17 — its unit (seconds) lived in a source comment only and the published description named none, so a reader of the reference page could not tell 300 seconds from 300 milliseconds. Rename the key to `idleTimeoutSeconds`; the value (seconds) is unchanged. |
338338
| **usePooler** | `boolean` | optional (default: `true`) | Use connection pooler |
339339

340340
### Nested Shape: `TenantIsolationConfig[strategy='isolated_db'].backup`
@@ -412,7 +412,7 @@ This schema accepts one of the following structures:
412412
| **requireSSO** | `boolean` | optional (default: `false`) | Require SSO |
413413
| **ipWhitelist** | `string[]` | optional | Allowed IP addresses |
414414
| **sessionTimeoutSeconds** | `integer` | optional (default: `3600`) | Session timeout in seconds |
415-
| **sessionTimeout** | `never` | optional | [REMOVED] `accessControl.sessionTimeout` was removed from `TenantSecurityPolicy` in @objectstack/spec 17 (#14478) — its unit (seconds) lived in a source comment only and the published description named none, so a reader of the reference page could not tell 3600 seconds from 3600 milliseconds. Rename the key to `sessionTimeoutSeconds`; the value (seconds) is unchanged. |
415+
| **sessionTimeout** | `never` | optional | [REMOVED] `accessControl.sessionTimeout` was removed from `TenantSecurityPolicy` in @objectstack/spec 17 — its unit (seconds) lived in a source comment only and the published description named none, so a reader of the reference page could not tell 3600 seconds from 3600 milliseconds. Rename the key to `sessionTimeoutSeconds`; the value (seconds) is unchanged. |
416416

417417
### Nested Shape: `TenantSecurityPolicy.compliance`
418418

packages/spec/scripts/check-duration-unit-keys.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,18 @@ import ts from 'typescript';
107107
const pkgRoot = join(dirname(fileURLToPath(import.meta.url)), '..');
108108
const SRC_ROOT = join(pkgRoot, 'src');
109109

110+
/**
111+
* The dispatch-gates declaration — the `ROOT_DIR_WATCH_HINTS` idiom (#12310).
112+
* `scripts/pm/dispatch-gates.mjs` derives which gates a card must run from the
113+
* path literals in each gate's source, and `check:declared-population-live`
114+
* refuses a gate whose only path-shaped literal names nothing in the tree.
115+
* This gate walks exactly one subtree — `packages/spec/src/`, tests excluded —
116+
* so that is what it declares, as a LITERAL (the extractor reads source text;
117+
* a value computed from `SRC_ROOT` would produce no hint). The self-test holds
118+
* the literal against the constant the scan actually reads from.
119+
*/
120+
export const ROOT_DIR_WATCH_HINTS = ['packages/spec/src/**'];
121+
110122
/** Canonical unit → every spelling the describe prose or a key token may use. */
111123
const UNIT_SPELLINGS: Readonly<Record<string, readonly string[]>> = {
112124
ms: ['ms', 'msec', 'msecs', 'millis', 'millisecond', 'milliseconds'],
@@ -438,6 +450,13 @@ function selfTest(): number {
438450
rulesOf(`const S = z.object({ a: z.number().describe('Wait 1 second'), b: z.number().describe('A 15-minute window'), c: z.number().describe('Poll every 5 min'), d: z.number().describe('Debounce of 30 ms') });`)
439451
.join() === 'unit-in-prose-not-in-name,unit-in-prose-not-in-name,unit-in-prose-not-in-name,unit-in-prose-not-in-name');
440452

453+
// The declared population must be the population the scan reads (the
454+
// ROOT_DIR_WATCH_HINTS idiom's coupling, held from this side).
455+
const repoRoot = join(pkgRoot, '..', '..');
456+
const declared = `${relative(repoRoot, SRC_ROOT).split('\\').join('/')}/**`;
457+
expect(`declared population \`${ROOT_DIR_WATCH_HINTS.join(', ')}\` is the subtree the scan walks (\`${declared}\`)`,
458+
ROOT_DIR_WATCH_HINTS.length === 1 && ROOT_DIR_WATCH_HINTS[0] === declared);
459+
441460
console.log(failures === 0 ? '\nself-test: all cases pass' : `\nself-test: ${failures} case(s) FAILED`);
442461
return failures === 0 ? 0 : 1;
443462
}

packages/spec/src/data/driver.zod.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ export const DriverOptionsSchema = lazySchema(() => z.object({
2626
*/
2727
timeoutMs: z.number().optional().describe('Operation timeout in milliseconds'),
2828
timeout: retiredKey(
29-
'`DriverOptions.timeout` was removed in @objectstack/spec 17 (#14478) — its unit (milliseconds) ' +
29+
'`DriverOptions.timeout` was removed in @objectstack/spec 17 — its unit (milliseconds) ' +
3030
'lived only in the description. Rename the key to `timeoutMs`; the value (milliseconds) is unchanged.',
3131
),
3232

packages/spec/src/data/hook.zod.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -330,7 +330,7 @@ export const HookSchema = lazySchema(() => strictObject(
330330
*/
331331
timeoutMs: z.number().optional().describe('Maximum execution time in milliseconds before the hook is aborted'),
332332
timeout: retiredKey(
333-
'`hook.timeout` was removed in @objectstack/spec 17 (#14478) — its unit (milliseconds) lived ' +
333+
'`hook.timeout` was removed in @objectstack/spec 17 — its unit (milliseconds) lived ' +
334334
'only in the description, beside a body-level `timeoutMs` and a `retryPolicy.backoffMs` that ' +
335335
'spell theirs, so the same number read as two conventions on one surface. ' +
336336
'Rename the key to `timeoutMs`; the value (milliseconds) is unchanged. ' +

packages/spec/src/kernel/metadata-loader.zod.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ export const MetadataManagerConfigSchema = lazySchema(() => z.object({
8888
*/
8989
ttlSeconds: z.number().int().min(0).default(3600).describe('Cache TTL in seconds'),
9090
ttl: retiredKey(
91-
'`cache.ttl` was removed from `MetadataManagerConfig` in @objectstack/spec 17 (#14478) — ' +
91+
'`cache.ttl` was removed from `MetadataManagerConfig` in @objectstack/spec 17 — ' +
9292
'its unit (seconds) lived only in the description, while the nested `cache.databaseLoader.ttl` ' +
9393
'spelled the same word in milliseconds, so one key name meant two magnitudes 1000× apart. ' +
9494
'Rename the key to `ttlSeconds`; the value (seconds) is unchanged.',
@@ -109,7 +109,7 @@ export const MetadataManagerConfigSchema = lazySchema(() => z.object({
109109
ttlMs: z.number().int().min(0).default(60_000).describe('Cache TTL in milliseconds'),
110110
ttl: retiredKey(
111111
'`cache.databaseLoader.ttl` was removed from `MetadataManagerConfig` in @objectstack/spec 17 ' +
112-
'(#14478) — its unit (milliseconds) lived only in the description, while the outer `cache.ttl` ' +
112+
'— its unit (milliseconds) lived only in the description, while the outer `cache.ttl` ' +
113113
'spelled the same word in seconds, so one key name meant two magnitudes 1000× apart. ' +
114114
'Rename the key to `ttlMs`; the value (milliseconds) is unchanged.',
115115
),

0 commit comments

Comments
 (0)