Skip to content

Commit 0c2334f

Browse files
authored
feat(spec): retire preview mode — the RuntimeMode 'preview' value and the whole PreviewModeConfig block (#12718)
1 parent ac35e2a commit 0c2334f

18 files changed

Lines changed: 646 additions & 227 deletions

File tree

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
---
2+
"@objectstack/spec": minor
3+
---
4+
5+
feat(spec): retire preview mode — the `'preview'` RuntimeMode value and the whole `KernelContext.previewMode` / `PreviewModeConfig` block (#11846, ADR-0049)
6+
7+
<!-- adr-0087: registered kernel-context-preview-mode-retired -->
8+
9+
**BREAKING** accept-set narrowing and export removal, landing after the
10+
v17.0.0 cut (the lockstep launch-window convention ships it as `minor`; the
11+
prescription is registered under protocol major 18 —
12+
`RETIRED_KEYS_BY_MAJOR[18]` for both walked-shape keys,
13+
`RETIRED_DEFS_BY_MAJOR[18]` for the def, plus the D3 semantic entry
14+
`kernel-context-preview-mode-retired` — where `os migrate meta` users will
15+
look).
16+
17+
The declaration was the sharpest declared-≠-enforced shape on a SECURITY
18+
surface: the schema promised "bypass auth, simulate admin identity" and named
19+
a production guard "the runtime must enforce", and NO code path implemented
20+
either half. Measured zero consumers in objectstack, objectui and cloud
21+
(cloud#1651, closed 2026-08-26 with positive controls: `RuntimeMode` has zero
22+
hits repo-wide there, and `ArtifactKernelFactory` — where preview auto-login
23+
would live if anywhere — has 20+ hits and never touches `previewMode`;
24+
re-verified in objectstack at dispatch, 2026-08-27). An author — very often
25+
an AI — could write the six-key block per the reference docs, parse cleanly,
26+
and get no behaviour and no diagnostic.
27+
28+
FROM → TO:
29+
30+
- `mode: 'preview'` → *(removed value)* — `mode` defaults to `'production'`;
31+
use `'development'` for local demo work. The rejection carries the
32+
prescription via the enum's own error map (the `HookBodyCapability`
33+
precedent); every other mode keeps zod's own message.
34+
- `previewMode: { … }` on `KernelContext` / `TenantRuntimeContext` →
35+
*(removed key)* — tombstoned with `retiredKey()` (the schemas are not
36+
`.strict()`, so a bare deletion would be a silent strip): authoring it is
37+
now a `tsc` error and a parse error carrying the prescription.
38+
- `PreviewModeConfigSchema` / `PreviewModeConfig` / `PreviewModeConfigParsed`
39+
→ *(removed — no replacement)*. The def described behaviour no layer
40+
implemented; an exported value schema with no consumer reads as a
41+
capability (#3950).
42+
43+
One-line fix: delete the key and the value — neither ever changed runtime
44+
behaviour, so removing them changes nothing observable. Preview deployment
45+
ROUTING is untouched: `OS_PREVIEW_MODE` / `OS_PREVIEW_BASE_DOMAINS` keep
46+
working exactly as documented (deployment routing, never identity). If a
47+
preview experience becomes a product capability it re-declares fresh, with
48+
the production-posture hard-refusal as the first-landed half (#11846 ruling
49+
record, maintainer 2026-08-27).
50+
51+
The retirement kit:
52+
53+
- tombstones at both declarations (`kernel/KernelContext:previewMode` and the
54+
`.extend()` copy `kernel/TenantRuntimeContext:previewMode`, both in
55+
`RETIRED_KEYS_BY_MAJOR[18]`); the enum value's prescription on
56+
`RuntimeMode`'s error map (enum-VALUE retirements register nothing in
57+
RETIRED_KEYS_BY_MAJOR and leave the surface ratchets byte-identical)
58+
- whole-def deletion `kernel/PreviewModeConfig` in `RETIRED_DEFS_BY_MAJOR[18]`
59+
(manifest key deliberately removed; the #4725 gate adjudicated it)
60+
- deliberately NO D2 conversion: a kernel context is constructed by host code
61+
at boot — not a stack collection member, never a `sys_metadata` row — so
62+
the conversion chain has no seam that would ever see one (the
63+
`kernel/Manifest:loading` disposition); the D3 semantic entry carries the
64+
prescription
65+
- pin tests (`kernel/preview-mode-retirement.test.ts`): both rejection sites
66+
flip from silent parse to the prescription; zero holders for all 3 retired
67+
export names on every public entry; the carrier schemas survive

‎content/docs/references/index.mdx‎

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
title: Protocol Reference
3-
description: Every schema published by @objectstack/spec — 1605 schemas across 14 protocol modules
3+
description: Every schema published by @objectstack/spec — 1604 schemas across 14 protocol modules
44
---
55

66
{/* ⚠️ AUTO-GENERATED — DO NOT EDIT. Run build-docs.ts to regenerate. Hand-written docs live in the module folders under content/docs/. */}
@@ -26,14 +26,14 @@ counts are sums of the rows they head. Regenerate with
2626
| [Data Protocol](/docs/references/data) | 29 | 166 | Objects, fields, queries, filters, datasources and drivers — the ObjectQL layer. |
2727
| [Identity Protocol](/docs/references/identity) | 5 | 27 | Users and accounts, organizations, positions, SCIM provisioning. |
2828
| [Integration Protocol](/docs/references/integration) | 1 | 27 | The single connector protocol (ADR-0097) — catalog descriptors and provider-bound instances. |
29-
| [Kernel Protocol](/docs/references/kernel) | 31 | 171 | Plugin lifecycle and manifests, capabilities and security, metadata loading, service registry. |
29+
| [Kernel Protocol](/docs/references/kernel) | 31 | 170 | Plugin lifecycle and manifests, capabilities and security, metadata loading, service registry. |
3030
| [QA Protocol](/docs/references/qa) | 1 | 8 | Declarative test suites — scenarios, steps, actions and assertions. |
3131
| [Security Protocol](/docs/references/security) | 5 | 29 | Permission sets, row-level security, sharing rules, tenancy posture. |
3232
| [Shared Protocol](/docs/references/shared) | 8 | 32 | Primitives used across every protocol — identifiers, HTTP, expressions, error maps, enums. |
3333
| [Studio Protocol](/docs/references/studio) | 3 | 35 | Studio designer metadata — the authoring surfaces for the protocols above. |
3434
| [System Protocol](/docs/references/system) | 36 | 291 | The runtime environment — logging, jobs, cache, metrics, notifications, i18n and compliance. |
3535
| [UI Protocol](/docs/references/ui) | 16 | 152 | Apps, pages, views, dashboards, reports, actions and themes — the ObjectUI layer. |
36-
| **Total** | **201** | **1605** | 14 protocol modules |
36+
| **Total** | **201** | **1604** | 14 protocol modules |
3737

3838
---
3939

@@ -217,15 +217,15 @@ The single connector protocol (ADR-0097) — catalog descriptors and provider-bo
217217

218218
## Kernel Protocol
219219

220-
**Source:** `packages/spec/src/kernel/` · **Import:** `@objectstack/spec/kernel` · **31 pages, 171 schemas**
220+
**Source:** `packages/spec/src/kernel/` · **Import:** `@objectstack/spec/kernel` · **31 pages, 170 schemas**
221221

222222
Plugin lifecycle and manifests, capabilities and security, metadata loading, service registry.
223223

224224
| File | Schemas |
225225
| :--- | :--- |
226226
| [`cli-extension.zod.ts`](/docs/references/kernel/cli-extension) | `OclifPluginConfig` |
227227
| [`cluster.zod.ts`](/docs/references/kernel/cluster) | `ClusterCapabilityConfig`, `ClusterDriver`, `ClusterTenantIsolation`, `EventClusterOptions`, `EventDeliverySemantics`, `EventScope`, `MetadataChangeOperation`, `ServiceClusterAnnotations`, `ServiceClusterScope`, `ServiceLeaderStrategy` |
228-
| [`context.zod.ts`](/docs/references/kernel/context) | `KernelContext`, `PreviewModeConfig`, `RuntimeMode`, `TenantRuntimeContext` |
228+
| [`context.zod.ts`](/docs/references/kernel/context) | `KernelContext`, `RuntimeMode`, `TenantRuntimeContext` |
229229
| [`dependency-resolution.zod.ts`](/docs/references/kernel/dependency-resolution) | `DependencyResolutionResult`, `DependencyStatusEnum`, `RequiredAction`, `ResolvedDependency` |
230230
| [`events/bus.zod.ts`](/docs/references/kernel/events-bus) | `EventBusConfig` |
231231
| [`events/core.zod.ts`](/docs/references/kernel/events-core) | `Event`, `EventMetadata`, `EventPriority`, `EventTypeDefinition` |

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

Lines changed: 6 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,15 @@ description: Context protocol schemas
55

66
{/* ⚠️ AUTO-GENERATED — DO NOT EDIT. Run build-docs.ts to regenerate. Hand-written docs live in the module folders under content/docs/. */}
77

8-
Runtime Mode Enum
9-
Defines the operating mode of the kernel
10-
118
<Callout type="info">
129
**Source:** `packages/spec/src/kernel/context.zod.ts`
1310
</Callout>
1411

1512
## TypeScript Usage
1613

1714
```typescript
18-
import { KernelContextSchema, PreviewModeConfigSchema, RuntimeMode, TenantRuntimeContextSchema } from '@objectstack/spec/kernel';
19-
import type { KernelContext, PreviewModeConfig, RuntimeMode, TenantRuntimeContext } from '@objectstack/spec/kernel';
15+
import { KernelContextSchema, RuntimeMode, TenantRuntimeContextSchema } from '@objectstack/spec/kernel';
16+
import type { KernelContext, RuntimeMode, TenantRuntimeContext } from '@objectstack/spec/kernel';
2017

2118
// Validate data
2219
const result = KernelContextSchema.parse(data);
@@ -31,41 +28,14 @@ const result = KernelContextSchema.parse(data);
3128
| Property | Type | Required | Description |
3229
| :--- | :--- | :--- | :--- |
3330
| **instanceId** | `string` | ✅ | Unique UUID for this running kernel process |
34-
| **mode** | `Enum<'development' \| 'production' \| 'test' \| 'provisioning' \| 'preview'>` | optional (default: `"production"`) | Kernel operating mode |
31+
| **mode** | `Enum<'development' \| 'production' \| 'test' \| 'provisioning'>` | optional (default: `"production"`) | Kernel operating mode |
3532
| **version** | `string` | ✅ | Kernel version |
3633
| **appName** | `string` | optional | Host application name |
3734
| **cwd** | `string` | ✅ | Current working directory |
3835
| **workspaceRoot** | `string` | optional | Workspace root if different from cwd |
3936
| **startTime** | `integer` | ✅ | Boot timestamp (ms) |
4037
| **features** | `Record<string, boolean>` | optional (default: `{}`) | Global feature toggles |
41-
| **previewMode** | `{ autoLogin: boolean; simulatedRole: Enum<'admin' \| 'user' \| 'viewer'>; simulatedUserName: string; readOnly: boolean; … }` | optional | Preview/demo mode configuration (used when mode is "preview") |
42-
43-
### Nested Shape: `KernelContext.previewMode`
44-
45-
| Property | Type | Required | Description |
46-
| :--- | :--- | :--- | :--- |
47-
| **autoLogin** | `boolean` | optional (default: `true`) | Auto-login as simulated user, skipping login/registration pages |
48-
| **simulatedRole** | `Enum<'admin' \| 'user' \| 'viewer'>` | optional (default: `"admin"`) | Permission role for the simulated preview user |
49-
| **simulatedUserName** | `string` | optional (default: `"Preview User"`) | Display name for the simulated preview user |
50-
| **readOnly** | `boolean` | optional (default: `false`) | Restrict the preview session to read-only operations |
51-
| **expiresInSeconds** | `integer` | optional (default: `0`) | Preview session duration in seconds (0 = no expiration) |
52-
| **bannerMessage** | `string` | optional | Banner message displayed in the UI during preview mode |
53-
54-
55-
---
56-
57-
## PreviewModeConfig
58-
59-
### Properties
60-
61-
| Property | Type | Required | Description |
62-
| :--- | :--- | :--- | :--- |
63-
| **autoLogin** | `boolean` | optional (default: `true`) | Auto-login as simulated user, skipping login/registration pages |
64-
| **simulatedRole** | `Enum<'admin' \| 'user' \| 'viewer'>` | optional (default: `"admin"`) | Permission role for the simulated preview user |
65-
| **simulatedUserName** | `string` | optional (default: `"Preview User"`) | Display name for the simulated preview user |
66-
| **readOnly** | `boolean` | optional (default: `false`) | Restrict the preview session to read-only operations |
67-
| **expiresInSeconds** | `integer` | optional (default: `0`) | Preview session duration in seconds (0 = no expiration) |
68-
| **bannerMessage** | `string` | optional | Banner message displayed in the UI during preview mode |
38+
| **previewMode** | `never` | optional | [REMOVED] `context.previewMode` was removed in @objectstack/spec 17 (#11846, 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 (#11846 ruling record). |
6939

7040

7141
---
@@ -80,7 +50,6 @@ Kernel operating mode
8050
* `production`
8151
* `test`
8252
* `provisioning`
83-
* `preview`
8453

8554

8655
---
@@ -94,31 +63,20 @@ Tenant-aware kernel runtime context
9463
| Property | Type | Required | Description |
9564
| :--- | :--- | :--- | :--- |
9665
| **instanceId** | `string` | ✅ | Unique UUID for this running kernel process |
97-
| **mode** | `Enum<'development' \| 'production' \| 'test' \| 'provisioning' \| 'preview'>` | optional (default: `"production"`) | Kernel operating mode |
66+
| **mode** | `Enum<'development' \| 'production' \| 'test' \| 'provisioning'>` | optional (default: `"production"`) | Kernel operating mode |
9867
| **version** | `string` | ✅ | Kernel version |
9968
| **appName** | `string` | optional | Host application name |
10069
| **cwd** | `string` | ✅ | Current working directory |
10170
| **workspaceRoot** | `string` | optional | Workspace root if different from cwd |
10271
| **startTime** | `integer` | ✅ | Boot timestamp (ms) |
10372
| **features** | `Record<string, boolean>` | optional (default: `{}`) | Global feature toggles |
104-
| **previewMode** | `{ autoLogin: boolean; simulatedRole: Enum<'admin' \| 'user' \| 'viewer'>; simulatedUserName: string; readOnly: boolean; … }` | optional | Preview/demo mode configuration (used when mode is "preview") |
73+
| **previewMode** | `never` | optional | [REMOVED] `context.previewMode` was removed in @objectstack/spec 17 (#11846, 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 (#11846 ruling record). |
10574
| **tenantId** | `string` | ✅ | Resolved tenant identifier |
10675
| **tenantPlan** | `Enum<'free' \| 'pro' \| 'enterprise'>` | ✅ | Tenant subscription plan |
10776
| **tenantRegion** | `string` | optional | Tenant deployment region |
10877
| **tenantDbUrl** | `string` | ✅ | Tenant database connection URL |
10978
| **tenantQuotas** | `{ maxUsers?: integer; maxStorage?: integer; apiRateLimit?: integer; maxObjects?: integer; … }` | optional | Tenant resource quotas |
11079

111-
### Nested Shape: `TenantRuntimeContext.previewMode`
112-
113-
| Property | Type | Required | Description |
114-
| :--- | :--- | :--- | :--- |
115-
| **autoLogin** | `boolean` | optional (default: `true`) | Auto-login as simulated user, skipping login/registration pages |
116-
| **simulatedRole** | `Enum<'admin' \| 'user' \| 'viewer'>` | optional (default: `"admin"`) | Permission role for the simulated preview user |
117-
| **simulatedUserName** | `string` | optional (default: `"Preview User"`) | Display name for the simulated preview user |
118-
| **readOnly** | `boolean` | optional (default: `false`) | Restrict the preview session to read-only operations |
119-
| **expiresInSeconds** | `integer` | optional (default: `0`) | Preview session duration in seconds (0 = no expiration) |
120-
| **bannerMessage** | `string` | optional | Banner message displayed in the UI during preview mode |
121-
12280
### Nested Shape: `TenantRuntimeContext.tenantQuotas`
12381

12482
| Property | Type | Required | Description |

‎docs/audits/2026-07-unknown-key-strictness-ledger.counts.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,7 @@ directory rather than per file.
261261
| `cloud/` | 83 |
262262
| `identity/` | 32 |
263263
| `integration/` | 10 |
264-
| `kernel/` | 272 |
264+
| `kernel/` | 271 |
265265
| `qa/` | 6 |
266266
| `shared/` | 20 |
267267
| `system/` | 364 |

‎packages/spec/api-surface/kernel.json‎

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -356,9 +356,6 @@
356356
"PluginVersionMetadata (type)",
357357
"PluginVersionMetadataParsed (type)",
358358
"PluginVersionMetadataSchema (const)",
359-
"PreviewModeConfig (type)",
360-
"PreviewModeConfigParsed (type)",
361-
"PreviewModeConfigSchema (const)",
362359
"ProtocolFeature (type)",
363360
"ProtocolFeatureParsed (type)",
364361
"ProtocolFeatureSchema (const)",

‎packages/spec/authorable-defaults/kernel.json‎

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -119,11 +119,6 @@
119119
"kernel/PluginTrustScore:badges = []",
120120
"kernel/PluginVendor:trustLevel = \"unverified\"",
121121
"kernel/PluginVendor:verified = false",
122-
"kernel/PreviewModeConfig:autoLogin = true",
123-
"kernel/PreviewModeConfig:expiresInSeconds = 0",
124-
"kernel/PreviewModeConfig:readOnly = false",
125-
"kernel/PreviewModeConfig:simulatedRole = \"admin\"",
126-
"kernel/PreviewModeConfig:simulatedUserName = \"Preview User\"",
127122
"kernel/ProtocolFeature:enabled = true",
128123
"kernel/RealTimeNotificationConfig:enabled = true",
129124
"kernel/RealTimeNotificationConfig:eventPattern = \"*\"",

‎packages/spec/authorable-surface/kernel.json‎

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,7 @@
250250
"kernel/KernelContext:features",
251251
"kernel/KernelContext:instanceId",
252252
"kernel/KernelContext:mode",
253-
"kernel/KernelContext:previewMode",
253+
"kernel/KernelContext:previewMode [RETIRED]",
254254
"kernel/KernelContext:startTime",
255255
"kernel/KernelContext:version",
256256
"kernel/KernelContext:workspaceRoot",
@@ -654,12 +654,6 @@
654654
"kernel/PluginVersionMetadata:support",
655655
"kernel/PluginVersionMetadata:version",
656656
"kernel/PluginVersionMetadata:versionString",
657-
"kernel/PreviewModeConfig:autoLogin",
658-
"kernel/PreviewModeConfig:bannerMessage",
659-
"kernel/PreviewModeConfig:expiresInSeconds",
660-
"kernel/PreviewModeConfig:readOnly",
661-
"kernel/PreviewModeConfig:simulatedRole",
662-
"kernel/PreviewModeConfig:simulatedUserName",
663657
"kernel/ProtocolFeature:deprecatedSince",
664658
"kernel/ProtocolFeature:description",
665659
"kernel/ProtocolFeature:enabled",
@@ -801,7 +795,7 @@
801795
"kernel/TenantRuntimeContext:features",
802796
"kernel/TenantRuntimeContext:instanceId",
803797
"kernel/TenantRuntimeContext:mode",
804-
"kernel/TenantRuntimeContext:previewMode",
798+
"kernel/TenantRuntimeContext:previewMode [RETIRED]",
805799
"kernel/TenantRuntimeContext:startTime",
806800
"kernel/TenantRuntimeContext:tenantDbUrl",
807801
"kernel/TenantRuntimeContext:tenantId",

‎packages/spec/export-origins/kernel.json‎

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -356,9 +356,6 @@
356356
"PluginVersionMetadata": "src/kernel/plugin-versioning.zod.ts#PluginVersionMetadata (type)",
357357
"PluginVersionMetadataParsed": "src/kernel/plugin-versioning.zod.ts#PluginVersionMetadataParsed (type)",
358358
"PluginVersionMetadataSchema": "src/kernel/plugin-versioning.zod.ts#PluginVersionMetadataSchema (const)",
359-
"PreviewModeConfig": "src/kernel/context.zod.ts#PreviewModeConfig (type)",
360-
"PreviewModeConfigParsed": "src/kernel/context.zod.ts#PreviewModeConfigParsed (type)",
361-
"PreviewModeConfigSchema": "src/kernel/context.zod.ts#PreviewModeConfigSchema (const)",
362359
"ProtocolFeature": "src/kernel/plugin-capability.zod.ts#ProtocolFeature (type)",
363360
"ProtocolFeatureParsed": "src/kernel/plugin-capability.zod.ts#ProtocolFeatureParsed (type)",
364361
"ProtocolFeatureSchema": "src/kernel/plugin-capability.zod.ts#ProtocolFeatureSchema (const)",

‎packages/spec/json-schema.manifest/kernel.json‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,6 @@
128128
"kernel/PluginTrustScore",
129129
"kernel/PluginVendor",
130130
"kernel/PluginVersionMetadata",
131-
"kernel/PreviewModeConfig",
132131
"kernel/ProtocolFeature",
133132
"kernel/ProtocolReference",
134133
"kernel/ProtocolVersion",

0 commit comments

Comments
 (0)