Skip to content

fix(amico-run): bundle-build CI gate + upgrade-path dist rebuild — no more stale verb router (#643) - #647

Open
aarontrowbridge wants to merge 4 commits into
mainfrom
643-fix-dist-rebuild-lane
Open

fix(amico-run): bundle-build CI gate + upgrade-path dist rebuild — no more stale verb router (#643)#647
aarontrowbridge wants to merge 4 commits into
mainfrom
643-fix-dist-rebuild-lane

Conversation

@aarontrowbridge

Copy link
Copy Markdown
Member

Closes #643

What

The deployed amico verb-router bundle sat 46 days stale because nothing rebuilt it: no CI lane built the bundles, and the upgrade path deployed new code while the dist stayed months old. This closes both holes and fixes the cosmetic that made every invocation noisy.

  1. CI bundle-build-gate lane — builds every declared bundle from current source on every push/PR (mirroring app-bundle-gate's shape), then asserts the full declared set exists and smoke-runs the built verb-router (--help must exit 0). A broken entry or unresolvable import fails the build step; a build that silently drops a bundle reds the assert. The vitest suite never executes the shipped artifact — this lane is where a builds-but-dies bundle is caught.
  2. Upgrade-path rebuild hook — server-binary upgrades now rebuild the verb-router dists and refresh both copies (build output + the extension-side byte-copy the launcher execs), fail-closed before any server surface is touched, with receipt evidence (amicode_head, verb_router_sha256, verb_router_staged_sha256). A deployed upgrade can no longer leave a stale verb router behind.
  3. Module-type marker — the refreshed extension-side bundle gets a bin-scoped {"type": "module"} marker beside it, killing the per-invocation MODULE_TYPELESS_PACKAGE_JSON reparse warning. The VS Code extension manifest was deliberately not touched (adding "type": "module" there would flip the CJS extension-host entry); the marker is byte-identical to what the extension build's staging already writes, so both refresh paths converge.

Acceptance criteria — evidence

  • CI builds all bundles and fails on broken entry / unresolvable import — local replay of the CI steps: all five declared bundles PASS + verb-router smoke PASS, exit 0. Negative direction verified live by the director: mutating a real import to an unresolvable package makes the build step fail with ERROR: Could not resolve (the exact incident class, re-created and caught).
  • Upgrade path rebuilds + deploys dists with a receipt — the upgrade-server-binary suite extends with the tracer: success run → all five bundles byte-identical across both copies, receipt digests match the staged bytes; a failing build command aborts with the binary untouched and no receipt written; a half-built set aborts naming the missing bundle. 14/14 in that suite.
  • No module-type reparse warning — RED reproduced the warning on both the live deployed machine and the fixture; GREEN asserts the refreshed bundle spawns exit 0 with no warning on stderr. The VSIX packaging list now pins the marker as a required runtime asset.

Director gates (run independently on this branch)

  • pnpm --filter @amicode/amico-run build — clean, all five entries
  • node packages/amico-run/scripts/assert_built_bundles.mjs — all PASS, exit 0
  • pnpm --filter amico-run typecheck — clean
  • Full suite env-scrubbed — 75 files / 1193 passed / 13 skipped / 0 failed
  • Mutation check — unresolvable-import build fails, tree reverted clean

Design choices worth review

  • The hook lives in the server-binary lane specifically (not every upgrade verb): the receipts journal shows server-binary upgrades ran repeatedly during the stale window while the checkout's dists never refreshed; the extension verb's packaging already rebuilds them incidentally. No-op upgrades short-circuit at pre-flight and don't rebuild (no-op semantics preserved).
  • The declared bundle set is enumerated from the package's bin map — single source of truth shared with extension staging and packaging asserts — so a future sixth bundle is covered automatically.
  • --dist-build-command is the hermetic test seam, mirroring the extension verb's --package-command.

Ops follow-through (not repo work)

The live server's extension-side copies still hold the ops-refreshed bundles from 2026-08-30 minus the marker; the next amico upgrade server-binary run on that machine refreshes all five bundles and writes the marker under the new receipt. Trivial, but it's the closing step of the original incident.

Merge

Draft until CI runs the new lane on this very PR (its own first execution). Ready-for-review once green; merge is human-only.

…smoke-runs the verb router (#643)

The CLI bundles (packages/amico-run/dist/*.js) are gitignored build
artifacts; the deployed verb-router bundle sat 46 days stale because
nothing gated the build — a broken entry or unresolvable import only
failed on whichever machine last tried to build. New bundle-build-gate
job (mirrors app-bundle-gate's shape): install + the package's own
build + scripts/assert_built_bundles.mjs, which re-reads the package's
bin map (single source of truth, same map the extension staging and
assert_packaged_cli.mjs re-read) and asserts every declared bundle is
built non-empty, plus a verb-router smoke (amico --help exits 0 with
the usage surface) — the vitest suite transpiles and never executes
the shipped artifact, so a builds-but-dies bundle reds here. Both
directions pinned in test/bundle_gate.test.ts (real build green;
fabricated missing-bundle and dead-router red).
…r dists, with receipt evidence (#643)

The incident: the deployed amico bundle sat 46 days stale while
server-binary upgrades ran — the ledger verbs existed in source, were
absent from the deployed binary, and every ledger call silently
degraded for weeks. Deployed router freshness must not depend on
whoever last ran a local build.

The server-binary verb (the deployed-code lane) now rebuilds the CLI
bundles from the amicode checkout's current source and refreshes BOTH
copies — the build output (packages/amico-run/dist) and the
extension-side byte-copy the PATH-first launcher execs
(packages/extension/bin/dist) — between the artifact smoke (5) and
the freeze (6). Fail-closed by placement: a failed or incomplete
rebuild aborts BEFORE any server surface is touched, so the receipt
never lies about a half-deployed state.

- the declared bundle set comes from the amico-run package's bin map +
  shadowBins (the single source of truth the extension staging and
  CI's bundle-build-gate re-read); a build that drops a declared
  bundle is the stale-bundle signature and aborts
- receipt evidence: amicode_head (the commit the dists were built
  from), verb_router_sha256 + verb_router_staged_sha256 (the router's
  bytes on both sides)
- --dist-build-command is the hermetic stub seam (mirrors
  --package-command); the default is 'pnpm run build' in the package
  dir with a pnpm environment check
- fixture worlds carry the real amico-run bin map; the server-binary
  suite stubs the dist build through the shared verbArgs helper
…le — no MODULE_TYPELESS reparse (#643)

The ESM CLI bundles staged under packages/extension/bin sit inside the
VS Code extension manifest's package.json — which MUST stay typeless
(adding "type": "module" would flip the CJS extension-host entry
dist/extension.js and break vsce packaging). Without a scoped marker,
node reparse-warns (MODULE_TYPELESS_PACKAGE_JSON) on every invocation —
the deployed live machine paid exactly this, because its bin/ was
staged before the marker existed and refreshed by hand.

The dist-rebuild hook now writes the bin/-scoped {"type":"module"}
marker alongside the refreshed bundles — byte-identical to the one the
extension build's staging writes (esbuild.config.mjs), so both refresh
paths converge. Blast radius: zero changes to the extension manifest,
no .mjs rename (which would churn the staging convention, the launcher
exec path, and the packaging gates for no additional benefit).

Pinned from both sides: the upgrade test spawns the refreshed
extension-side bundle and asserts no MODULE_TYPELESS warning on
stderr; the VSIX packaging list pins extension/bin/package.json as a
required runtime asset (the packaged side of the contract).
@coderabbitai

coderabbitai Bot commented Aug 30, 2026

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: b3102be3-856b-47ef-8a1a-fb248cfdd5c0


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@aarontrowbridge
aarontrowbridge marked this pull request as ready for review August 30, 2026 12:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

BUG: deployed amico verb-router bundle goes stale — nothing rebuilds the dist on upgrade

1 participant