---
title: "Day five: nothing in Ursprung transpiles"
date: 2026-08-05
summary: >-
  Asking who the transpile was actually for produced no answer, so the
  transpiler left and the three oxc wasm packages went with it. What reads the
  module graph is now a single 27-kilobyte lexer, Tailwind became a command the
  producer runs, and five tickets were ruled out of scope in one day. It is the
  first time the map has been redrawn rather than extended.
---

# Day five: nothing in Ursprung transpiles

Day four ended with the build living inside a Worker, three oxc wasm packages in
the isolate, a 128 MiB memory ceiling bisected against production, and a
paid-plan-only clause. Day five deleted all of it — not by finding a better
bundler, but by asking who the transpile was for and getting no answer.

**Ursprung is no longer the TypeScript→JavaScript transpiler that builds it.**
That is a change to the Destination rather than an amendment to an axiom, and it
is the first time this map has been redrawn rather than extended.

Six tickets resolved, eight opened, **five ruled out of scope** — a disposition
that had never been used before today and was used five times. Five throwaway
spikes, no research surveys: the day ran entirely on grilling sessions and code.
The ticket count moved from twenty-eight with fourteen resolved to **thirty-six
with twenty resolved**. The map itself grew from 851 lines to 1,471.

And for the first time in three days the repository changed as much as the plan
did: 53 files outside the planning directory, 1,132 lines added and 454 removed.
The Worker now compiles with TypeScript 7 and uploads unbundled, `ursprung` ships
the compiler settings every app extends, and the project has its first ADR.

This post is mostly a ledger of reversals, because that is mostly what the day
was.

## The question that dissolved

Ticket 34 asked whether TypeScript 7 should own emit. The spike had already run
and the numbers were good: `allowImportingTsExtensions` plus
`rewriteRelativeImportExtensions` without `noEmit` is legal on 7.0.2, the output
matched ticket 11's hand-written specification on **all eight assertions**
— automatic JSX runtime as real ESM with no `require()` trap, type-only imports
elided, one source map per module — and it ran in **169 ms emit-only, 450 ms
emit-plus-check** over 201 modules against ticket 26's 1,044 ms cold for the oxc
chain. `--noCheck` even preserved ticket 11's central result, keeping the build a
pure transform that never consults types.

**None of that decided it.** TypeScript 7 ships 21 platform-native Go
executables, has no wasm target, and its JS API is an IPC proxy to a spawned
process — so _tsc emits_ and _the build runs inside workerd_ are mutually
exclusive. The ticket could not be answered on emit evidence at all. It had to go
at the axiom.

And the axiom turned out to have no recorded justification. Both previous forms
of it argued about _how_ to guarantee "the build works in a Worker". **Neither
recorded who wanted the property.**

> Nobody wants a build inside a Worker. They want the **app** inside a Worker.

Ad hoc Ursprung applications, written by agents, running in a **dynamic worker**,
spun up with no deploy cycle. That is what the build-host requirement was for the
whole time. And an agent producing such an app either writes JavaScript directly
or runs `tsc` in the sandbox it already has — so **the modules arriving are
already JavaScript, and the transpile has no caller.**

The axiom **stands**, in an operative form that is a capability bar rather than a
location: _whatever is left as tasks for the build needs to be able to run inside
the worker._ A sandbox may run the residue too, so ticket 24's one-code-path
property is kept rather than traded. What changed is how much has to clear the
bar.

**Ticket 24 had the distinction and drew the wrong half of it.** It wrote the
Worker Loader _out_ of the surface — "built in a Worker does not imply served
through the Worker Loader" — while keeping the half that followed from it. The
Loader **is** the surface. That clause is reversed, and it is the largest single
addition the public-API patch has taken.

## What day four bought and day five returned

The oxc apparatus was the map's most expensive single arrangement, and almost
everything costly on the map descended from it rather than from anything Ursprung
wanted. It left in one patch:

