diff --git a/docs/superpowers/specs/2026-08-19-legacy-and-config-cleanup-design.md b/docs/superpowers/specs/2026-08-19-legacy-and-config-cleanup-design.md new file mode 100644 index 00000000..d8bdbe35 --- /dev/null +++ b/docs/superpowers/specs/2026-08-19-legacy-and-config-cleanup-design.md @@ -0,0 +1,138 @@ +# Legacy, deprecated, and unused-config cleanup — Design + +**Date:** 2026-08-19 +**Status:** Approved for implementation +**Scope:** Remove the compatibility-flag surface, the `"legacy"` function runtime, dead migrations, +and deprecated APIs — before the framework's first public release. + +## Goal + +WRNexus carries compatibility machinery for a public it does not yet have. Every branch of it is +either switched off in the only apps that exist, or wired to nothing at all. Removing it now costs +almost nothing; removing it after release costs a major version and other people's time. + +## Why this is safe now + +Two pieces of evidence, both verified rather than assumed: + +**The only consumers already run without it**, which matters for the config files themselves even +though the keys are inert. `D:\Company\wrnexus\apps\admin` and +`D:\Company\wrnexus\apps\web` — both test projects, neither deployed — set every compatibility flag +to `false` and `legacyDefaultRuntime: "current"`. They are already on the modern path; removing the +flags means deleting the lines that say "off". + +**None of the seven keys change any behaviour.** Verified by tracing every reference, not by +reading the types. `legacyEmit`, `legacyEventProps`, `legacyComponentDiscovery`, `stringLayouts`, +and `legacyDefaultRuntime` appear in exactly three places each: the type declaration in +`config.ts`, the scaffolder in `create.ts`, and the insertion in `update.ts`. **No compiler, +codegen, or runtime code reads any of them.** They are written into every generated config and then +ignored. + +The remaining two are the same story with more machinery. `resolveCompatibility` (`packages/styles/src/compatibility.ts`) +produces a report — an effective date, a behaviour number, and advisory strings. Tracing every +consumer: the `wrnexus compatibility` command prints it, and `config.ts` raises one validation error +when the configured date is _newer_ than the CLI supports. **No compiler branch and no runtime +behaviour reads `effectiveDate` or `effectiveBehaviour`.** The mechanism is scaffolding that was +never connected. + +### Non-goals + +- **The legacy `api` block forms.** Bare-body blocks using `with ($data)`, and `api` entries inside + `ssr {}` / `client {}`, are _replaced_ rather than deleted — that is the next spec's job. Removing + them here would leave a gap with no working mechanism. +- Adding any configuration key. This spec only removes them. +- Changing any behaviour that is currently switched on. + +## What gets removed + +| Item | Where | Why it goes | +| --------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------- | +| `compatibility: { legacyEmit, legacyEventProps, legacyComponentDiscovery, stringLayouts }` | `packages/styles/src/config.ts` (`CompatibilityConfig`) | Never read by any code | +| `functions: { legacyDefaultRuntime }` | `packages/styles/src/config.ts` (`FunctionsConfig`) | Never read by any code | +| `compatibilityDate` | `packages/styles/src/compatibility.ts` (`CompatibilityPolicy`) | Gates nothing | +| `frameworkBehaviour` | same | Gates nothing | +| `resolveCompatibility`, `CompatibilityReport`, `isCompatibilityDate`, `CURRENT_COMPATIBILITY_DATE`, `CURRENT_FRAMEWORK_BEHAVIOUR` | `packages/styles/src/compatibility.ts` | Whole module serves only the two dead keys | +| `wrnexus compatibility ` | `packages/cli/src/compatibility-command.ts`, dispatch at `packages/cli/src/index.ts:295`, help text at `:75` | Reports on removed keys | +| `"legacy"` variant of `FunctionRuntime` | `packages/syntax/src/v060.ts`, branches in `packages/compiler/src/client-codegen.ts` and `server-codegen.ts` | An unmarked function becomes `shared` (see below) | +| Migrations below `0.8.0` | `packages/cli/src/update.ts` | 111 migrations reach back to `0.2.8`; no project exists below `0.8.x` | +| Deprecated re-export shims | `packages/compiler/src/{parser,tokenizer,types}.ts` | Two lines each, re-exporting `@wrnexus/syntax` | +| Deprecated `@wrnexus/auth` options | `engine.ts` (4 sites), `http/index.ts` (2), `plugin.ts` (2), `types.ts` (1) | See "Deprecated auth options" | + +## The `"legacy"` function runtime + +`FunctionRuntime` is `"legacy" | "client" | "server" | "shared"`. `"legacy"` is what an _unmarked_ +`function foo()` gets, and `legacyDefaultRuntime` decides how it behaves. Both codegens then test +membership: `["legacy", "client", "shared"]` for the browser and `["legacy", "server", "shared"]` +for the server — which is to say **an unmarked function is currently emitted into both bundles, +exactly like `shared`.** + +`legacyDefaultRuntime` looks like it should modulate this, but it is never read (above), so the +mapping is unconditional: unmarked is always `"legacy"`, and `"legacy"` is always emitted to both +bundles. + +So the removal is mechanical: delete the `"legacy"` variant, and parse an unmarked function as +`"shared"`. The emitted output for every existing unmarked function is unchanged, in every +configuration. `FunctionRuntime` +becomes `"client" | "server" | "shared"`, and the membership tests lose one element each. + +This is the one item where behaviour could drift if done carelessly, so its test is explicit: an +unmarked function must still appear in both the browser and server modules. + +## Deprecated re-export shims + +`packages/compiler/src/parser.ts`, `tokenizer.ts`, and `types.ts` are two-line files re-exporting +`@wrnexus/syntax`. They are marked deprecated, but **`codegen.ts` and `native-codegen.ts` still +import from them**, so deleting the files is not enough — those imports must be repointed at +`@wrnexus/syntax` first. Removing the files without that step breaks the build. + +## Deprecated auth options + +`@wrnexus/auth` carries nine `@deprecated` markers in three groups: + +- `onSignedIn` / `onSignedOut` aliases in `http/index.ts` and `plugin.ts`, superseded by + `createAuthEngine({ onSignedIn })` +- `onSuccessfulSignUp`'s misspelled alias in `types.ts` +- Four RP-ID and origin options in `engine.ts`, superseded by values bound to the issued challenge + +These are a different package from the compiler and config surface, and they are removable +independently. They are included here because the instruction was to remove deprecated code, and +splitting them into their own pass would mean two migrations of the same apps. If the auth surface +should stay, drop this section — nothing else in the spec depends on it. + +## Migration + +`examples/basic-app` and both test apps need one pass each: + +1. Delete `compatibility`, `functions`, `compatibilityDate`, and `frameworkBehaviour` from + `wrnexus.config.ts` — seven lines per app. +2. `packages/cli/src/create.ts` stops scaffolding those keys, so new apps get a shorter config. + +No `.wrn` source changes. Nothing in this spec alters page syntax. + +The removed `0.2.x`–`0.7.x` migrations mean a project below `0.8.0` can no longer be upgraded by +`wrnexus update`. No such project exists, and rescuing one would be a manual job either way. + +## Testing + +- **The `"legacy"` runtime removal is behaviour-preserving**: an unmarked function still appears in + both the browser and server modules. This is the assertion most worth writing, because it is the + only removal that could silently change output. +- **Config rejects the removed keys** rather than ignoring them, so a stale config fails loudly with + a message naming the key. A silently-ignored key would leave someone believing a flag still + applies. +- **`create.ts` scaffolds a config without them**, asserted against the generated file. +- **`wrnexus update` still runs** with the pre-`0.8.0` migrations gone, and reports correctly for an + app already at the current version. +- **`examples/basic-app` builds and its suite passes** after its config is trimmed — the end-to-end + guard that nothing depended on the removed surface. +- **The full gate** (`bun run check:production`) passes, including the editor bundles, which embed + the compiler and must be rebuilt after `FunctionRuntime` changes. + +## What we give up + +Deleting `compatibilityDate` and `frameworkBehaviour` removes the standard escape hatch for changing +a default after going public — the mechanism that lets an existing app keep old behaviour by pinning +a date. Today it is wired to nothing, so it protects nobody, and an unused mechanism rots rather +than matures. If a gate is needed later it can be reintroduced deliberately, against a real +behaviour change, instead of being carried empty. This is a considered trade rather than a free +deletion.