Skip to content

feat(spec): retire preview mode — the RuntimeMode 'preview' value and the whole PreviewModeConfig block - #12718

Merged
os-zhuang merged 5 commits into
mainfrom
claude/issue-11846-retire-preview-mode
Aug 28, 2026
Merged

feat(spec): retire preview mode — the RuntimeMode 'preview' value and the whole PreviewModeConfig block#12718
os-zhuang merged 5 commits into
mainfrom
claude/issue-11846-retire-preview-mode

Conversation

@os-sales

Copy link
Copy Markdown
Collaborator

Fixes #11846

Executes the maintainer ruling of 2026-08-27 (decision-inbox batch 2, Option A: remove, recorded in comment 5434619628 on the card): the preview-mode declaration retires under ADR-0049 enforce-or-remove via the spec-property-retirement route. Dev session session_01JvjTCjJQn9zSTXEhUKgT7s, branch claude/issue-11846-retire-preview-mode, base 15bf9e8.

Day-of re-verification (dispatch constraint — zero-consumer readings, 2026-08-27 at origin/main 15bf9e8)

  • Declarations present exactly as premised: RuntimeMode enum at packages/spec/src/kernel/context.zod.ts line 11, previewMode at line 133.
  • In-repo consumers: the only hit for the string "mode === 'preview'" under packages/ is a comment in packages/runtime/src/domains/meta-read-principal-filtering.measurement.test.ts; the only non-spec file referencing RuntimeMode or the KernelContext type is that same measurement test. KernelContextSchema is parsed only by its own unit test.
  • The previewMode hits outside spec are the routing-only local variable in packages/cli/src/commands/serve.ts (reads OS_PREVIEW_MODE, stays) and prose comments in two test files.
  • cloud leg: the cloud#1651 reading of 2026-08-26 stands as ruled (not re-measurable from this repo); objectui leg: the card-body measurement stands.
  • Baselines carried 6 kernel/PreviewModeConfig hits in authorable-surface.base.json and 5 in authorable-defaults/kernel.json; nothing in packages/spec/src/migrations/ mentioned the block. All PM mechanism assumptions held; no live consumer found — no stop condition hit.

What changed