- **Three packages, 2,343 KiB gzipped** — `oxc-parser`, `oxc-transform`,
  `oxc-minify` — gone. With no TypeScript, no JSX and no splicing, what the walk
  needs from a module is a **specifier list**, which is a lexer's output rather
  than a parser's.
- The **forked wasm loaders**, which ticket 26 had proved were the difference
  between working and not working on production.
- **wasm only** and **sync throughout**, both dead rather than amended — the
  first because there may be no wasm, the second because its ground was oxc's
  `wasi.thread-spawn`.
- The **128 MiB single-shared-memory ceiling**, the **183.87 MiB floor**,
  _"exactly three memories fit and a fourth kills the isolate"_, and the
  `SharedArrayBuffer` dependency.
- **Paid-plan-only**, of which more below.

`node:crypto`'s `createHash` survives as the emit hash. _TypeScript 7 can never
run inside the build_ survives too and **stops mattering**, because tsc is not in
the build any more; it is in the agent's sandbox, outside Ursprung's scope
entirely.

Ticket 26 — the production deploy probe that was day four's sharpest empirical
work, run without a Cloudflare account through `wrangler deploy --temporary` and
a probe that _threw_ its results so wrangler would surface them — **becomes
history rather than a live constraint.** The map kept its paragraphs for the
reasoning and for what they proved about workerd, marked as no longer binding.
That is the right disposition and it costs nothing; deleting them would have made
the same mistake look fresh in six months.

Three tickets went out with the apparatus, each closed in its own body rather
than left to inference: **source-map composition** (no chain to compose), **oxc
wasm distribution** (no wasm to distribute), and **a non-threads oxc build** (no
floor to relieve).

The replacement evidence is weaker than ticket 26's, and the map says so rather
than pretending otherwise. It does not need to be stronger: **both local/production
divergences this map was burned by were wasm memory facts, and there is no wasm.**

## One package, and it is not really asm.js

Ticket 36 asked what reads the module graph now that nothing transpiles. The
answer is **`es-module-lexer` 2.3.1, its `./js` subpath, 27,504 B, zero
dependencies** — against the _"three packages, all oxc, 2,343 KiB gzipped"_ the
entry started as. **The build isolate holds one package.**

The candidate to beat was confirmed rather than beaten, and the interesting part
is what the `./js` entry actually is: **V8 refuses to validate it as asm.js**
(`Invalid asm.js: Invalid return type`) and runs it as ordinary JavaScript. So
adopting it bets on **no engine feature whatever** — not wasm, not
`SharedArrayBuffer`, not asm.js AOT. There is no `init` and no async entry point;
`parse()` is synchronous by construction.

Twenty of twenty correctness cases passed inside a real workerd isolate on the
first run, malformed input is refused with a `line:column`, an analysable
`import("./page.js")` is distinguishable from an unanalysable one, and it produced
**zero disagreements with the wasm entry across 201 modules**.

The whole residue — `lex → walk → refuse → hash → manifest` — runs in **28 ms
cold, 8 ms warm** for 201 modules, against ticket 26's 1,044 ms. **workerd costs
nothing over Node**, where wasm cost 5.35×, and the pure-JS entry's ~1.5×
slowdown against the wasm entry is the entire price.

Pin it exactly, for the same reason as `capnweb`: `./js` is a subpath nobody
upstream has promised to keep engine-independent.

## Tailwind leaves the framework

This is the largest change the styling axiom has taken, and it retires three days
of work that was correct and is now irrelevant.

**Ursprung no longer ensures Tailwind runs. It does not run Tailwind at all.**
Tailwind is a **CLI in the producer's hands**, alongside `tsc` — the same move
ticket 34 made for transpilation, for the same reason: an agent producing an app
already has a sandbox, and one more command in it is cheaper than a library in
the isolate.

