docs: spec for the legacy, deprecated, and unused-config cleanup

All seven compatibility keys are dead configuration: traced every reference
and none is read by any compiler, codegen, or runtime code. They are written
into every generated config and ignored.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-19 22:47:22 +05:30
co-authored by Claude Opus 5
parent 2f075df42c
commit e40d8319a6
@@ -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 <check\|explain\|upgrade>` | `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.