Skip to content

Commit 9605808

Browse files
committed
docs(spec): drop the internal issue id from the four epoch-instant tombstone prescriptions (#15676)
`check:doc-authoring` rule 3 forbids an internal issue id in customer-facing spec text (maintainer ruling 2026-08-12). The four `retiredKey()` prescriptions this stack card added each opened with a `(#14478 ruling B)` parenthetical — a citation-shaped token that resolves to nothing for the author who meets it the moment their key is refused, and which the generated reference pages publish verbatim. The parenthetical goes; the sentence keeps everything actionable it carried — the FROM to TO rename, that the value is unchanged, and the `SimplePresenceState.lastSeen` neighbour caveat — matching the shape the campaign's already-compliant tombstones use (`hook.timeout`, `job.timeout`, `DriverOptions.timeout`). The internal anchor is untouched in the adjacent `//` and `/** */` comments, which are not customer-facing and were never findings. `content/docs/references/**` regenerated with `pnpm --filter @objectstack/spec gen:docs` — no generated artifact was hand-edited. check:doc-authoring: 4 findings before, exit 0 after. check:duration-unit-keys: unmoved — 48 offender(s) among 215 duration-shaped numeric key(s), (6 declared `EpochMs` instant(s), 11 declared `externalVocabulary` mirror(s)). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G4138K1EG7kQ81FNba5Kp4
1 parent c1949b9 commit 9605808

6 files changed

Lines changed: 24 additions & 25 deletions

File tree

‎content/docs/references/api/websocket.mdx‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -363,7 +363,7 @@ Event pattern (supports wildcards like "record.*" or "*.created")
363363
| **userName** | `string` | ✅ | User display name |
364364
| **status** | `Enum<'online' \| 'away' \| 'offline'>` | ✅ | User presence status |
365365
| **lastSeenAt** | `integer` | ✅ | Unix timestamp of last activity in milliseconds |
366-
| **lastSeen** | `never` | optional | [REMOVED] `SimplePresenceState.lastSeen` was renamed to `lastSeenAt` in @objectstack/spec 17 (#14478 ruling B) — the last-activity INSTANT now carries the shared `EpochMs` schema, which declares the epoch-millisecond unit. Rename the key to `lastSeenAt`; the value is unchanged (`Date.now()`). Note the neighbouring `PresenceState.lastSeen` (api/realtime-shared.zod.ts) is a different key with a different type — an ISO-8601 datetime STRING — and is untouched. |
366+
| **lastSeen** | `never` | optional | [REMOVED] `SimplePresenceState.lastSeen` was renamed to `lastSeenAt` in @objectstack/spec 17 — the last-activity INSTANT now carries the shared `EpochMs` schema, which declares the epoch-millisecond unit. Rename the key to `lastSeenAt`; the value is unchanged (`Date.now()`). Note the neighbouring `PresenceState.lastSeen` (api/realtime-shared.zod.ts) is a different key with a different type — an ISO-8601 datetime STRING — and is untouched. |
367367
| **metadata** | `Record<string, any>` | optional | Additional presence metadata (e.g., current page, custom status) |
368368

369369

@@ -452,7 +452,7 @@ Event pattern (supports wildcards like "record.*" or "*.created")
452452
| **channel** | `string` | ✅ | Channel identifier (e.g., "record.account.123", "user.456") |
453453
| **payload** | `any` | ✅ | Event payload data |
454454
| **occurredAt** | `integer` | ✅ | Unix timestamp in milliseconds when the event occurred |
455-
| **timestamp** | `never` | optional | [REMOVED] `WebSocketEvent.timestamp` was renamed to `occurredAt` in @objectstack/spec 17 (#14478 ruling B) — the event INSTANT now carries the shared `EpochMs` schema, which declares the epoch-millisecond unit the bare key name left to the describe prose. Rename the key to `occurredAt`; the value is unchanged (`Date.now()`). |
455+
| **timestamp** | `never` | optional | [REMOVED] `WebSocketEvent.timestamp` was renamed to `occurredAt` in @objectstack/spec 17 — the event INSTANT now carries the shared `EpochMs` schema, which declares the epoch-millisecond unit the bare key name left to the describe prose. Rename the key to `occurredAt`; the value is unchanged (`Date.now()`). |
456456

457457

458458
---

‎content/docs/references/kernel/context.mdx‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ const result = KernelContextSchema.parse(data);
3636
| **startedAt** | `integer` | ✅ | Boot timestamp — Unix milliseconds |
3737
| **features** | `Record<string, boolean>` | optional (default: `{}`) | Global feature toggles |
3838
| **previewMode** | `never` | optional | [REMOVED] `context.previewMode` was removed in @objectstack/spec 17 (ADR-0049 enforce-or-remove) — nothing ever read the block: none of its six keys (`autoLogin`, `simulatedRole`, `simulatedUserName`, `readOnly`, `expiresInSeconds`, `bannerMessage`) had a consumer in any repo, so an authored block parsed cleanly and configured NOTHING, while its own docstring promised an auth bypass ("skips authentication screens", "simulates an admin identity") and named a production guard no runtime ever received. Delete the key. Preview/demo deployments belong to the deployment layer, which owns auth per-project (`ArtifactKernelFactory` in the cloud distribution); `OS_PREVIEW_MODE` stays there as a routing-only switch. If a preview experience becomes a product capability it re-declares fresh, with the production-posture hard-refusal as the first-landed half (ruling record). |
39-
| **startTime** | `never` | optional | [REMOVED] `context.startTime` was renamed to `context.startedAt` in @objectstack/spec 17 (#14478 ruling B) — the boot INSTANT now carries the shared `EpochMs` schema, which declares the epoch-millisecond unit the key name used to leave to the describe prose. Rename the key to `startedAt`; the value is unchanged (`Date.now()`). `*At` rather than `startTimeMs` deliberately: every `*Ms` key in this package is a DURATION, so spelling an instant that way would move it into the family the rule exists to separate it from. |
39+
| **startTime** | `never` | optional | [REMOVED] `context.startTime` was renamed to `context.startedAt` in @objectstack/spec 17 — the boot INSTANT now carries the shared `EpochMs` schema, which declares the epoch-millisecond unit the key name used to leave to the describe prose. Rename the key to `startedAt`; the value is unchanged (`Date.now()`). `*At` rather than `startTimeMs` deliberately: every `*Ms` key in this package is a DURATION, so spelling an instant that way would move it into the family the rule exists to separate it from. |
4040

4141

4242
---
@@ -72,7 +72,7 @@ Tenant-aware kernel runtime context
7272
| **startedAt** | `integer` | ✅ | Boot timestamp — Unix milliseconds |
7373
| **features** | `Record<string, boolean>` | optional (default: `{}`) | Global feature toggles |
7474
| **previewMode** | `never` | optional | [REMOVED] `context.previewMode` was removed in @objectstack/spec 17 (ADR-0049 enforce-or-remove) — nothing ever read the block: none of its six keys (`autoLogin`, `simulatedRole`, `simulatedUserName`, `readOnly`, `expiresInSeconds`, `bannerMessage`) had a consumer in any repo, so an authored block parsed cleanly and configured NOTHING, while its own docstring promised an auth bypass ("skips authentication screens", "simulates an admin identity") and named a production guard no runtime ever received. Delete the key. Preview/demo deployments belong to the deployment layer, which owns auth per-project (`ArtifactKernelFactory` in the cloud distribution); `OS_PREVIEW_MODE` stays there as a routing-only switch. If a preview experience becomes a product capability it re-declares fresh, with the production-posture hard-refusal as the first-landed half (ruling record). |
75-
| **startTime** | `never` | optional | [REMOVED] `context.startTime` was renamed to `context.startedAt` in @objectstack/spec 17 (#14478 ruling B) — the boot INSTANT now carries the shared `EpochMs` schema, which declares the epoch-millisecond unit the key name used to leave to the describe prose. Rename the key to `startedAt`; the value is unchanged (`Date.now()`). `*At` rather than `startTimeMs` deliberately: every `*Ms` key in this package is a DURATION, so spelling an instant that way would move it into the family the rule exists to separate it from. |
75+
| **startTime** | `never` | optional | [REMOVED] `context.startTime` was renamed to `context.startedAt` in @objectstack/spec 17 — the boot INSTANT now carries the shared `EpochMs` schema, which declares the epoch-millisecond unit the key name used to leave to the describe prose. Rename the key to `startedAt`; the value is unchanged (`Date.now()`). `*At` rather than `startTimeMs` deliberately: every `*Ms` key in this package is a DURATION, so spelling an instant that way would move it into the family the rule exists to separate it from. |
7676
| **tenantId** | `string` | ✅ | Resolved tenant identifier |
7777
| **tenantPlan** | `Enum<'free' \| 'pro' \| 'enterprise'>` | ✅ | Tenant subscription plan |
7878
| **tenantRegion** | `string` | optional | Tenant deployment region |

‎content/docs/references/kernel/startup-orchestrator.mdx‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ const result = HealthStatusSchema.parse(data);
3737
| :--- | :--- | :--- | :--- |
3838
| **healthy** | `boolean` | ✅ | Whether the plugin is healthy |
3939
| **checkedAt** | `integer` | ✅ | Unix timestamp in milliseconds when health check was performed |
40-
| **timestamp** | `never` | optional | [REMOVED] `HealthStatus.timestamp` was renamed to `checkedAt` in @objectstack/spec 17 (#14478 ruling B) — the instant the check RAN now carries the shared `EpochMs` schema, which declares the epoch-millisecond unit the bare key name left to the describe prose. Rename the key to `checkedAt`; the value is unchanged (`Date.now()`). |
40+
| **timestamp** | `never` | optional | [REMOVED] `HealthStatus.timestamp` was renamed to `checkedAt` in @objectstack/spec 17 — the instant the check RAN now carries the shared `EpochMs` schema, which declares the epoch-millisecond unit the bare key name left to the describe prose. Rename the key to `checkedAt`; the value is unchanged (`Date.now()`). |
4141
| **details** | `Record<string, any>` | optional | Optional plugin-specific health details |
4242
| **message** | `string` | optional | Error message if plugin is unhealthy |
4343

@@ -71,7 +71,7 @@ const result = HealthStatusSchema.parse(data);
7171
| :--- | :--- | :--- | :--- |
7272
| **healthy** | `boolean` | ✅ | Whether the plugin is healthy |
7373
| **checkedAt** | `integer` | ✅ | Unix timestamp in milliseconds when health check was performed |
74-
| **timestamp** | `never` | optional | [REMOVED] `HealthStatus.timestamp` was renamed to `checkedAt` in @objectstack/spec 17 (#14478 ruling B) — the instant the check RAN now carries the shared `EpochMs` schema, which declares the epoch-millisecond unit the bare key name left to the describe prose. Rename the key to `checkedAt`; the value is unchanged (`Date.now()`). |
74+
| **timestamp** | `never` | optional | [REMOVED] `HealthStatus.timestamp` was renamed to `checkedAt` in @objectstack/spec 17 — the instant the check RAN now carries the shared `EpochMs` schema, which declares the epoch-millisecond unit the bare key name left to the describe prose. Rename the key to `checkedAt`; the value is unchanged (`Date.now()`). |
7575
| **details** | `Record<string, any>` | optional | Optional plugin-specific health details |
7676
| **message** | `string` | optional | Error message if plugin is unhealthy |
7777

‎packages/spec/src/api/websocket.zod.ts‎

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -480,10 +480,10 @@ export const WebSocketEventSchema = lazySchema(() => z.object({
480480

481481
/** Tombstone for the rename above (#15676, ruling B on #14478). */
482482
timestamp: retiredKey(
483-
'`WebSocketEvent.timestamp` was renamed to `occurredAt` in @objectstack/spec 17 '
484-
+ '(#14478 ruling B) — the event INSTANT now carries the shared `EpochMs` schema, '
485-
+ 'which declares the epoch-millisecond unit the bare key name left to the describe '
486-
+ 'prose. Rename the key to `occurredAt`; the value is unchanged (`Date.now()`).',
483+
'`WebSocketEvent.timestamp` was renamed to `occurredAt` in @objectstack/spec 17 — the '
484+
+ 'event INSTANT now carries the shared `EpochMs` schema, which declares the '
485+
+ 'epoch-millisecond unit the bare key name left to the describe prose. Rename the key '
486+
+ 'to `occurredAt`; the value is unchanged (`Date.now()`).',
487487
),
488488
}));
489489

@@ -519,12 +519,12 @@ export const SimplePresenceStateSchema = lazySchema(() => z.object({
519519

520520
/** Tombstone for the rename above (#15676, ruling B on #14478). */
521521
lastSeen: retiredKey(
522-
'`SimplePresenceState.lastSeen` was renamed to `lastSeenAt` in @objectstack/spec 17 '
523-
+ '(#14478 ruling B) — the last-activity INSTANT now carries the shared `EpochMs` '
524-
+ 'schema, which declares the epoch-millisecond unit. Rename the key to `lastSeenAt`; '
525-
+ 'the value is unchanged (`Date.now()`). Note the neighbouring '
526-
+ '`PresenceState.lastSeen` (api/realtime-shared.zod.ts) is a different key with a '
527-
+ 'different type — an ISO-8601 datetime STRING — and is untouched.',
522+
'`SimplePresenceState.lastSeen` was renamed to `lastSeenAt` in @objectstack/spec 17 — '
523+
+ 'the last-activity INSTANT now carries the shared `EpochMs` schema, which declares '
524+
+ 'the epoch-millisecond unit. Rename the key to `lastSeenAt`; the value is unchanged '
525+
+ '(`Date.now()`). Note the neighbouring `PresenceState.lastSeen` '
526+
+ '(api/realtime-shared.zod.ts) is a different key with a different type — an '
527+
+ 'ISO-8601 datetime STRING — and is untouched.',
528528
),
529529
metadata: z.record(z.string(), z.unknown()).optional().describe('Additional presence metadata (e.g., current page, custom status)'),
530530
}));

‎packages/spec/src/kernel/context.zod.ts‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,10 @@ const RUNTIME_MODE_PREVIEW_RETIRED =
2929
+ 'experience becomes a product capability it re-declares fresh, with the '
3030
+ 'production-posture hard-refusal as the first-landed half.';
3131
const START_TIME_RENAMED =
32-
'`context.startTime` was renamed to `context.startedAt` in @objectstack/spec 17 '
33-
+ '(#14478 ruling B) — the boot INSTANT now carries the shared `EpochMs` schema, which '
34-
+ 'declares the epoch-millisecond unit the key name used to leave to the describe prose. '
35-
+ 'Rename the key to `startedAt`; the value is unchanged (`Date.now()`). `*At` rather '
32+
'`context.startTime` was renamed to `context.startedAt` in @objectstack/spec 17 — the '
33+
+ 'boot INSTANT now carries the shared `EpochMs` schema, which declares the '
34+
+ 'epoch-millisecond unit the key name used to leave to the describe prose. Rename the '
35+
+ 'key to `startedAt`; the value is unchanged (`Date.now()`). `*At` rather '
3636
+ 'than `startTimeMs` deliberately: every `*Ms` key in this package is a DURATION, so '
3737
+ 'spelling an instant that way would move it into the family the rule exists to '
3838
+ 'separate it from.';

‎packages/spec/src/kernel/startup-orchestrator.zod.ts‎

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -103,11 +103,10 @@ export const HealthStatusSchema = lazySchema(() => z.object({
103103

104104
/** Tombstone for the rename above (#15676, ruling B on #14478). */
105105
timestamp: retiredKey(
106-
'`HealthStatus.timestamp` was renamed to `checkedAt` in @objectstack/spec 17 '
107-
+ '(#14478 ruling B) — the instant the check RAN now carries the shared `EpochMs` '
108-
+ 'schema, which declares the epoch-millisecond unit the bare key name left to the '
109-
+ 'describe prose. Rename the key to `checkedAt`; the value is unchanged '
110-
+ '(`Date.now()`).',
106+
'`HealthStatus.timestamp` was renamed to `checkedAt` in @objectstack/spec 17 — the '
107+
+ 'instant the check RAN now carries the shared `EpochMs` schema, which declares the '
108+
+ 'epoch-millisecond unit the bare key name left to the describe prose. Rename the key '
109+
+ 'to `checkedAt`; the value is unchanged (`Date.now()`).',
111110
),
112111

113112
/**

0 commit comments

Comments
 (0)