Day three proved Tailwind runs unmodified inside workerd with no `nodejs_compat`
and no wasm. Day four priced it at 79.9 KiB gzipped and 8 ms, found `build()`
monotonic, measured a **cross-request CSS leak** from a module-scope compiler,
refused the CSS minifier on 519 B of savings, and established that `compile()` is
**undocumented and has broken in every v4 minor**. All of it real, all of it now
moot.

What survives is the axiom's second half, and it is sharper for the move: **the
compiled stylesheet is an _input_ to Ursprung's build, never a product of it.**
It arrives in the same file map as the modules, is treated as **opaque bytes**,
is content-hashed into the output store, recorded in the manifest, and linked
from the document head by the renderer. **Ursprung never parses it** — which is
what keeps the no-CSS-pipeline exclusion intact by construction rather than by
care. Three terms ride with it: the stylesheet is **declared, not discovered**;
the **producer** runs the CLI, always; and there is **one stylesheet for the
app**, not one per route.

Everything ticket 13 found living in Tailwind's bundler _plugins_ rather than its
engine — content detection, `@source` resolution, URL rewriting, minification,
dependency tracking — is exactly what `@tailwindcss/cli` provides. That paragraph
was written as a cost of the delegation; it is now the argument **for** the move.
The undocumented-API exposure goes with it, because the CLI is one of the four
documented routes ticket 13 itself named: an unversioned private API traded for a
published contract, and _"budget real work at every minor"_ softens to ordinary
version pinning.

Four things follow. **Candidate extraction is out of scope** — and it was priced
rather than waved away: a scanner over raw module text cost **25 ms for 201
modules, five times the lex**, and silently lost a real class string to a
regex-versus-division mistake on the way. **The build becomes synchronous again**,
because `compile()` was the one irreducibly async stage. **An app installs a
second dependency**, so the honest form of "one dependency" is now _"Ursprung is
one dependency; the toolchain that produces the JavaScript and the CSS is the
producer's."_ And **staleness between the JavaScript and the CSS becomes a silent
failure mode** that ticket 14 now owns.

It does not violate ticket 16's _one pipeline, zero permitted differences_: that
rule is about the **build**, and the build is identical either way — it receives
a file it does not interpret. Only the producer differs, which was already true
of `tsc`.

## The reversal ledger

The three above were structural. These are the rest, and several of them reverse
decisions taken within the last forty-eight hours.

**The free-plan exclusion is reversed.** It rested on _"the free tier allows
10 ms CPU per request and a single module transform costs several times that"_ —
and there is no transform. The whole residue is 28 ms cold for 201 modules; at
**startup** that is charged against the 1 s gate ticket 4 measured, two orders of
magnitude of headroom. Kept on the map as a **struck entry** rather than deleted,
because the arithmetic is worth not rediscovering — and because which budget a
startup build is actually charged against on production is **unmeasured**.

**The route table is executed, reversing ticket 7.** Evaluating the module gives
each URL, and `Function.prototype.toString()` plus a lex of the thunk gives its
page, exactly, per entry, **with no page module loaded**. That rescues ticket
23's two endangered bullets and makes _configuration as an executed module_ a
mechanism available generally — which the public-API patch had been owing a shape
for.

**A client module's content hash is content-local, reversing ticket 8's
2026-08-04 amendment.** No splicing had already removed the dependency cascade;
ticket 8 kept the hash dependency-inclusive anyway, routing the dependency
through the import map's entries. **That was a choice presented as a necessity.**
Reversing it makes the output directory a content-addressed store, makes an
incremental cache worth having, and improves browser caching — a leaf edit now
invalidates one entry instead of an ancestor chain. It costs ticket 8 its
bottom-up emit ordering and leaves **the client-graph import-cycle refusal with
no stated ground**. The refusal is probably worth keeping; it now needs a reason
written down rather than inherited.

