[CI] INC-D6: de-vendor the EmDash host onto the released emdash@0.38.0 - #295
Conversation
`0.38.0`, published 2026-09-15, is the first release cut after the conditional-write PR merged upstream, so the primitives Otta's commerce truth rides on — `updateIf`, `getVersioned`, `compareAndSet`, `compareAndDelete` — are released code now rather than a locally built merge. The four `file:` overrides are DELETED rather than repointed, because every reason they existed is gone at this release. `@emdash-cms/admin@0.38.0` exports `./portable-text-table` and `@emdash-cms/registry-client@0.6.0` exports `./listing-policy` — the two unreleased subpaths whose absence forced those siblings to be vendored beside the core. And the quiet override, `@emdash-cms/cloudflare`, still pins `emdash` exactly but now pins `0.38.0`, which is the version the manifests themselves name, so the exact pin and the manifests agree and one copy resolves with no help: one `emdash@0.38.0` in `node_modules/.pnpm`, one key in the lockfile. That agreement is a coincidence, not a guarantee, which is why `host-pin.test.ts` is kept rather than deleted. A future `@emdash-cms/cloudflare` that pins some other exact `emdash` puts a second copy in the store and binds the Worker bridge to a host WITHOUT the primitives — no install error, no type error. The test is what makes that loud; the remedy, if it ever fires, is to bring back an exact `emdash` override. `minimumReleaseAgeExclude` grows rather than shrinks: the four packages were absent from it only because `file:` tarballs bypass the release-age check entirely. They resolve from the registry again, so the whole 0.38 train is listed. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CQbJYWWm8tf8owshm7XRp8
…he memory note `vendor/` was always temporary: four tarballs, a recorded diff and a rebuild script, kept only until a release carried the conditional writes. `0.38.0` does, so the directory goes — 9.6 MB of committed binaries and the script that produced them. Nothing is lost with it. `vendor/README.md` was the authoritative record of the build — the pinned base and PR-head SHAs, the migration number, why each of the four tarballs was required, why each override was load-bearing, the conflict resolutions and the build evidence — and all of it moves into §6 of the work order's memory note, which was reserved for exactly this. The temporary "how to rebuild the tarballs" framing is replaced by what the release swap settled, and the generalisable lesson is kept past the vendoring: the host monorepo's workspace packages can carry source newer than the release their version names, so any sibling whose unreleased source the core reaches has to be vendored alongside it — which is what both extra tarballs were. §5 records the R13 outcome. The released build's migration runner is byte-identical to the vendored one — 76 migrations, same order, same `077_plugin_storage_revisions` tail — so a database migrated by the vendored build holds exactly the rows the released runner expects, by name. No staging D1 rename was needed and none was performed. The read-only procedure for checking it is recorded anyway, because the next host bump may genuinely need it, along with the rule that applied rows are compared by NAME, never by count. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CQbJYWWm8tf8owshm7XRp8
…irections The gate asserts the set of skip-shaped constructs across the sandbox suites EXACTLY, so it fails when its allowance list is too generous AND when it is too strict. It had become both at once, and the branch's `pnpm test:e2e` has been red for several increments as a result — found while re-running the full battery on the released host, not caused by it. Too generous: `ALLOWED_SKIPS` still permitted a `describe.skipIf` in `account-routes` and `download-route` under the documented Postgres gate. The mode-collapse retrofit moved both suites onto the plugin's own document store, so neither is conditional any more and every sandbox suite runs unconditionally. The allowance outlived the thing it allowed. Too strict: it did not permit the thirteen `test.todo` cases in `storefront-checkout` or the one in `reports-widget`, all deliberately parked rather than deleted or inverted when the HTTP transport went, each naming its blocking work in its own title so the coverage stays visible. Both corrected, and the matcher tightened to require a trailing `(`. These suites explain their parked cases at length, so a bare-word match counted every backticked `test.todo` in a comment as a skip and would have filled the allowance list with entries that are not code. The gate is stricter after this than before it, and the parked-case count is now a number a reviewer can argue with — it should only ever go down. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CQbJYWWm8tf8owshm7XRp8
INC-A0's re-verification list, walked again on the released buildA0's list was gathered against the vendored merge. Every item was re-executed against
Corroborating this from the source side: only six files differ between the vendored core's And the R13 evidence, since it is the acceptance criterion
The read-only procedure is recorded in the memory note for the next host bump, which may genuinely need it: the table is |
24fc1a0 to
cef7a88
Compare
…ons/ reference Both independent PR #295 reviewers flagged that pnpm-workspace.yaml's one-copy comment said "invariant, not a coincidence" while going on to describe how a future @emdash-cms/cloudflare release could break it — the opposite of what the commit message for 2d6b6d4 and the memory note's §5 already say. Flipped the polarity to match: it's a coincidence of agreement, not an invariant. Also fixed one sentence in the memory note's §5 that referenced a nonexistent `migrations/` directory listing; migration names live in runner.ts, as the rest of the same paragraph already correctly uses. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CQbJYWWm8tf8owshm7XRp8
QA — full battery + T3, final commit
|
| Check | Result |
|---|---|
pnpm lint |
PASS (oxlint clean; depcruise: 0 violations) |
pnpm typecheck |
PASS |
pnpm -r build |
PASS (astro check clean; build banner confirms emdash v0.38.0 wired in) |
pnpm test |
PASS — 224 files, 4348 tests passed, 820 skipped, 14 todo |
pnpm test:pg (127.0.0.1:55432, not prod) |
PASS — 60 files, 1551 tests passed, 7 skipped |
pnpm test:d1 (T3, production dialect) |
PASS — 14 files, 525 tests passed, 0 skipped |
pnpm test:e2e |
PASS — 13 passed, 21 skipped (pre-existing seeded-data gates, unrelated to this diff); harness.spec.ts itself 12/12 green |
pnpm install --frozen-lockfile |
PASS |
pnpm why emdash |
exactly 1 version resolved (0.38.0) |
vendor/ directory |
confirmed absent |
Verdict: GO.
INC-D6, the last increment of work order 02 before the integration branch merges.
vendor/was always temporary — four tarballs, a recorded diff and a rebuild script, kept only until a release carried the conditional-write primitives.emdash@0.38.0, published 2026-09-15, is the first release cut after that PR merged upstream, so the directory goes.The overrides are deleted, not repointed
Every reason the four
file:overrides existed is gone at this release:@emdash-cms/admin@0.38.0now exports./portable-text-table, and@emdash-cms/registry-client@0.6.0now exports./listing-policy— the two unreleased subpaths the core build reaches into, whose absence at0.37.0/0.5.0is what forced those siblings to be vendored beside the core at all.@emdash-cms/cloudflare, still pinsemdashexactly — but it now pins0.38.0, which is the version the manifests themselves name. The exact pin and the manifests agree, so a single copy resolves with no help.Exactly one
emdash@in the store, verified two ways: oneemdash@0.38.0directory innode_modules/.pnpm, oneemdash@0.38.0key inpnpm-lock.yaml.That agreement is a coincidence rather than a guarantee, which is why
sites/staging/test/host-pin.test.tsis kept and updated rather than deleted. A future@emdash-cms/cloudflarepinning some other exactemdashwould put a second copy in the store and bind the Worker bridge to a host without the primitives — no install error, no type error. That test is what makes it loud, and the remedy is to bring an exactemdashoverride back.minimumReleaseAgeExcludegrows rather than shrinks: the four packages were absent from it only becausefile:tarballs bypass the release-age check entirely. They resolve from the registry again, so the whole 0.38 train is listed.R13 did not materialize — no migration rename, no deploy
The released
0.38.0numbers the conditional-write migration077_plugin_storage_revisions, the same name the vendored merge had renumbered it to. Stronger than that: the released build's migration runner is byte-identical to the vendored build's (519 lines,diffsilent), themigrations/directory listings match, and both carry 76 migrations in the same order with077_plugin_storage_revisionslast inMIGRATION_NAMES.So the fast-path hazard the risk exists for —
runMigrationsshort-circuiting onappliedCount >= MIGRATION_COUNTand silently never applying a real migration — has no way to fire on this swap. A database migrated by the vendored build holds exactly the rows the released runner expects, by name. No staging rename was needed and none was performed; nothing was deployed and nothing was written. The read-only check procedure is recorded in the memory note anyway, because the next host bump may genuinely need it.vendor/README.mdis folded in, not lostIts full record — pinned base and PR-head SHAs, the migration number, why each of the four tarballs was required, why each override was load-bearing, the conflict resolutions and the build evidence — moves into §6 of
plans/work-order-02-fold-service-into-plugin-memory.md, the section reserved for exactly this. §5 records what the release swap settled. The generalisable lesson is kept past the vendoring: the host monorepo's workspace packages can carry source newer than the release their version names, so any sibling whose unreleased source the core reaches has to be vendored alongside it.A pre-existing red gate, found and fixed
pnpm test:e2ewas already failing on the integration branch before this increment touched anything.sites/staging/e2e/harness.spec.ts's ADR-0006 additive gate asserts the set of skip-shaped constructs across the sandbox suites exactly, so it fails when its allowance list is too generous and when it is too strict. It was both at once:describe.skipIfinaccount-routesanddownload-routeunder the documented Postgres gate. The mode-collapse retrofit moved both suites onto the plugin's own document store, so neither is conditional any more.test.todocases instorefront-checkoutor the one inreports-widget, all deliberately parked when the HTTP transport was deleted, each naming its blocking work in its own title.Both corrected, and the matcher tightened to require a trailing
(so that prose about a parked case — these suites explain themselves at length — no longer counts as a skip. The gate is stricter after this than before it. That the branch's e2e had been red for several increments without anyone noticing is worth flagging on its own.Verification
Full battery on the released build, run from the worktree root:
pnpm lint(incl. the domain-purity dep check)pnpm typecheckpnpm -r buildpnpm testpnpm test:pg(real Postgres)pnpm test:d1— T3, the production dialectpnpm test:e2epnpm install --frozen-lockfileINC-A0's re-verification list was walked again item by item against the released build rather than the vendored one; the outcome is in the review notes below.
Does not touch
README.md,DEPLOYMENT.mdoradr/*— already correct from INC-D4. Does not merge the integration branch tomain; that is a separate step.🤖 Generated with Claude Code
https://claude.ai/code/session_01CQbJYWWm8tf8owshm7XRp8