feat: migration nudges, inspect --bundler, and dev-watch of bin/lib (RFC #50 Phase 2, framework side) - #60
Conversation
…ndler, dev-watch of bin/lib
🦋 Changeset detectedLatest commit: 04c5084 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
commit: |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: cb0cffab7d
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if (model?.packageBuild === undefined) { | ||
| this.#last = undefined; | ||
| return outcome('absent'); |
There was a problem hiding this comment.
Remove stale outputs when the package build disappears
In agent-bundle dev, if the last package entry is removed—for example, src/cli.ts is deleted or configuration changes to bin: false with no library entry—the prepared model has no packageBuild, so this path merely clears in-memory state. The previously published dist/bin/... output remains on disk indefinitely, leaving consumers able to run a package executable that no longer exists in the current project configuration. Remove the outputs previously owned by the package build when transitioning to the absent state.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Fixed in d1d526a: when the prepared model no longer declares a package build (entries removed or bin: false/lib: false), the service now removes exactly the outputs this session previously published (tracked per file from the last successful build) and prunes then-empty directories, so dist/ never keeps serving executables the configuration no longer declares. Files the session never published are untouched, and cross-session staleness intentionally matches agent-bundle build (documented in docs/entry-conventions.md). Covered by new unit tests (removal, foreign-file preservation, root pruning) and an end-to-end coordinator step that deletes src/cli.ts and observes the bin output disappear.
| const identity = digest(model.packageBuild); | ||
| if (!this.#shouldRebuild(identity, invalidation)) { | ||
| return outcome('skipped'); |
There was a problem hiding this comment.
Include bundler tools in the rebuild identity
When a project uses conventional src/cli.ts or src/index.ts entries, changing tools.rsbuild or tools.rspack in agent-bundle.config.ts does not change model.packageBuild, and that config path is not among the conventional entries' recorded provenance inputs. Consequently the source-change invalidation is skipped even though the package build consumes prepared.tools below, leaving dist compiled with the old bundler configuration until another tracked input changes or dev restarts. Include the effective tools/config input in the identity or otherwise force a rebuild for the prepared config path.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Fixed in d1d526a: the rebuild identity now digests the tools hatch alongside the normalized bin/lib declaration (hatch functions compared by source text, which stays current because every preparation re-evaluates the config), and the configuration file path joined package.json/tsconfig.json as an always-rebuild input — so a hatch edit rebuilds dist/ even for conventional entries whose provenance never references the config. Covered by new unit tests: identical hatch source skips, edited mutator body rebuilds, rsbuild-fragment change rebuilds, config-path change rebuilds.
…d disappears; include the tools hatch and config path in the rebuild identity
Implements the framework-side Phase 2 items of RFC #50 (Phase 1 shipped in #52).
Summary
Migration nudge diagnostics (AB4730–AB4733, severity
info)Validate-time nudges for the pre-Phase-1 patterns the entry conventions now replace. Never errors — migrations stay optional per the RFC's additive-first principle. Codes follow the existing AB47xx package-build block (
AB470xbin,AB471xlib,AB472xtools →AB473xnudges):AB4730— a local stdio MCP entry (explicitentry:or conventional) whose module has no default export is self-connecting, so the build cannot wrap it in the framework lifecycle shell. Detection reusesscanEntryExportsSource, the exact static scan the build uses to decide the wrap, so nudge and build can never disagree.AB4731—src/cli.tsexists but explicitbinconfig never references it (shadowed convention).bin: falsestays silent (deliberate opt-out).AB4732—src/index.tsexists but explicitlibconfig points elsewhere.lib: falsestays silent.AB4733—src/mcp/<server-id>.tsexists but the declared server's explicitentry/command/urldoesn't resolve to it.Each nudge carries an "Optional: …" recovery describing both the adopt path and the silence path. Human
validateoutput now prints non-error diagnostics (they were previously invisible outside--json). All five examples validate clean — the nudges fire only on genuinely pre-convention projects.agent-bundle inspect --bundler(RFC §3.4)A third
inspectfocus (mutually exclusive with--hooks/--skills, same result envelope:selected.bundler). Dumps the synthesized bundler configuration for every output the build composes — per-target artifact scripts, MCP entries, hook wrappers, the per-target MCP Apps Rsbuild config, and thedist/package build — post-tools-hatch merge with the framework invariant hook visible last, exactly as lowered: the composition was extracted intocomposeEntryLibConfig(rslib) andcomposeMcpAppsRsbuildConfig(rsbuild) and both the build and the inspection call the same functions, so the dump cannot drift.Output shape:
{ entries: [{ bundler, kind, name, target?, source, outputPath, generatedEntry?, config }] }, sorted by (target, kind, name).configis the composed config JSON-rendered with functions as[function <name>](the consumer'stools.rspackmutators and the invariant hook stay visible in position).generatedEntrycarries the framework-provided wrapper module source for wrapped entries. Nothing is redacted; two per-build temp values are replaced with stable tokens for determinism:<output>/<target>(artifact output root) and<generated-dts-tsconfig>. Tested: a consumer with atools.rspackhatch seestools.rspack: [<hatch>, "[function enforceInvariants]"]and atools.rsbuildfragment merged over the profile, plus byte-identical repeated runs.Dev-watch of bin/lib outputs (RFC §3.5)
agent-bundle devnow rebuilds the framework-owned package build inside the same debounced, serialized rebuild pass that publishes artifact epochs (newDevPackageBuildService, wired intoDevCoordinatorafter epoch commit). Incremental discipline is provenance-based: after a successful package build the service keeps the source inputs of every emitted file (recorded from bundler stats), and skips the next rebuild unless an invalidated path was one of those inputs,package.json/tsconfig.jsonchanged, the normalizedbin/libdeclaration changed, the invalidation wasmanual/initial, or the previous package build failed.Boundary, stated honestly: a package build failure never invalidates the artifact epoch that already committed — it surfaces as one
AB7103warning on the succeeded attempt and retries on the next invalidation; and a brand-new file that changes module resolution without touching a tracked input is only picked up on the next tracked change. No fake watch: an end-to-end integration test drives a real coordinator through initial build → untracked-change skip (dist mtime unchanged) → entry-change rebuild (new dist content).Docs
docs/diagnostics.md— diagnostics reference (severity model, code families, detailed AB473x + AB7103).docs/entry-conventions.md— migration nudges,inspect --bundlercontract and tokens, the dev-watch section; the "dev never writes dist/" stance updated.agent-bundleminor.Test plan
pnpm build— greenpnpm typecheck(root + workbench) — greenpnpm lint— 0 errors, 0 warningspnpm test:unit— 1730 passed (includes newdev-package-build-service,inspect-bundler, and nudge suites)public-api-packed— green (declaration graph now reaches@rslib/core, a runtime dependency; the source-tree consumer fixture gained the same symlink@rsbuild/@rspackalready had)agent-bundle validate --jsonon all five examples — zero diagnostics (no stray nudges)public-api-packedwas not rerun.Refs #50.