**Minification of the app's JavaScript is out of scope — refused on the argument,
not the arithmetic.** The arithmetic would not have supported a refusal:
minification saves **25.8% gzipped** on real unminified published ESM, against
ticket 13's 519 B for the CSS minifier. It is out because it is the **producer's**
business, exactly as transpilation is. Ursprung has no more standing to minify
the JavaScript it is handed than to transpile it.

**No splicing's residue is struck, and the axiom is now literally true.** Day four
recorded, honestly, that a bounded check on handler bodies survived and that
claiming "no language analysis" would be overclaiming. It turns out **there was
never anything to check**: handler identity is `module URL + export name`, so only
a module-scope exported function can _be_ a handler, and such a function has
nothing in scope to capture but its parameters, module-scope bindings and globals.
That is the rule, by construction. A closure returned from a factory has no export
name, therefore no identity, and fails where ticket 9's serializer is asked to
write a reference it cannot form. **The build performs no language analysis
whatever.**

**Ticket 31 is out of scope on measurement.** It existed because oxc's wasm linear
memory grew ~90 KiB per module and never shrank, giving roughly 600 modules per
isolate. The chosen reader's buffer is a **high-water mark sized by the largest
single module** — `nextPow2(max(2·chars + 256 KiB, 1 MiB))`, unchanged at 1 MiB
across 1,206 lexes. No accumulation, nothing to design around.

**capnweb's serializer was re-opened and refused a second time.** Ticket 21 had
rejected it on grounds the closed value domain removed, so ticket 9 re-opened it
honestly — and then refused it on arithmetic: **14,418 B gzipped against a 221 B
hand-rolled reviver**, landing on _every_ page including static ones, since
capnweb otherwise reaches a page only when that page does RPC. **capnweb is
adopted as a specification, not as a dependency.**

**`wrangler` stops being types-only.** Ticket 11 added it as a peer for
`InferEnv`; ticket 16 made `ursprung dev` shell out to it, so it is a **runtime
CLI dependency of the dev loop** as well as a type source.

**Build-time enforcement of what a signal may hold is gone**, because ticket 34
left the build with a lexer and it can never see what a signal holds. Enforcement
moves to a `Serializable<T>` natural signature in `ursprung check` plus an
authoritative retention-path throw at serialize time.

## The two axioms that were challenged and held

Day two recorded three findings that contradicted axioms set at charting. Two of
them came due yesterday, and **both were upheld** — on evidence, not preference.

### No virtual DOM

Ticket 19 **reproduced Qwik's premise exactly**: the DOM's _element model_ cannot
express a component boundary. Attribute schemes fail on text-only, empty and
multi-root components; positional paths break silently on the first mutation.
Then it showed the conclusion does not transfer, **because comment nodes are not
part of that model.**

A component boundary is **a pair of HTML comments — `<!--u7-->` … `<!--/u7-->`**
— and a resumed document locates a boundary and replaces its subtree with
server-sent markup **with no component code on the page at all**. That was proven
structurally: the runtime has no imports.

Ranges beat points, because a point's extent has to end at "the next marker or the
parent's end", which is wrong for a component that renders nothing and for a
fragment wrapping a component. A fifth scheme found on the way — `adaptive`,
emitting a closer only where ambiguous — matched `range` at **half the bytes** and
was **refused on diagnosis**: when the absence of a closer is _meaningful_, a build
bug fails silently instead of raising. Ranges are **self-describing, zero
side-channel**, which lets a navigation payload or a streamed fragment carry its
own boundaries. Effect disposal falls out as a containment test at no marginal
cost. Comment markers are **not** foster-parented inside `<table>`.

The cost is stated with its denominator, because otherwise the number means
nothing: **21.9 raw bytes per component, +6.9% raw and +47.9% gzipped** on a
realistic page.

The axiom now carries a limit rather than an open challenge. Ticket 19 verified
locate, replace, insert and dispose; it did **not** verify reconciling two
component trees, which is where a VDOM historically earns its keep. That is ticket
32, and it carries the same instruction: **if a diffing structure turns out to be
unavoidable, amend the axiom rather than quietly violating it.**