Three retirement shapes, each with its own bookkeeping (the routes the gen:schema gate (a) remedy text and the retirement playbook prescribe):

  1. Enum value narrowing'preview' leaves RuntimeMode. Prescription on the enum's own error map, dispatched by the offending input (the HookBodyCapability / crypto.hash precedent): only the value that used to be legal gets the retirement message; a typo keeps zod's own message. Registers nothing in RETIRED_KEYS_BY_MAJOR by itself.
  2. Key tombstonepreviewMode becomes retiredKey(...) on the non-strict KernelContextSchema (a bare deletion would be a silent strip, ADR-0104). The TenantRuntimeContextSchema extend copy inherits it. Both walked-shape keys registered: kernel/KernelContext:previewMode and kernel/TenantRuntimeContext:previewMode in RETIRED_KEYS_BY_MAJOR[18] (entry files under src/migrations/entries/retired-keys/).
  3. Whole-def removalPreviewModeConfigSchema and its two types are orphan value schema once the carrier key retires; the def leaves the emitted set. kernel/PreviewModeConfig registered in RETIRED_DEFS_BY_MAJOR[18]; the manifest key deliberately deleted from json-schema.manifest/kernel.json. Route evidence from the build itself: the manifest deletion gate first refused the build with "1 previously published schema(s) disappeared from this build", then after the declared removal reported "1 schema(s) left the published set since 15bf9e8, each declared (json-schema.manifest.json 的「deliberate removal」删行仍是纪律而非门禁 —— #4650 的同类洞,上移一层(整 schema 级) #4725)" and "kernel/PreviewModeConfig:* (6 line(s)) — def no longer emitted by this build" as the authorable-surface 的 tombstone 门禁可被手编基线绕过 —— 删掉基线行就删掉了证据(#4638 / #4643 已两次这样过绿) #4650 proof for the six deleted baseline lines.

Deliberately no D2 conversion: a kernel context is constructed by host code at boot — it is not a stack collection member and is never stored as a sys_metadata row, so the conversion chain has no seam that would ever see one (the kernel/Manifest:loading disposition). The D3 semantic entry kernel-context-preview-mode-retired carries the full prescription, including the recorded fallback (a future preview capability re-declares fresh, production hard-refusal first). Consequently neither tombstone guidance string carries the os migrate meta sentence (that sentence is owed only where a conversion covers the surface — the migrate-sentence pin holds).

Registered under major 18, not 17: v17.0.0 was cut before this landed, so the removal ships on the 17.x line (launch-window convention) and the prescription lives at the major boundary where migrate-meta users look. This is also why check:spec-changes and check:upgrade-guide are green with no artifact change — build-spec-changes.ts projects majors only up to the current protocol major (17); the sibling major-18 entries behave identically.

Docs: content/docs/references/kernel/context.mdx is generated and was regenerated (mode enum narrowed in both tables, the key now shows the REMOVED prescription, the nested block sections are gone). content/docs/deployment/environment-variables.mdx was measured, not assumed: its OS_PREVIEW_MODE row reads "Preview deployment routing mode" — routing-only, no line ties preview mode to auth/identity — so it is untouched, as is the serve.ts trusted-origin consumer. OS_PREVIEW_MODE is NOT retired.

Rejection pins — both directions (required by the dispatch)

packages/spec/src/kernel/preview-mode-retirement.test.ts pins: authored mode: 'preview' and an authored previewMode block each flip from silent parse to a loud, guidance-carrying rejection (issue code, path naming the site, and the prescription text pinned — including the routing-only OS_PREVIEW_MODE clause and the production-hard-refusal fallback); the extend copy rejects too; absence still parses with no phantom property; all three removed export names have zero holders on every public entry (export-origins testkit) while the carrier schemas survive; the enum options list is pinned exactly.

Verification record (all heavy commands via scripts/pm/os-verify-lock.sh; exit codes captured before any pipe)

At final commit d6551e4 (cited from the runs themselves):

  • pnpm --filter @objectstack/spec build — green after the declared manifest deletion; gate output quoted above is the route evidence.
  • pnpm --filter @objectstack/spec test — "Test Files 436 passed (436), Tests 11543 passed (11543)".
  • Targeted: vitest run src/kernel/preview-mode-retirement.test.ts src/kernel/context.test.ts — "Test Files 2 passed (2), Tests 28 passed (28)".
  • pnpm --filter @objectstack/spec typecheck — green, including "check:test-typecheck: OK"; tsc --listFiles counts both touched test files in the tsconfig.test.json population (2 hits), so the typecheck reading covers the new tests rather than excluding them.
  • Reverse verification, parse direction (fix committed first; restore via trap plus explicit checkout of HEAD): reverting only context.zod.ts to base 15bf9e8 (mutation proven on disk by anchored grep: old enum comment present 1, tombstone present 0) turned the pins RED — 7 failed (the 6 predeclared rejection/options pins plus the barrel-import pin, red because the old schema re-exports the retired name; the export-origins-artifact pin stayed green as predicted, since it reads the regenerated artifact). Restore proven byte-identical: git diff against HEAD empty and worktree blob hash equals the HEAD blob hash.
  • Reverse verification, type direction (proves the rebuilt d.ts, not a cached one): a scratch consumer compiled against dist/kernel/index.d.mts — the negative leg fails with exactly two diagnostics (TS2322 "preview" not assignable to the narrowed union; TS2322 the authored block not assignable to undefined), the control leg (legal context) compiles clean.
  • Playbook gate loop, each exit captured individually: check:liveness, check:empty-state, check:authorable-surface, check:docs, check:spec-changes, check:upgrade-guide, check:skill-refs, check:skill-docs, check:variant-docs PASS on first run; check:api-surface, check:skill-examples, check:exported-any, check:dual-source-exports first returned prerequisite refusals (dist older than src after the reverse-verification restore; client-react not built) — treated as NOT MEASURED, then all green in one AND-joined chain after pnpm --filter @objectstack/spec build and pnpm --filter '@objectstack/client-react...' build ("no new dual-source exports: 4982 names across 17 entry points").
  • check:generated fully green in an AND-joined chain (single verdict, exit 0) after regenerating exactly the five artifacts it proved stale (api-surface, export-origins, docs, skill-refs, strictness-ledger).
  • Dogfood radius (playbook default): pnpm --filter '@objectstack/dogfood^...' build && vitest run test/expression-conformance.test.ts — 3 passed.
  • Gate union derived by node scripts/pm/dispatch-gates.mjs (no paths passed; stderr names this repo at commit d6551e4) — 24 families; all run at d6551e4 in two AND-joined chains, both exit 0 (UNION-PART1-ALL-PASS, UNION-PART2-ALL-PASS), including check-adr-0087-registration --base origin/main ("1 declared-breaking changeset(s), each carrying an ADR-0087 disposition"), check-changeset-no-major (no major bump) and check-empty-changeset (1 declaring changeset added). Families beyond the dispatch-named set that the derivation added and I ran: agent-test-spelling, cross-package-test-inputs, doc-anchors, doc-authoring, doc-formula-expressions, doc-security-posture, docs-audit-scope, docs-redirects, docs-single-h1, merge-driver, objectql-double-limit, objectui-changeset, page-declaration-shape, pm-governed-merges, pm-half-states, published-files, published-readme-links, query-options-erasure, changeset-gate-self-tests.
  • node scripts/check-nul-bytes.mjs — "OK (scanned 7061 text file(s) ... no raw ASCII control bytes)".
  • Repo-wide eslint (pnpm lint) is CI's run and was not run locally; no local reading is claimed for it.

skills/** readings (generated shrink, zero net growth)

skills/objectstack-platform/references/_index.md (touched by gen:skill-refs only): whole file 62 lines before, 62 after (one generated one-liner rewritten). Whole published package, sum of all SKILL.md files: 10515 lines before, 10515 after.

Changeset

.changeset/kernel-context-preview-mode-retired.md@objectstack/spec: minor (launch-window convention; a major bump is refused by check-changeset-no-major), BREAKING prose with the FROM to TO mapping and one-line fix, ADR-0087 disposition marker registering kernel-context-preview-mode-retired.

Related context, none closed by this PR: #11806 remains open (the measurement that surfaced this card); objectstack-ai/cloud#1651 is already closed with the cloud reading this PR relies on.

Generated by Claude Code


Generated by Claude Code

…ewModeConfig block

Fixes #11846 (ADR-0049 enforce-or-remove; maintainer ruling 2026-08-27,
Option A: remove). The declaration promised an auth bypass no layer
implemented; zero consumers measured in objectstack, objectui and cloud.

- 'preview' leaves the RuntimeMode enum; the prescription hangs on the
  enum's own error map (the HookBodyCapability precedent)
- KernelContext.previewMode is tombstoned with retiredKey() (non-strict
  schema — a bare deletion would silently strip); the TenantRuntimeContext
  extend copy inherits it; both keys registered in RETIRED_KEYS_BY_MAJOR[18]
- kernel/PreviewModeConfig leaves the emitted set whole (orphan value
  schema, #3950): RETIRED_DEFS_BY_MAJOR[18] + deliberate manifest deletion
- no D2 conversion (host-constructed context, no stack seam — the
  kernel/Manifest:loading disposition); D3 semantic entry
  kernel-context-preview-mode-retired carries the prescription
- pin tests for both rejection directions; regenerated authorable
  surface/defaults, api-surface, export-origins, docs, skill refs,
  strictness ledger; breaking changeset with the ADR-0087 marker

Co-Authored-By: Claude <noreply@anthropic.com>
@github-actions

github-actions Bot commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

10 anchor(s) derived from 1 changed package(s); no hand-written page names any of them. ⚠️ 8 changed file(s) yielded no anchor (packages/spec/api-surface/kernel.json, packages/spec/authorable-defaults/kernel.json, packages/spec/authorable-surface/kernel.json, …), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

What this run could not see
  • 8 changed file(s) yielded no anchor (packages/spec/api-surface/kernel.json, packages/spec/authorable-defaults/kernel.json, packages/spec/authorable-surface/kernel.json, …) — pages documenting those are invisible to this run
  • 4 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 47 of 219 client-bound route-ledger rows — the other 172 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 172: 14 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 56 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 102 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.

Coarse fallback — 126 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 54e2d3692abd745e7582ec57af3a3c3daec043f0packageMentionDocs.

Which tree this was computed on

This run read content/docs from 5b6ab84ed90d7425f253117ac4c4a3950949e44f — the merge of head dbbb7810cd468dc9b3edd60ec5401f53f2e28ce8 into base 54e2d3692abd745e7582ec57af3a3c3daec043f0, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 5b6ab84ed90d7425f253117ac4c4a3950949e44f && git checkout 5b6ab84ed90d7425f253117ac4c4a3950949e44f
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 54e2d3692abd745e7582ec57af3a3c3daec043f0 dbbb7810cd468dc9b3edd60ec5401f53f2e28ce8 && git checkout -B drift-repro 54e2d3692abd745e7582ec57af3a3c3daec043f0 && git merge --no-ff dbbb7810cd468dc9b3edd60ec5401f53f2e28ce8

node scripts/docs-audit/affected-docs.mjs --json 54e2d3692abd745e7582ec57af3a3c3daec043f0

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

claude added 2 commits August 27, 2026 14:45
os-regen-merge four-step: merge commit first (deferral recorded), then the
deliberate manifest + surface-baseline deletions re-applied on the merged
tree and the whole chain regenerated with repo tooling. Both sides' sources
verified present (retirement tombstone + registry entries; #12704
OrgScopingEntitlement tenancy-posture + capabilities).

Co-Authored-By: Claude <noreply@anthropic.com>
@os-sales
os-sales added this pull request to the merge queue Aug 27, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Aug 27, 2026
@os-sales
os-sales requested a review from os-zhuang August 27, 2026 17:53

Copy link
Copy Markdown
Collaborator Author

Queue kick #2 disposition (spec seat, session_01JvjTCjJQn9zSTXEhUKgT7s): dequeued CI_FAILURE at 17:51Z. The failing check is the Governed Surface Queue Guard's merge_group leg (run 33099507477, failure, 17:39Z) — its own designed fail-closed refusal, not a defect in this PR: the diff's one register hit, skills/objectstack-platform/references/_index.md, is a PURE REGENERATION and the seat-side #11705/#10277-C predicate lifts it with dependencies installed ("✅ NOT governed — ordinary queue landing", measured on this head's tree), but the queue job installs no dependencies, so the generated-surface exception fails closed there and the PR reads GOVERNED at merge-group time — needing an APPROVED review to exist on the PR (the #8161 predicate). Its script header records exactly this limit as filed, not taken.

All 33 checks are green on head 5eb237fd and the mergeable state is clean; every other queue-group check passed. Blocker = the missing approving review, which the PR author's account cannot supply. Review requested from os-zhuang — one approval releases it; it re-enters the queue on re-arm after that.


Generated by Claude Code

@os-sales
os-sales added this pull request to the merge queue Aug 28, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to a conflict with the base branch Aug 28, 2026

os-elon commented Aug 28, 2026

Copy link
Copy Markdown
Collaborator

Merging directly on the maintainer's explicit order — provenance: maintainer, 2026-08-28, live director session (session session_016SG9S6V15MqeAgkehDcTwk), verbatim: 「12718 12736 12689 我都批准了,为什么合并队列会踢出,你帮我合并」.

Why not the queue: the 2026-08-27 kick disposition on this PR stands — the Governed Surface Queue Guard's merge_group leg fails closed on the skills/** register hit (a pure regeneration the dependency-less queue job cannot verify as generated). The maintainer's approval + this recorded order IS the governed-face review record; a direct squash merge is the sanctioned landing for it. mergeable_state: clean on head dbbb7810c at merge time.


Generated by Claude Code

@os-elon
os-elon added this pull request to the merge queue Aug 28, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Aug 28, 2026
@os-zhuang
os-zhuang merged commit 0c2334f into main Aug 28, 2026
34 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-11846-retire-preview-mode branch August 28, 2026 07:49
os-trump pushed a commit that referenced this pull request Aug 28, 2026
Pre-fix sync for the export-origins patch round: origin/main moved 30
commits since the last merge (2b4178a head), including #12718
(preview-mode retirement, touches packages/spec/src/kernel/context.zod.ts
and migrations) and unrelated pm/docs/engine work. Confirmed no overlap
with this branch's four barrel edits before merging: diffing this
branch against origin/main on src/{index,kernel,ai,ui}/index.ts shows
exactly this branch's own additions and nothing else.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LpRNHxWZgSUgVnFT9mQQo4
os-trump pushed a commit that referenced this pull request Aug 28, 2026
Patch round on #12414/PR #12899: CI flagged check:export-origins STALE
on ai.json/kernel.json/root.json/ui.json -- the seven re-exports this
branch adds regenerated api-surface but not the sibling export-origins
artifact when the branch was first merged with origin/main (the #12949
precedent this diff follows regenerates both).

Root cause, precisely: merging origin/main (which had since landed
#12718, retiring PreviewModeConfig) picked up main's source changes but
the two GENERATED artifacts derived from that merged source were never
rebuilt -- kernel.json's api-surface still listed the retired
PreviewModeConfig/PreviewModeConfigParsed/PreviewModeConfigSchema, and
export-origins hadn't gained entries for this branch's seven new
re-exports at all.

Fix: pnpm --filter @objectstack/spec build (fresh dist reflecting both
this branch's re-exports and #12718's retirement), then
gen:export-origins + gen:api-surface. Read the resulting diff per the
gate's own #4411 dual-source-trap remedy: each of the seven names'
newly-gained export-origins entry was cross-checked against its origin
on every sibling entry that already carried it (root vs /system vs /ui,
kernel vs /data, /ai and /ui vs /data and /automation) -- all match
byte-for-byte, so this is the harmless re-export shape, never dual-source.
kernel.json's api-surface diff is exactly the PreviewModeConfig removal
#12718 made; nothing from this branch's own repair moved.

Verified: check:export-origins, check:dual-source-exports and
check:generated (14/14 generated artifacts) all green on the merged +
rebuilt tree.

Part of #12414

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LpRNHxWZgSUgVnFT9mQQo4
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/l tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

RuntimeMode: 'preview' and the whole KernelContext.previewMode block are declared, documented as an auth bypass, and enforced by nothing

4 participants