Repository navigation
Infer plugin version from package.json and expose a build-time version constant to plugin code #237
Description
Problem
Plugin projects currently declare release identity in multiple places. The cargo-hauler consumer plugin is a concrete example:
package.json"version"(npm release identity)agent-bundle.config.tsplugin.version(required config field)src/lib/version.ts— a hand-writtenexport const packageVersion = '…'because runtime code (daemon status, CLI, MCP application registry) and browser widget bundles need an importable version constant and cannot readpackage.jsonat runtime
The plugin also carries a version-consistency test that asserts all three agree — slop the framework forces authors to maintain until release identity has one source and compiled surfaces get the value automatically.
Current framework behavior (evidence)
Config plugin.version is still required
The public config type requires a version string:
// packages/agent-bundle/src/core/types.ts
export interface AgentBundlePluginConfig {
description?: string;
name: string;
version: string;
[key: string]: unknown;
}Source validation rejects missing/empty values with AB4001:
// packages/agent-bundle/src/config/validate.ts
if (typeof pluginVersion !== 'string' || pluginVersion.trim().length === 0) {
diagnostics.push(
sourceDiagnostic(
'AB4001',
'Plugin metadata must define a nonempty version.',
loaded.configPath,
),
);
}Normalization still copies the authored config value into model metadata; it does not infer from package.json when omitted:
// packages/agent-bundle/src/config/normalize.ts
// package.json is authoritative for release identity (issue #94), while plugin.version
// remains the host-facing declared version during the migration.
version: loaded.config.plugin.version,Issue #94 stage 1 is partially landed: snapshotPackageIdentity() reads package.json name/version (packages/agent-bundle/src/core/project-context.ts), and a mismatch with plugin.version emits warning AB4008 — but the config field remains mandatory and there is no default/fallback during normalization.
The audiobook-curator example documents the interim contract explicitly: package.json is the single version source, config must restate it until plugin.version becomes optional (#94 stage 3).
No build-time version constant for plugin source
Search of the build pipeline (packages/agent-bundle/src/build/**, MCP App Rsbuild config in mcp-apps.ts, Rslib composition in rslib.ts) shows:
- No framework-provided virtual module such as
agent-bundle:meta - No injected
source.define/ compile-time constant carryingname/versioninto plugin-authored modules - Virtual sources exist only for generated entry shells, hook wrappers, MCP route workers, etc. — not for exposing project metadata to consumer source
Browser widget bundles (e.g. cargo-hauler views/dashboard-lib.ts) therefore cannot import release identity from the framework and rely on the hand-written src/lib/version.ts shim.
Requested design
(a) Config plugin.version optional with package.json default
- Make
plugin.versionoptional in config types and validation - When omitted, infer from
package.jsonversion during normalization (same axis already read bysnapshotPackageIdentity) - When both are present, validate they agree (upgrade AB4008 from warning to error once inference lands, or keep warning during migration per Derive project identity from package version and compiler revisions #94 policy)
- Release builds without valid package identity continue to fail closed (Derive project identity from package version and compiler revisions #94)
(b) Framework-provided build-time version constant
Expose release identity to all compiled surfaces, including browser MCP App/widget bundles, via one of:
- a virtual module (e.g.
agent-bundle:metaexporting{ name, version, packageName, packageVersion }), or - a compile-time define injected consistently across Rslib route builds and Rsbuild MCP App builds
Goal: plugin runtime code imports the framework constant instead of maintaining src/lib/version.ts. The constant must reflect the same inferred identity used in manifests, dev status, and host projections.
(c) Release flow
One npm version patch (or equivalent) should be the entire bump — no parallel edits to config and hand-written version modules. Generated/consistency tests should pin the invariant automatically rather than requiring each plugin to author its own triplication police test.
Related issues
- #94 Derive project identity from package version and compiler revisions — parent identity migration; this issue is the missing plugin-source compile-time constant slice plus completing optional config inference
- #103 Generate an Agent Bundle framework test harness on top of Rstest — generated harness should assert version consistency across source, artifact, and projections without per-plugin boilerplate
- #218 Generated plugin contract matrix — installed-host smoke already calls out reporting source/artifact/installed/running versions separately; framework-injected identity should feed those checks
Acceptance criteria
- A packaged plugin can omit
plugin.versionfrom config whenpackage.jsonversion is valid - Plugin runtime modules (Node routes, daemon, CLI) and browser widget bundles can import framework-provided name/version without a local
version.ts - Build, inspect, dev status, artifact manifests, and compiled surfaces agree on the same version axis
- cargo-hauler (or equivalent fixture) can delete
src/lib/version.tsand its hand-written consistency test once the framework owns the constant
Activity
ScriptedAlchemy commentedon Sep 2, 2026
Delivered in #270 (merge 09067f6).
plugin.versionis optional. Omitted → derived frompackage.jsonversion during normalization (the same axissnapshotPackageIdentityreads). Declared → still wins during the migration, with the AB4008 mismatch warning per Derive project identity from package version and compiler revisions #94 policy; declared-but-empty/non-string stays an AB4001 error. Release builds without any release identity fail closed with the new AB4013 (agent-bundle buildis exactly the release boundary;dev/inspect/validatekeep the labeled0.0.0-dev.<short-revision>fallback).agent-bundle/metapublic subpath exporting{ name, version, packageName, packageVersion }, injected as a generated virtual module into every compiled surface — Node route/CLI/MCP/hook/script/package bundles via the Rslib composition and browser MCP App/widget bundles via the Rsbuild engine — always matching what manifests,inspect, and dev status report. Types ship with the package export map (no generated.d.tsneeded). Outside a compiled surface the published module throws a named error. It is a reserved specifier thetoolsescape hatch cannot externalize.npm version patchis now the whole bump:examples/audiobook-curatordropped its restatedplugin.version, andexamples/mcp-appfeedsMcpServer({ name, version })fromagent-bundle/meta— thesrc/lib/version.tsshim-deletion pattern cargo-hauler can now follow, including deleting its hand-written version-consistency test (themcp.test.tsfixture pins source/artifact/browser-bundle agreement in-framework).Note: the fail-closed code landed as AB4013, not AB4012 — AB4012 was claimed by
plugin.logovalidation (#246/#267) which merged first. Docs (docs/diagnostics.md"Release identity",docs/entry-conventions.md"agent-bundle/meta") cover the full contract.