### Signals

Ticket 18 did not defend the signals decision. **It replaced its justification.**

The original ground was _"signals will one day be native"_, and that is not
supportable: the proposal is **Stage 1**, `spec.emu` is a 313-byte stub declaring
`stage: 0` with zero clauses, `tc39.es/proposal-signals` 404s, there is **no
browser-engine champion** and no plenary appearance since 2024-06-13, the package
has been **dormant for 18 months**, its own README says **"Do not use this in
production"**, and **Angular, Vue, Preact and Solid all ship their own**.

The replacement is structural rather than aspirational: **the build has no
parser.** Tickets 34 and 36 removed every one, and what reads a module is a lexer
whose entire output is a specifier list. Compiler-derived reactivity —
Svelte-style dependency analysis, compiled subscriptions — is therefore not
something Ursprung declines to build, it is something Ursprung is **incapable** of
building. Read-time dependency discovery is the only kind left. **Standardisation
is demoted to a free hedge**: if the proposal ships, ~800 lines get deleted.

**The polyfill is vendored, not depended on**, and the decisive argument is local:
**there is no bundler, therefore no tree shaking, therefore whatever a package
exports is shipped.** Vendoring is the only tree shaking this framework has. Nine
divergences from the proposal were triaged three-fixed, three-deleted,
two-deliberately-kept, under the rule _observable behaviour matches; the
implementation need not_. `Signal.*` is never exported — the public surface is
`signal()`, `computed()` and `effect()` — so a future swap is invisible to apps.

Two consequences with teeth. **On the server, a signal must be created inside a
request**, which collapses both server hazards into one rule and **fails at
deploy rather than at a user's request**, because Cloudflare runs module scope
once at startup. And **`ursprung` gains two realm entry points chosen by `exports`
condition, which must be type-identical**, because `ursprung check` is one tsc
program that resolves only one of them.

Recorded as **ADR-0001, the project's first** — with a review
_trigger_ rather than a review date, and an explicit note that upstream polyfill
activity is **not** one of the triggers, because the code is ours now and a new
release is a diff to read.

## The format that shrank because the domain closed

Ticket 9 settled the resumability serialization format, and the headline is that
**closing the value domain is what deletes the machinery.** A signal may hold
**`JSON + bigint + Date` and nothing else**, so there are no user-authored cycles
and no shared identity — which removes allocate-then-inflate, the back-reference
table, and delta-encoded reference ids in one go. Qwik needs all three because it
accepted an open domain first.

The domain is a **strict subset of capnweb's** (`S9 ⊂ S21`), deliberately, so
_"anything the server sent you fits in a signal"_ is true by construction rather
than by coincidence. `Error` is the one exclusion, because its **message** leaks
internals into a cacheable public document — and the spike corrected the original
reasoning on the way: the **stack does not cross the wire**.

Encoding is JSON in capnweb's `[typeCode, ...args]` shape inside an inert
`<script type="application/json">`. **Executable JavaScript is refused**: it earns
its keep on cycles, identity and rich constructors, all three now ruled out, and
it would cost `'unsafe-inline'` or a nonce that makes the document uncacheable.
The array-escaping tax the session accepted was then measured and is **negative**
— −1.4% raw, −0.4% gzipped — because the `["date",…]` tag beats the ISO string it
replaces.

**The ticket was split rather than stretched.** It carried eight independent
sub-decisions, more than any other ticket on the map. It is resolved as the
_format_ — what the bytes mean — and the _runtime_ is now ticket 37: dependency
edges, blob versus attributes, signal-level DOM addressing, the resume entry point
and its capture window, the handler-reference wire shape, effect records, and the
size target. Tickets 10 and 32 are **re-pointed to 37**, because both need the
runtime half and would have entered the frontier unworkable.

The charting lesson was written down rather than absorbed: **a ticket whose
Question has eight bullets is not one session's work.**

## What nobody had noticed

Ticket 35 is open, and it is the day's most uncomfortable finding: **the server
graph's specifiers do not resolve, and no ticket had noticed.**

Source names `./foo.ts` (ticket 6), emit produces `.js`, **No splicing forbids the
rewrite**, and wrangler under `no_bundle` resolves nothing. Bare specifiers are
worse: **`ursprung/jsx-runtime` is in every JSX module and has no resolution
mechanism in a `no_bundle` artifact at all** — which means ticket 6's admission of
third-party server packages and ticket 21's measurement of capnweb as one flat
module both rest on an untested premise.

The `cf-wrangler-dev` spike missed it because **its fixture was hand-written as
all-relative and all-`.js`**. Worth recording as a spike-design lesson: a fixture
that avoids the shape the framework actually emits proves less than it looks.

A related result outlived its reason and needed no ticket: ticket 8's
`(module, realm)` pair still holds as the **walk** unit but has lost its ground as
the **emit** unit, because No splicing removed the stub generation that made one
input file produce two output texts.

## The dev loop Ursprung does not own

Ticket 16: **Ursprung does not write a dev server.** `ursprung dev` runs its own
embedded miniflare for the **build** only, and shells out to **`wrangler dev
--x-new-config`** to serve the artifact from disk — so `dev` and `deploy` read the
same config and the same directory with the same tool, and **fidelity becomes
unbreakable rather than maintained**.

**One pipeline, zero permitted differences.** The module fallback service is
refused because it would defer ticket 5's boundary check to runtime. No
Ursprung-owned proxy and no live reload, on agent-first grounds — wrangler's
ProxyWorker is inherited anyway, so requests queue across a reload and GET/HEAD
retry. Four wrangler settings are mandatory rather than advisory, one of them
(`dev.types.generate: false`) because wrangler would otherwise generate the
bindings types ticket 11 chose to **infer**. The `cf` delegate is the migration
target and is **unusable today**: it cannot read `cloudflare.config.ts`. The Build
Output Specification is **write-only** in wrangler 4.118.0, so v0 matches its shape
without emitting it.

One consequence lands on the diagnostics work and is larger than a wording
problem: **because `ursprung dev` shells out, Ursprung does not own stdout.** Two
processes log into one stream in two formats. A patch whose premise is that
diagnostics are the primary agent interface has to answer that before it answers
anything about phrasing.

That patch took a second hit from ticket 34 — **the first constraint on it that is
Ursprung's own doing rather than a dependency's.** The build's input is now
_emitted_ JavaScript, so every position Ursprung reports names a line the author
did not write. Note the irony, which the map records rather than hides: **No
splicing's strongest claim was that never having left the written source beats
mapping back to it accurately — and the build now reads a file the author never
wrote at all.**

Ticket 36 paid for it rather than dropping it: **v0 reads tsc's source map.** With
minification out of scope, nothing invalidates a position, so the map is accurate
with no work from anyone. The build reads it to name the author's line — a
read-only VLQ decode and a binary search — and **falls back to the emitted
position when no map is present**, which is not optional, because an agent may
hand over hand-written JavaScript. For the browser, maps are served at their
**unhashed** names, because a module emitted as `<hash>.js` still carries
`//# sourceMappingURL=m5.js.map` inside it and No splicing forbids rewriting that
comment.

## The question the day ended on

Ticket 36 produced an observation too large to absorb, so it graduated into a
ticket of its own: **is there an Ursprung CLI at all?**

Cloudflare runs a Worker's module scope once at startup, behind a 1 s gate. The
entire build residue is 28 ms cold for 201 modules and 116 ms for 1,000. **Two
orders of magnitude of headroom.** So the shape being proposed is: _there is no
build step and no artifact. The modules are the deployment, and the build runs
inside the deployed worker at startup._

Three responsibilities become homeless if that holds, each decided in an already
_resolved_ ticket — `ursprung check` and its validation half, `ursprung dev` and
its fidelity argument, and whoever produces the manifest for a normally deployed
worker. The ticket is required to say explicitly what happens to tickets 11 and 16
rather than leaving it to inference.

And it names the one cheap measurement that should come first, because a negative
answer collapses the rest: **where does the module source text come from at
startup?** The walk lexes _text_; a deployed worker holds its modules as ES
modules, not as strings.

## Scope, drawn twice

Two whole efforts were ruled out of scope, both as scoping statements rather than
judgements: **native app targets** — no Hermes host, no SwiftUI, no third realm —
and **a data component** — no schema language, no sync protocol, no data-loading
API. Each gets its own effort and its own map when v0 lands, because the fog only
gathers toward _this_ destination.

Neither is deferred cleanly, and the map says where the seam is. The data ruling
carries an explicit warning: **server-loaded data surviving into a resumed client
is the same problem as ticket 9's closed value domain.** A data layer does not get
a private answer to it, and reopening the format after v0 ships is the expensive
direction. And the native ruling does **not** pre-answer whether v0 _states_ its
client-host commitment — the map declares its server host commitment plainly and
has no equivalent for the client, though the commitment is already made in three
resolved tickets and is browser-shaped in every one.

One process wart worth admitting: **two tickets were both numbered 38** — the
client-host question and the is-there-a-CLI question — because they were charted
in parallel branches within a few hours of each other.

## The repository caught up

The plan has been moving faster than the code for three days. Yesterday the code
took a step:

- **The Worker compiles with TypeScript 7 and uploads unbundled.** tsc strips the
  types, `wrangler.config.ts` sets `noBundle`, and wrangler runs `tsc --build`
  itself through its custom `build.command` — so `dev`, `preview` and `deploy`
  need no separate step. The emit is **type erasure only**
  (`erasableSyntaxOnly`, `verbatimModuleSyntax`, `isolatedModules`).
- **`ursprung` ships the compiler settings as public API**, exported as
  `ursprung/tsconfig` and carrying no relative paths, because `extends` resolves
  them against the declaring file — which for a consumer is inside
  `node_modules`.
- **Apps are standalone.** An app builds and type-checks by itself; the root may
  look down into it, but nothing under `apps/` references the root. Removing the
  root `tsconfig.json` would not affect the app.
- Two TypeScript projects per app, split because `cloudflare.config.ts` imports
  the built module and a project may not read its own output (`TS5055`); the root
  references `./src` rather than the app, because a referenced project may not
  disable emit (`TS6310`). Both traps are now written into `AGENTS.md` so they are
  not rediscovered.
- The first **`CONTEXT-MAP.md`** and the first **ADR**.

This is the first day the repository has dogfooded a decision rather than only
recorded one: _no bundler_ is now how this repo's own Worker ships.

## Where this leaves things

Thirty-six tickets: twenty resolved, five out of scope, eleven open.

The subtraction tally, continued from day four and longer than it: no native
addon, no host adapter, no bundler, no CSS minifier, no source-map dependency, no
codegen step, no branded types, no extraction transform, no scope analyser, no
stub generation, no specifier rewriting — and now **no transpiler, no parser, no
wasm, no minifier, no Tailwind, no candidate extractor, no memory ceiling, no
paid-plan restriction, and no dev server.**

What is left is `lex → walk → refuse → hash → manifest`: 27,504 bytes of lexer,
28 ms cold for a 201-module app, no language analysis anywhere, and a build whose
main output is **diagnostics** rather than bytes. Which is why the testing patch
was rewritten too — two of its three subjects, the transpiler and the bundler, no
longer exist, and what is left to test is mostly **refusal**.

The open question is whether even that needs a command line.
