Skip to content

feat(build): hold the package build's dist bundles to AB6005 - #588

Merged
ScriptedAlchemy merged 8 commits into
mainfrom
feat/package-build-ab6005
Sep 5, 2026
Merged

feat(build): hold the package build's dist bundles to AB6005#588
ScriptedAlchemy merged 8 commits into
mainfrom
feat/package-build-ab6005

Conversation

@ScriptedAlchemy

@ScriptedAlchemy ScriptedAlchemy commented Sep 5, 2026

Copy link
Copy Markdown
Owner

Rule

The npm package build's dist JavaScript is held to the same self-containment rule as the host packs. Every emitted dist/**/*.js and *.mjs bundle — dist/bin/<name>.js, its rendered-route Flight worker, the generated install bin, and the lib entry dist/<name>.js — is walked as an ES module before dist is published, and a bare import specifier that is not a Node built-in fails the build. There is no escape hatch: an import a tools.rspack/tools.rsbuild hatch keeps external (output.externals) used to survive in dist and be judged only by the prepack gate; it now fails the package build exactly as it already failed a host pack.

What stays valid: node: and bare built-in specifiers (isBuiltin), relative imports of emitted files, import.meta, createRequire(import.meta.url) of packed files. A literal dynamic import() counts like a static import (the walker already did this — confirmed, and the fixture covers it); a non-literal dynamic import is a finding. .d.ts output is not walked; declarations may still reference declared dependencies (check-declaration-imports covers the framework's own packages). The walk reads import specifiers: a require, createRequire(…)(…), or import.meta.resolve(…) call is not an import and is outside AB6005 in dist as in a host pack — see Self-review and #591.

Code chosen: AB6005, reused

AB6005 is documented as "Generated JavaScript import from <path> uses unsupported specifier <specifier>", with recovery "Bundle every JavaScript dependency into the artifact, then rebuild it." Its generatedPath is the only context it needs: the package build reports the consumer-visible path (dist/bin/<name>.js, dist/index.js), so the message reads correctly for dist without a sibling code. No new code allocated.

The existing walker is reused, not forked:

  • validate-artifact-modules.ts: validateJavaScriptModules gains a reportedRoot option so a tree validated in its staging directory is reported under the path a consumer sees (dist/… rather than the stage-relative bin/…). resolveJavaScriptImport takes the reported importer separately from the artifact-relative one it resolves against.
  • package-build.ts: after the invariant assertions and before publishArtifact, walks the staged tree with validateJavaScriptModules and throws DiagnosticError on any finding, so nothing is published and the stage directory is removed (the fixture asserts both).
  • module-imports.ts: bundleSyntaxCheckFor(tools) is extracted from build.ts so the artifact and package builds decide "lexed vs parsed" the same way (a tools hatch that can rewrite emitted assets gets a full parse). build.ts is rewired to it in the same change.

Prepack interplay (AB7014)

prepack always runs the build before the pack inventory. With dist now failing AB6005 on a bare import, a dependency imported only by a compiled bundle is a build failure before AB7014 is ever evaluated — the two gates cannot disagree. The "a packed JavaScript module imports the dependency" evidence class stays, and is now, by construction, import evidence from modules the framework copied rather than compiled: prebuilt payload modules (#574/#577) and other scripts the files allowlist packs. The require/createRequire/import.meta.resolve evidence is different — those are calls AB6005 does not walk, so they are still read from every packed file, compiled bundles included. AB7014's recovery text no longer suggests importing a package from a compiled bundle; it names what still keeps a runtime dependency. docs/diagnostics.md updated for both codes; pack-dependencies.ts / pack-inventory.ts docblocks say the same.

Fixture proofs

packages/agent-bundle/tests/package-build.test.ts

  • fails the package build with AB6005 when the tools hatch keeps a dependency external in a dist bundletools.rsbuild.output.externals: ['left-pad', 'right-pad']; src/cli.ts imports left-pad statically and right-pad through a literal dynamic import(); src/index.ts imports left-pad. The build rejects with DiagnosticError whose diagnostics are exactly AB6005 × 3: dist/bin/package-build-fixture.js"left-pad", dist/bin/package-build-fixture.js"right-pad", dist/index.js"left-pad"; no dist exists afterwards and no .dist.stage-* is left behind.
  • accepts Node built-ins under node: and bare specifiers in dist bundlesnode:fs, node:module, bare path, createRequire(import.meta.url): build passes, the bin runs, and the emitted source still carries those imports by specifier.

packages/agent-bundle/tests/prepack.test.ts

  • accepts a dependency that only a prebuilt payload module importspayload: { runtime: './built/runtime' } with a copied mcp/server.js that imports express (declared): prepack passes, no AB6005, no AB7014, the module is packed at host-packs/cursor/runtime/mcp/server.js.
  • fails prepack with AB6005, never AB7014, when only a compiled dist bundle imports a declared dependency — externalized left-pad in a lib entry: prepack rejects with AB6005 on dist/index.js and no AB7014 is reported.

Docs

AGENTS.md ("Generated plugin output"), docs/entry-conventions.md (tools section), docs/diagnostics.md (AB60xx row, AB7014 row, evidence paragraph), website/docs/{en,zh}/guide/distribution/validation.mdx (the does not walk sentences and the prepack section). Changeset: minor for agent-bundle.

Concurrency note

#578 (composite root) and its successor #569 were both closed unmerged, so there was nothing to wait for. The branch has been merged with origin/main three times as it moved (#575 Rslib 1.0 / #583 / #581, then #582 serve-app-command, then #586 / #580 / #585); every gate below ran on the final merged tree. src/build/mcp-apps.ts was not touched by this PR (only by the #585 merge from main).

Gates (final merged tree, local)

  • pnpm typecheck ✓, pnpm lint ✓ (0 errors, 0 warnings), pnpm test:unit ✓ (247 files, 3774 tests, 0 failed)
  • integration pool pnpm test:integration:run ✓ (94 files, 1148 tests, 0 failed, 33 skipped) — includes package-build.test.ts (18/18) and prepack.test.ts (58/58)
  • packed pool pnpm test:packed ✓ (12 files, 34 tests, 0 failed, 1 skipped)
  • release-only scaffolder template matrix node scripts/run-packed-tests.mjs --release packages/create-agent-bundle/tests/scaffold-packed-matrix.e2e.test.ts ✓ (mcp-server template with a lib entry, cli-tool template with a routed bin, lib, and artifact script — both build, check, validate, and prepack clean against the packed tarball)
  • pnpm examples:check ✓ — all seven examples build, validate, typecheck, and test (audiobook-curator and host-test are the two package-build consumers with dist/bin)
  • pnpm docs:site:build ✓ (dead-link, dead-anchor, language parity)

Self-review

Reviewer: generalPurpose subagent on gpt-5.6-sol-medium (the change-risk-reviewer MCP was in an error state), asked for concrete merge risks against origin/main and specifically whether any legitimate package-build output — Workbench bundles, serve-app bins, create-agent-bundle templates — would now fail.

Pass 1 — one finding (reviewer severity: blocker). AB6005 consumes es-module-lexer import records only; it does not read require("pkg"), createRequire(import.meta.url)("pkg") (or a bound loader), require.resolve, or import.meta.resolve("pkg"), so a compiled module can still load a package from the consumer's node_modules through one of those calls, and the prose claimed more ("loads nothing from a consumer's node_modules", "compiled bundles can never supply import evidence").

  • Disposition: code change dismissed for this PR; prose defect fixed. The gap is a property of the existing AB6005 walker shared by every host-pack build (it has never read those call forms), and this PR's scope is to reuse that walker for dist without forking or redesigning it; extending it changes host-pack validation too. Tracked as AB6005: also refuse bare createRequire/require/import.meta.resolve loads in compiled modules #591 with the proposed design (reuse pack-dependencies.ts's scanner through a leaf module, compiled modules only; also closes the externalsType: 'node-commonjs' route). The two gates agree today: pack-dependencies.ts reads exactly those call forms as AB7014 evidence from every packed file, so a package loaded that way must be declared and is accepted. Commit cdb70bd9 rewords package-build.ts, pack-dependencies.ts, pack-inventory.ts (docblock, comment, and the AB7014 recovery string, which now also names "a packed file requires or resolves"), AGENTS.md, docs/diagnostics.md, validation.mdx en+zh, and the changeset to say exactly what the code does.
  • Verified with no issue by the reviewer: (A) no legitimate output fails — audiobook-curator and host-test dist use only built-ins externally, Flight workers and install bins are included in the walk, agent-bundle/serve-app-command is inlined into routed bins, the three scaffolder templates declare no runtime dependencies or externals, and the Workbench is built by its own Rsbuild config (out of scope); (B) reportedRoot plumbing resolves against the stage-relative importer and only names the reported path, with the package output root fixed to dist; (C) validation precedes publishArtifact, a failure removes the stage and leaves any prior dist untouched; (D) validJson: new Set() and bundledPaths are correct for what the package build can emit, and a tools hatch selects the full parse; (E) the fixtures prove the exact diagnostics, output/stage cleanup, built-ins, prebuilt evidence, and prepack ordering; (F) en/zh structure parallel, diagnostic and recovery text match the code, changeset is one minor for agent-bundle ending in (#588).

Pass 2 (after cdb70bd9) — disposition accepted; one should-fix. Four places still said AB6005 rejects "any bare specifier" (changeset, docs/entry-conventions.md, validation.mdx en and zh) and entry-conventions.md said the hatch "cannot externalize a dependency". Fixed in f80da9eb: "bare import specifier" everywhere, and the hatch claim narrowed to imports with the call forms named as outside the walk. No other findings; en/zh additions claim-equivalent; no stale assertion on the recovery string.

The npm package build now walks every emitted dist/**/*.js|*.mjs with the
same ESM import-graph walker the artifact build runs over host-pack modules
(validateJavaScriptModules), before dist is published: a bare specifier that
is not a Node built-in — including an import the tools hatch kept external —
fails the build with AB6005 naming dist/<path> and the specifier. A dependency
only a compiled bundle imports is therefore a build failure and never an
AB7014 finding; AB7014's recovery names what still keeps a runtime dependency.
bundleSyntaxCheckFor is shared by the artifact and package builds.
…6005

# Conflicts:
#	docs/entry-conventions.md
…6005

# Conflicts:
#	website/docs/en/guide/distribution/validation.mdx
#	website/docs/zh/guide/distribution/validation.mdx
@changeset-bot

changeset-bot Bot commented Sep 5, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: fae6c20

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
agent-bundle Minor

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

@pkg-pr-new

pkg-pr-new Bot commented Sep 5, 2026

Copy link
Copy Markdown
npm i https://pkg.pr.new/ScriptedAlchemy/agent-bundle@588
npm i https://pkg.pr.new/ScriptedAlchemy/agent-bundle/create-agent-bundle@588
npm i https://pkg.pr.new/ScriptedAlchemy/agent-bundle/rsc-markdown-stream@588
npm i https://pkg.pr.new/ScriptedAlchemy/agent-bundle/@agent-bundle/runtime@588

commit: fae6c20

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ea16ac714b

ℹ️ 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".

Comment on lines +379 to +383
const selfContainment = await validateJavaScriptModules({
artifactRoot: stageRoot,
bundledPaths: new Set(files.filter((file) => file.kind === 'bundle').map((file) => file.path)),
bundleSyntaxCheck: bundleSyntaxCheckFor(options.tools),
files: staged,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Cover CommonJS-form externals in the dist check

When a tools hatch emits an external in CommonJS form—for example tools.rspack.externals = { 'left-pad': 'node-commonjs left-pad' }, a syntax already exercised in packages/agent-bundle/tests/build.test.ts—Rspack writes a require("left-pad") or createRequire(...)("left-pad") load. validateJavaScriptModules delegates to readModuleImports, whose es-module-lexer pass reports only ESM imports, so it sees the node:module import but not left-pad; AB6005 is therefore skipped, dist is published, and prepack’s separate dependency scanner accepts the require as usage. Scan CommonJS loads too or reject CommonJS external forms.

AGENTS.md reference: AGENTS.md:L73-L76

Useful? React with 👍 / 👎.

Comment thread .changeset/package-build-ab6005.md Outdated
"agent-bundle": minor
---

Hold the npm package build's `dist` JavaScript to the same self-containment rule as the host packs: `agent-bundle build` and `agent-bundle prepack` now walk every emitted `dist/**/*.js` and `*.mjs` bundle (`dist/bin/<name>.js`, its rendered-route Flight worker, the generated install bin, and the `lib` entry) as an ES module and fail with `AB6005` on any bare specifier that is not a Node built-in — including an import kept external through the `tools` escape hatch, which previously survived in `dist` and was judged only by the prepack gate. The diagnostic names the file as `dist/<path>` and the specifier; a literal dynamic `import()` counts like a static import; `node:` and bare built-in imports, relative imports of emitted files, `import.meta`, and `createRequire` of packed files stay valid, and `.d.ts` output is not walked. A dependency that only a compiled bundle imports is therefore an `AB6005` build failure before the pack inventory runs, never an `AB7014` finding; `AB7014`'s recovery now names what still keeps a runtime dependency — a prebuilt payload module that imports it, a packed declaration that references it, or an install script or packed file that runs it. (#TBD)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Replace the placeholder changeset PR number

The changeset ends with (#TBD), so the generated user-facing release notes will retain a placeholder instead of identifying the originating PR. Replace it with the actual (#<PR>) suffix required by the repository’s changeset format.

AGENTS.md reference: AGENTS.md:L141-L142

Useful? React with 👍 / 👎.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-05T04:08:03.134051Z ea16ac7 PR opened
ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

Copy link
Copy Markdown
Owner Author

Architecture alignment with #592: this self-containment work is consistent with the intended Artifact IR boundary. Keep AB6005/module validation as common artifact validation rather than package-build-specific policy over time.

When the composite-root/package-root work is rebased, the desired invariant is: every compiled executable surface is validated by the same Artifact IR/module policy regardless of whether it is reached through a host manifest, routed CLI, browser host, or npm package entry. #591 can extend the scanner without introducing another package-only validation path.

@ScriptedAlchemy
ScriptedAlchemy enabled auto-merge (squash) September 5, 2026 05:20
@ScriptedAlchemy
ScriptedAlchemy merged commit d30d9ac into main Sep 5, 2026
16 checks passed
@ScriptedAlchemy

Copy link
Copy Markdown
Owner Author

Confirmed against the merged tree: there is one walker, not a package-build copy.

  • validateJavaScriptModules in packages/agent-bundle/src/build/validate-artifact-modules.ts is the AB6005 module walk. It has exactly two production callers — validate-artifact.ts (host packs, artifact-relative paths) and package-build.ts (the staged dist tree). resolveJavaScriptImport is private to that module; git grep -n 'validateJavaScriptModules\|resolveJavaScriptImport' packages/agent-bundle/src shows no second implementation.
  • bundleSyntaxCheckFor in module-imports.ts is likewise shared by build.ts and package-build.ts, so lexed-vs-parsed is decided once for both surfaces.

What remains package-specific is confined to package-build.ts and is plumbing, not policy:

  1. the reportedRoot mapping, so a finding names dist/bin/<name>.js / dist/index.js (the path a consumer sees) rather than the stage-relative path; and
  2. the publish abort — a non-empty result throws DiagnosticError before publishArtifact, so the stage directory is removed and no dist is written.

No rule, message, resolution step, or accepted-specifier set lives outside the shared walker; validJson: new Set() and bundledPaths are the package build describing its own tree to the common policy.

#591 extends that same walker: the literal require / createRequire / import.meta.resolve scanner moves out of pack-dependencies.ts into a leaf module both gates import, so the added forms reach host packs and dist through the one call path — no second scanner for package output. The prose that still describes those calls as outside AB6005 (AGENTS.md, docs/diagnostics.md, validation.mdx, the AB7014 recovery string) is rewritten there too.

@ScriptedAlchemy
ScriptedAlchemy deleted the feat/package-build-ab6005 branch September 5, 2026 05:52
ScriptedAlchemy added a commit that referenced this pull request Sep 5, 2026
…ost-packs/runtime/…), not under a target partition
ScriptedAlchemy added a commit that referenced this pull request Sep 5, 2026
…ap P3s (#599)

* ci(docs): drift, built-link, and diagnostics-coverage gates for the docsite; pin docs.yml actions; Dependabot scans composite actions (#590 lane E)

website/scripts (plain Node >= 22, no dependencies), wired into the website
`check` script that `pnpm docs:site:build` and docs.yml run:

- check-locale-drift.mjs: every authored en/ page needs a zh/ twin with the
  same fenced-code count, the same code per fence (comments stripped in
  sh/bash/ts/tsx/js/json fences, trailing `# note` in text tree listings),
  the same ABnnnn code set, the same table-row count, and an h2/h3 count
  within 2; _meta.json/_nav.json must match in entries, order, and link
  targets modulo the /zh prefix. Rspress's languageParity compares only the
  set of page paths.
- check-built-links.mjs: post-build walk of doc_build/**/*.html (+ sitemap
  <loc>s) resolving every site-internal href/src/og:url honouring cleanUrls
  and requiring each #fragment to name an id in the target. Rspress's own
  checks see only mdast links, so frontmatter hero/feature links, _nav.json,
  raw <a href>, and every generated page were unchecked, and a warm
  persistent cache let dead anchors into unchanged pages pass.
- check-diagnostics-coverage.mjs: every ABnnnn cited in website/docs
  {en,zh}/**/*.mdx and (strict, `--no-src` to skip) every ABnnnn literal in
  packages/agent-bundle/src must have an explicit row, range row, or code
  heading in docs/diagnostics.md; family catch-all rows do not count.

website `check` builds with RSPRESS_PERSISTENT_CACHE=false (the variable
@rspress/core reads to disable Rsbuild buildCache) so the anchor checks run
against a cold compile. Drops the two website devDependencies nothing
imports (@types/react-dom, agent-bundle); lockfile regenerated with
`pnpm install --lockfile-only`.

The root `typecheck` now also runs the website tsconfig (config, plugins,
theme), which only `docs:site:build` compiled before.

docs.yml pins actions/checkout, upload-pages-artifact, configure-pages, and
deploy-pages to full commit SHAs (job name unchanged; it is a required
check). dependabot.yml scans /.github/actions/* so the composite
setup-workspace action's pnpm/setup pin is updated too.

* docs(site): feed twoslash from tsconfig.typedoc.json paths, SSG worker, sidebar/nav tidy-ups (#590 lane A2)

- rspress.config.ts reads `compilerOptions.paths` from tsconfig.typedoc.json
  (JSONC, via ts.readConfigFile), makes the targets absolute, and merges them
  with the four `agent-bundle*` entries for pluginTwoslash, so
  `@agent-bundle/runtime*` and `rsc-markdown-stream` resolve to workspace
  sources without `pnpm build`. The 12 `: any` hovers on
  guide/development/testing (en + zh) are gone; the tsconfig comment now
  states that this file is the single source of the map.
- ssg.experimentalWorker: true; builderConfig.performance.printFileSize.detail: false.
- head: static theme-color meta. Per-route canonical/og:url functions are
  left out: renderPages passes config.head to the SSG worker threads through
  workerData, and a function fails structured cloning (DataCloneError).
- themeConfig.footer: Apache-2.0 message (home layout).
- Remove transformerNotationDiff/Focus (no [!code ++/--/focus] in docs) and
  search.codeBlocks (restates the default); TypeDoc index title template
  drops the empty {version} placeholder.
- api/_meta.json: collapsed: true on the six dir groups, both locales.
- _nav.json: "Type API" links to the hand-written /reference/api overview;
  activeMatch covers /api/ and /reference/api, both locales.
- mirror-api-locale: per-target `notice`, inserted as an :::info container
  directly under the title of every mirrored zh/api page.
- docs/public/robots.txt with the sitemap URL.

* docs(diagnostics): document every emitted AB0000–AB5999 code explicitly

Add explicit rows for the 103 emitted codes in AB3000–AB4716 that were
covered only by a family catch-all (AB30xx, AB40xx–AB46xx, AB470x/AB471x),
plus dedicated sections for AB4500 and AB5000, whose only mention was the
Code families summary row. New sections sit after the Code families table.
Adds the missing AB4204 and AB5000 rows; gives AB4716 a table row in its
Declaration generation section; folds the AB4834 recovery text into its
Trigger cell so the row matches its three-column header (GFM was dropping
the fourth cell). Every emitted code in AB0000–AB5999 now has an explicit
row; no ragged table rows remain in the file.

Refs #590

* docs(en): accuracy fixes, description lengths, and fence reflow for #590 (lane C)

- reference/cli: exit-code rows 1/2 state which option validators throw
  Commander InvalidArgumentError (exit 2) versus a plain TypeError reported
  as one AB5000 diagnostic (exit 1), per src/cli.ts and runCli.
- guide/authoring/hooks: result-contract table and per-event bullets match
  hook-handler.ts / hook-contract.ts — no event admits outcome 'stop',
  Cursor alone tolerates a denying agentStart, only Claude carries
  additionalContext from agentStop.
- reference/configuration: targets defaults to ['portable'], marketplace to
  false; evals and routes ride the AgentBundleConfig index signature, evals
  rules fire as EVAL_* errors when an eval or the Workbench loads the config.
- Tighten 13 en frontmatter descriptions to <=160 code points.
- Reflow every fenced code line over 90 columns in en (36 -> 0) and mirror
  the identical reflow in the zh twin fences (28 -> 0); zh prose untouched.

* docs(zh): mirror #590 accuracy fixes — CLI exit code 2 scope, hook result contract, config defaults and untyped evals/routes

Chinese twins of the lane-C English corrections for the docsite audit:

- reference/cli.mdx: exit code 2 is only a Commander error (unknown option,
  missing argument, InvalidArgumentError from --profile/--allow/doctor --host);
  the install <host>/--scope/--mode/--port/--trials validators throw a plain
  TypeError that runCli reports as one AB5000 diagnostic with exit 1.
- guide/authoring/hooks.mdx: outcome is continue or deny (no event accepts
  stop); agentStop additionalContext is rejected on Codex and Cursor, only
  Claude Code carries it; the typed contract rejects an agentStart denial
  while Cursor's wrapper tolerates one with a reason.
- reference/configuration.mdx: targets defaults to ['portable'] (--target
  overrides), marketplace defaults to false; evals and routes ride the
  index signature so tsc does not check them — evals rules fire at eval time
  (EVAL_CONFIG_INVALID), routes overrides are validated during route
  discovery and reported through validateSource.

Prose only; no fenced code block, heading, table row, or AB code set changed.

* docs(website): nav matcher excludes reference/api; drift check normalises regex alternatives

* docs(diagnostics): document every emitted AB6000–AB9999 code (#590, lane B2)

Add explicit rows or explicit ranges for the 121 agent-bundle codes in
AB6000–AB9999 that only a family row covered: built-artifact validation
(AB6000–AB6004, AB6006–AB6018, AB6023–AB6025), Workbench artifact
inspection (AB6200–AB6202), install/uninstall and project-preparation
codes (AB7000–AB7004), the AB7103 package-build warning, the whole
AB80xx development-server surface grouped by route module, the
route-manifest client code AB8123, and the eval API refusals
AB9001–AB9005/AB9007–AB9011. Refine the AB6xxx–AB9xxx family rows.
AB6005 is left untouched for PR #588.

* fix(website): patch @rspress/core LocalProvider.init to await FlexSearch indexing (#590)

LocalProvider.init() fired addAsync() for every document without awaiting,
so SearchPanel flipped initStatus to 'inited' while FlexSearch was still
indexing and an early query rendered 'No matching results' for documents
that had not been added yet (8/8 trials with a late-indexed query on the
971-document en index). Collect the three addAsync promises per item and
await them before init() resolves; PageSearcher hard-codes the provider, so
a pnpm patch is the smallest fix. Upstream main still has the bug.

* fix(website): edit links, llms rows, 404 trailing slash, and code width in the docsite theme (#590)

- EditLink: return null on TypeDoc pages (<lang>/api/**), point the
  generated reference pages at their sources (capabilities JSON directory
  for hosts/events/notices, docs/diagnostics.md for diagnostics), and defer
  to the original everywhere else. The patterns track .gitignore lines 20-27.
- LlmsCopyRow / LlmsOpenRow / LlmsHint: render nothing when the route path
  includes /api/, mirroring the pluginLlms exclude, so the outline no longer
  offers Copy Markdown / Open in chat for the 1,832 routes that have no
  Markdown twin.
- NotFoundLayout: cleanUrls emits quick-start.html, so a trailing-slash URL
  is a 404 on GitHub Pages; retry without the slash (query and hash kept).
- Desktop layout tokens (>= 1280px): sidebar 320 -> 280, outline 268 -> 240,
  content padding 80 -> 48. At 1440x900 the code scroller grows from 686 to
  818 px, so 92 monospace columns fit (was 76 once Shiki's line padding is
  paid) and a 90-column line no longer scrolls; overflowing fences on the 33
  authored en pages drop from 59/169 to 23/169, all of them >= 93 columns.

* docs(diagnostics): explicit AB6005 row (restates #588) so every emitted code has a row

* changeset: diagnostics contract rows (#599)

* docs(diagnostics): shorten the install-receipt placeholder so the fence fits 90 columns

* chore(patches): cite upstream rspress#3658 beside the @rspress/core patch

* review: built-link check rejects targets outside doc_build; changeset says recovery where the diagnostic carries one
ScriptedAlchemy added a commit that referenced this pull request Sep 5, 2026
…ections; remove the plugin target (#555 W1) (#578)

* feat(targets): remove the plugin target from the public target model (#555 S1)

- config/normalize.ts no longer synthesizes a plugin lowering target or a
  shared plugin skill document; loweringHosts is a pure isSkillHost filter.
- adapters/registry.ts registers portable, codex, claude, cursor only;
  adapters/plugin.ts is deleted together with the composite-only capability
  helpers (intersect/union/mergeCapabilityEvidence, capabilityBooleanView).
- targets: ['plugin'] and --target plugin fail with the existing AB4100.
- plugin branches removed from api.ts, pack-inventory, package-build,
  entry-shell, events/projection, lifecycle replay, install/surface,
  install/install, create-agent-bundle options/scaffold, capability JSON,
  and the CLI help text.
- Tests: plugin-bundle.test.ts deleted; plugin rows dropped from the
  adapter, install-surface, normalization and lifecycle suites; acceptance 3
  covered by normalization.test.ts and cli.test.ts.

* feat(build): plan the selected projections into one composite root (#555 S2)

build/build.ts stages every selected host projection into one root at
artifactRoot; build/compose.ts merges byte-identical entries once, fails
same-path collisions with AB4103 in host-name/path order, and refuses
host-scoped components another selected host would discover conventionally
with AB4105. Codex and Cursor hook/MCP documents move beside their manifests;
shared hooks compile to hooks/<name>.<host>.mjs; compiled surfaces are
attributed to the sorted composite identity; the artifact manifest records
only the selected projections. Install, doctor, dev services, eval harnesses,
Workbench, examples, and tests follow the composite root.

* feat(build): AB4106 for mixed advanced-registry selections; validate/inspect judge the composite root; port #569 consumer and docs fixes

- config/validate.ts: AB4106 when an adapter registered on an advanced
  TargetRegistry is selected beside any other target; the built-in host list
  moves to adapters/composite-layout.ts and install/surface.ts imports it.
- build/compose.ts: planComposite returns the plan beside its diagnostics;
  dev/project-service.ts prepare uses it so validate, inspect, dev report
  AB4103/AB4105 exactly where build refuses (caught by the composite-rules
  parity test ported from #569).
- Ported from #569 (superseded): tests/support/mcp-conformance.ts pluginRoot,
  tests/skill-document-service.test.ts decoy, examples/audiobook-curator and
  examples/hooks-and-scripts READMEs, scripts/measure-hook-cold-start.mjs.
- tests/composite-rules.test.ts: layout-independent rules from #569 (four-host
  root, commands/ dialect collision, INSTALL.md sections, validate parity).
- Stale per-host wording: Cursor capability evidence, doctor/types/cursor
  comments, create-agent-bundle README, docs/diagnostics.md, canvas sample.

* test(packed): read the composite root's bin/ and single mcp/ entry in the packed pool

* fix(inspect): describe the same event allowed-targets set the build bakes into a generated-route server; AB4105 trigger names skills

* build: carry #585's MCP App compile diagnostics through the composite root; mcp-apps-compile test selects the portable projection

* test(cli): MCP App compile report attributes the view to the composite selection identity (codex+portable), matching planCompiledMcpApps after the #585 merge

* build: identify the event runtime by the artifact alone; port #569's nested-root and shared-root tests (#592 boundary)

The generated MCP entry and every hook wrapper derived the event runtime's
endpoint id from `<epoch>:<selection identity>:<root>`, so the composite
selection (`claude+codex`) had become runtime identity, and the runtime
took `events.target` as the tool-call lineage host fallback, reading the
selection as a host. Both are projection selection leaking into runtime
identity (#592 §2). The endpoint is now `<epoch>:<root>` on both sides
(entry-shell, hook-contract, `agent-bundle/test` installed harness), the
`target` field leaves `GeneratedRouteMcpEntryOptions` and
`GeneratedEventRuntimeBinding`, and the lineage fallback is the one host a
single-projection root serves, or none for a composite root; `entries.ts`
requires the selection instead of defaulting it from the composite name.

`AB4105` never fired for a skill: normalization gives every skill every
selected target and per-host frontmatter extensions collide as `AB4103`
instead, so the dead skill branch leaves `compose.ts` and the diagnostics,
reference, and framework-mode prose say so. `AGENT_BUNDLE_HOOK_HOST` leaves
the runtime-environment reference (en/zh) and the test env fixtures; `api.ts`
reuses `isBuiltInHost`; `compose.ts` reuses `sortedProjections`; stale
`<target>/…` doc comments in `test/packed.ts` and `routes/graph.ts` name the
root layout.

Ports from #569: install refuses `--from` naming a directory above the plugin
root for all three hosts (`AB7001`, no host CLI runs), doctor lists Claude
plugins from the root `--from` names and never from a nested `claude/`, and
the Codex validator judges only `.codex-plugin/*` in a root shared with
Claude's `.mcp.json` and `hooks/hooks.json`. Lane C's docs parity pass:
tree drawings gain `commands/` and `rules/`, the folder-discovery shield
names all three guarded paths, over-wide code samples re-padded to 90
columns, `AB4808`/`AB4809` prose and the rsc-agent-runtime README describe
one composite root.

* test(prepack): #588's prebuilt payload lands in the composite root (host-packs/runtime/…), not under a target partition

* changeset: the event runtime endpoint is the artifact's alone (#592 boundary)

* review: lineage fallback is the one host whose MCP document lists the server; document AB7001's composite-root trigger

Self-review pass 1 read the fallback as inferring the root's cardinality
from `allowedTargets`. It is `server.targets ∩ selected` — the hosts whose
MCP documents list the server, so the hosts that can have spawned it — and a
Claude-only server in a Claude+Codex root is correctly assumed to be Claude's.
The comment and the two test names now say so. `docs/diagnostics.md` names
`AB7001` in the `AB700x` family row: the host manifest sits directly under
`--from`, never under `<from>/<host>`.

* build: host the composite root's event runtime per selected host's first generated server; judge built-in hosts by adapter identity for the install surface (#578 review)

- planMcpEntriesSurface no longer attaches every event route to the first
  generated-route server: eventRuntimeHosting hosts the runtime in the first
  generated server each selected host's MCP document lists (one process when
  they agree, one per host otherwise) and every hosting server accepts the
  same allowed set; a Claude-only server in a Claude+Codex root no longer
  refuses Codex wrappers while Codex's own server hosts nothing.
- The generated entry carries `hosts` (the selected hosts whose documents
  list the server) separately from `allowedTargets`; the lineage fallback
  reads `hosts`, so a Claude-only server hosting a two-host runtime still
  assumes Claude for an anonymous MCP client (#592).
- TargetRegistry.builtInHost()/builtInHosts() judge the four shipped adapters
  by identity; compose and artifact validation use it for INSTALL.md and
  install.mjs, so an advanced registry's adapter named `portable` earns no
  install surface it never asked for. The pack inventory (manifest names
  only) keeps the name-based requirement it had on main.

* test(browser): mount each MCP App as one host of the composite selection, never as the selection identity

The browser pool compiles every app once for the project's whole selection
(as the build stages it, #555) but the registry's `target` — the preview
profile and the binding's `target` the page sees — is the host the app
mounts as: the override, or the app's first declared target the project
selects, as before. `claude+codex+portable` had leaked into the binding
(#592) and failed examples/mcp-app's browser-app suite in CI.

* docs: fold #599's per-code diagnostics rows into the composite root — AB4100 lists the four hosts, AB6023/AB6024 and AB7001 describe the one root

* review: judge built-in hosts by adapter identity in AB4106 and --host-validation; the pack inventory expects exactly the manifested files

- NormalizationTargetRegistry.builtInHost? lets config/validate.ts refuse a
  custom adapter registered under a built-in host's name beside other targets
  (AB4106) the way compose and validate-artifact already judge it; registries
  that cannot tell still judge by name.
- validate --host-validation and build --host-validation select the shipped
  validators through registry.builtInHosts(), so a custom `claude` or
  `portable` adapter is held to no shipped host's contract.
- pack-inventory.ts no longer re-derives the install surface from manifest
  target names: every emitted file is manifested and AB6023/AB6024 already
  judged the surface by identity, so the pack expects what the manifest lists.
- Changeset names the browser pool's `target` option semantics.

* docs: AB4106 judges the shipped adapters by identity

* test: exercise host validation by adapter identity through validate --artifact and build --host-validation

The identity test validated the project root, which never enters host
validation; validate the built custom root as an artifact instead, and
build a custom adapter named claude with a runner spy that must not be
spawned. AB6024's row names the shipped cursor/portable adapters by
identity, as the validator judges.

* test: validate --artifact takes the project root too

* test: a custom adapter under the portable name owes no install surface (AB6023/AB6024 by identity)
ScriptedAlchemy added a commit that referenced this pull request Sep 5, 2026
…rs read the composite root through the manifest (#592 step 3, #555 W2/S3) (#604)

* feat(targets): remove the plugin target from the public target model (#555 S1)

- config/normalize.ts no longer synthesizes a plugin lowering target or a
  shared plugin skill document; loweringHosts is a pure isSkillHost filter.
- adapters/registry.ts registers portable, codex, claude, cursor only;
  adapters/plugin.ts is deleted together with the composite-only capability
  helpers (intersect/union/mergeCapabilityEvidence, capabilityBooleanView).
- targets: ['plugin'] and --target plugin fail with the existing AB4100.
- plugin branches removed from api.ts, pack-inventory, package-build,
  entry-shell, events/projection, lifecycle replay, install/surface,
  install/install, create-agent-bundle options/scaffold, capability JSON,
  and the CLI help text.
- Tests: plugin-bundle.test.ts deleted; plugin rows dropped from the
  adapter, install-surface, normalization and lifecycle suites; acceptance 3
  covered by normalization.test.ts and cli.test.ts.

* feat(build): plan the selected projections into one composite root (#555 S2)

build/build.ts stages every selected host projection into one root at
artifactRoot; build/compose.ts merges byte-identical entries once, fails
same-path collisions with AB4103 in host-name/path order, and refuses
host-scoped components another selected host would discover conventionally
with AB4105. Codex and Cursor hook/MCP documents move beside their manifests;
shared hooks compile to hooks/<name>.<host>.mjs; compiled surfaces are
attributed to the sorted composite identity; the artifact manifest records
only the selected projections. Install, doctor, dev services, eval harnesses,
Workbench, examples, and tests follow the composite root.

* feat(build): AB4106 for mixed advanced-registry selections; validate/inspect judge the composite root; port #569 consumer and docs fixes

- config/validate.ts: AB4106 when an adapter registered on an advanced
  TargetRegistry is selected beside any other target; the built-in host list
  moves to adapters/composite-layout.ts and install/surface.ts imports it.
- build/compose.ts: planComposite returns the plan beside its diagnostics;
  dev/project-service.ts prepare uses it so validate, inspect, dev report
  AB4103/AB4105 exactly where build refuses (caught by the composite-rules
  parity test ported from #569).
- Ported from #569 (superseded): tests/support/mcp-conformance.ts pluginRoot,
  tests/skill-document-service.test.ts decoy, examples/audiobook-curator and
  examples/hooks-and-scripts READMEs, scripts/measure-hook-cold-start.mjs.
- tests/composite-rules.test.ts: layout-independent rules from #569 (four-host
  root, commands/ dialect collision, INSTALL.md sections, validate parity).
- Stale per-host wording: Cursor capability evidence, doctor/types/cursor
  comments, create-agent-bundle README, docs/diagnostics.md, canvas sample.

* test(packed): read the composite root's bin/ and single mcp/ entry in the packed pool

* fix(inspect): describe the same event allowed-targets set the build bakes into a generated-route server; AB4105 trigger names skills

* build: carry #585's MCP App compile diagnostics through the composite root; mcp-apps-compile test selects the portable projection

* test(cli): MCP App compile report attributes the view to the composite selection identity (codex+portable), matching planCompiledMcpApps after the #585 merge

* build: identify the event runtime by the artifact alone; port #569's nested-root and shared-root tests (#592 boundary)

The generated MCP entry and every hook wrapper derived the event runtime's
endpoint id from `<epoch>:<selection identity>:<root>`, so the composite
selection (`claude+codex`) had become runtime identity, and the runtime
took `events.target` as the tool-call lineage host fallback, reading the
selection as a host. Both are projection selection leaking into runtime
identity (#592 §2). The endpoint is now `<epoch>:<root>` on both sides
(entry-shell, hook-contract, `agent-bundle/test` installed harness), the
`target` field leaves `GeneratedRouteMcpEntryOptions` and
`GeneratedEventRuntimeBinding`, and the lineage fallback is the one host a
single-projection root serves, or none for a composite root; `entries.ts`
requires the selection instead of defaulting it from the composite name.

`AB4105` never fired for a skill: normalization gives every skill every
selected target and per-host frontmatter extensions collide as `AB4103`
instead, so the dead skill branch leaves `compose.ts` and the diagnostics,
reference, and framework-mode prose say so. `AGENT_BUNDLE_HOOK_HOST` leaves
the runtime-environment reference (en/zh) and the test env fixtures; `api.ts`
reuses `isBuiltInHost`; `compose.ts` reuses `sortedProjections`; stale
`<target>/…` doc comments in `test/packed.ts` and `routes/graph.ts` name the
root layout.

Ports from #569: install refuses `--from` naming a directory above the plugin
root for all three hosts (`AB7001`, no host CLI runs), doctor lists Claude
plugins from the root `--from` names and never from a nested `claude/`, and
the Codex validator judges only `.codex-plugin/*` in a root shared with
Claude's `.mcp.json` and `hooks/hooks.json`. Lane C's docs parity pass:
tree drawings gain `commands/` and `rules/`, the folder-discovery shield
names all three guarded paths, over-wide code samples re-padded to 90
columns, `AB4808`/`AB4809` prose and the rsc-agent-runtime README describe
one composite root.

* test(prepack): #588's prebuilt payload lands in the composite root (host-packs/runtime/…), not under a target partition

* changeset: the event runtime endpoint is the artifact's alone (#592 boundary)

* review: lineage fallback is the one host whose MCP document lists the server; document AB7001's composite-root trigger

Self-review pass 1 read the fallback as inferring the root's cardinality
from `allowedTargets`. It is `server.targets ∩ selected` — the hosts whose
MCP documents list the server, so the hosts that can have spawned it — and a
Claude-only server in a Claude+Codex root is correctly assumed to be Claude's.
The comment and the two test names now say so. `docs/diagnostics.md` names
`AB7001` in the `AB700x` family row: the host manifest sits directly under
`--from`, never under `<from>/<host>`.

* build: host the composite root's event runtime per selected host's first generated server; judge built-in hosts by adapter identity for the install surface (#578 review)

- planMcpEntriesSurface no longer attaches every event route to the first
  generated-route server: eventRuntimeHosting hosts the runtime in the first
  generated server each selected host's MCP document lists (one process when
  they agree, one per host otherwise) and every hosting server accepts the
  same allowed set; a Claude-only server in a Claude+Codex root no longer
  refuses Codex wrappers while Codex's own server hosts nothing.
- The generated entry carries `hosts` (the selected hosts whose documents
  list the server) separately from `allowedTargets`; the lineage fallback
  reads `hosts`, so a Claude-only server hosting a two-host runtime still
  assumes Claude for an anonymous MCP client (#592).
- TargetRegistry.builtInHost()/builtInHosts() judge the four shipped adapters
  by identity; compose and artifact validation use it for INSTALL.md and
  install.mjs, so an advanced registry's adapter named `portable` earns no
  install surface it never asked for. The pack inventory (manifest names
  only) keeps the name-based requirement it had on main.

* wip(manifest): artifact manifest v2 — projections, routes, executables, distribution; hook index folded into executables.hooks (#592 step 3)

* test(browser): mount each MCP App as one host of the composite selection, never as the selection identity

The browser pool compiles every app once for the project's whole selection
(as the build stages it, #555) but the registry's `target` — the preview
profile and the binding's `target` the page sees — is the host the app
mounts as: the override, or the app's first declared target the project
selects, as before. `claude+codex+portable` had leaked into the binding
(#592) and failed examples/mcp-app's browser-app suite in CI.

* test(manifest): retarget hook and MCP suites at the v2 artifact manifest

The hook-index sidecar is gone; these tests now assert executables.hooks
on agent-bundle.manifest.json and pass the required route graph into build().

* test: migrate manifest coverage to v2

* refactor: read install identity from artifact manifest

* feat: inspect authoritative artifact manifest

* docs: fold #599's per-code diagnostics rows into the composite root — AB4100 lists the four hosts, AB6023/AB6024 and AB7001 describe the one root

* feat(cli): resolve MCP --target from the artifact manifest

serve-app and mcp list|invoke|run default to the only projection that
runs the named server, and inspect reports a built-manifest summary
when one exists at the project's artifact output.

* manifest: routes.cli.routes may hold projected MCP tool routes (routes.mcpCommands)

* test(build-compose): TargetRegistry is constructed, keep the value import

* review: judge built-in hosts by adapter identity in AB4106 and --host-validation; the pack inventory expects exactly the manifested files

- NormalizationTargetRegistry.builtInHost? lets config/validate.ts refuse a
  custom adapter registered under a built-in host's name beside other targets
  (AB4106) the way compose and validate-artifact already judge it; registries
  that cannot tell still judge by name.
- validate --host-validation and build --host-validation select the shipped
  validators through registry.builtInHosts(), so a custom `claude` or
  `portable` adapter is held to no shipped host's contract.
- pack-inventory.ts no longer re-derives the install surface from manifest
  target names: every emitted file is manifested and AB6023/AB6024 already
  judged the surface by identity, so the pack expects what the manifest lists.
- Changeset names the browser pool's `target` option semantics.

* docs: AB4106 judges the shipped adapters by identity

* manifest: hooks[].routeId for event-route wrappers; cross-check CLI command route ids; inspect reads the CLI build root; drop unused imports

* workbench: hook playground rows are manifest hook rows (host, kind, routeId)

* test(workbench): hook client fixture carries manifest hook rows

* docs(manifest): agent-bundle.manifest.json v2 reference; consumers read the composite root through the manifest (#592 step 3, #555 W2/S3)

- New reference page website/docs/{en,zh}/reference/artifact-manifest.mdx
  (+ _meta.json): every section and field of the v2 manifest, who writes it,
  who reads it, the shipped JSON Schema and public reader exports, reserved
  keys not yet emitted, the versioning rule, one abbreviated example.
- install / uninstall / doctor: --from is the composite root; identity and the
  host plugin document come from application + projections[host]; AB7001
  reworded (cli.mdx, installation.mdx, package README).
- serve-app / mcp: --target optional, ambiguity names the choices; inspect
  --json gains output.manifest (cli.mdx, mcp.mdx).
- docs/diagnostics.md: AB7001 and AB60xx family rows; new "Artifact manifest
  coherence (AB6039–AB6040)" section; AB6018 reads executables.hooks[].
- Stale statements: agent-bundle.hooks.json sidecar removed from artifact
  trees and prose (index, project-structure, targets-artifacts, hooks,
  entry-conventions); targets rows -> projection rows.
- Changeset .changeset/592-authoritative-manifest.md (agent-bundle minor).

* feat(validate): AB6039/AB6040 manifest coherence lane over the v2 artifact manifest (#592 step 3)

New `src/build/validate-artifact-manifest.ts`, wired into `validateArtifact`
beside the hook and MCP coherence validators and gated on a parsed manifest
whose file table verified (no AB6004), so neither code fires on top of
AB6000/AB6001/AB6004 noise.

AB6039 (error) — manifest section coherence, what the parser cannot know
without the adapter registry or the tree:
- executables.bins[].path|worker, scripts[].path|worker,
  mcpServers[].entry.path|worker, mcpServers[].apps[].path must be direct
  files of the row's host layout (cliBin, scripts, mcpEntries, mcpApps);
  hooks[] stays with AB6018, which already holds it to hookWrappers.
- a route-generated server (routes.servers[] mode 'generated' with routes)
  whose executables.mcpServers[] row is not 'compiled' or carries another name.
- projections[host].documents.mcp / .hooks must name the document the host's
  runtime / hook contract reads; a row listing a host without an MCP runtime
  or without an MCP document.
- the host MCP document and the rows listing that host declare the same
  server names, each with the transport its row records.

AB6040 (error) — host document disagrees with the manifest identity:
- documents.plugin name/version vs application.name/version.
- documents.marketplace name vs projections[host].marketplace.name; a
  marketplace document the projection does not record (the parser already
  refuses the reverse).
- a host document that is not a strict JSON object cannot be proven.

Both are error severity because a consumer acting on the manifest would
install or launch something the tree does not contain.

Tests: tests/artifact-manifest-coherence.test.ts builds one composite root
(claude, codex, cursor, portable; compiled + command + remote MCP servers;
three marketplaces) and forges one disagreement per case with the manifest
re-serialized and the file table re-hashed, asserting exactly one code.

* fix: resolve MCP documents from artifact manifest

* docs(manifest): hooks[].routeId and projected CLI tool routes (en, zh)

* test: exercise host validation by adapter identity through validate --artifact and build --host-validation

The identity test validated the project root, which never enters host
validation; validate the built custom root as an artifact instead, and
build a custom adapter named claude with a runner spy that must not be
spawned. AB6024's row names the shipped cursor/portable adapters by
identity, as the validator judges.

* Ship a JSON Schema for agent-bundle.manifest.json (#592 step 3)

Add schemas/agent-bundle.manifest.schema.json (draft 2020-12, closed keys
at every level, $defs per manifest shape, "present exactly when" rules as
if/then/else) and publish it through package.json `files` and the
`./schemas/agent-bundle.manifest.schema.json` export.

src/build/manifest-schema.ts imports the JSON, deep-freezes it as
`artifactManifestSchema`, and compiles it once with Ajv 2020 strict mode
(strictRequired relaxed for the conditionals) behind
`validateArtifactManifestSchema(value): readonly string[]`; both are
exported from src/api.ts and src/index.ts. The Rslib bundle inlines the
JSON, so dist needs nothing from the packed schemas directory.

src/schemas/ajv-issues.ts now installs ajv-formats, accepts Ajv options,
and owns the shared `compareSchemaIssues` comparator (lifted from
agent-skills/contract.ts) plus `formatSchemaIssue`.

scripts/dist-freshness.mjs counts the schemas directory as an agent-bundle
build input, since the JSON is compiled into dist.

tests/manifest-schema.test.ts checks a populated and a minimal hand-built
manifest against both validators, sweeps every object for delete /
unknown-key / retype mutations asserting parser-schema agreement with a
two-entry documented allowlist, pins the parser-only rules (sorted arrays,
cross-references, digests, runtime floor, npm name and semver validity)
as accepted-by-schema, pins the schema-encoded rules as rejected by both,
and asserts $id, $schema, manifestVersion const, freezing, and the
package.json wiring.

* test: validate --artifact takes the project root too

* validate: manifest coherence judges MCP rows against the MCP lane's single document read; fixtures follow host layouts

* schema: hooks[].routeId, projected CLI tool routes; writer output validates against the shipped schema; changeset names #604

* doctor: AB7306 recovery names the composite root and manifest projection

* review fixes: reciprocal contract binding, ENOENT-only missing manifest, marketplace pointer existence, projection document pointers judged by the host contract, doctor proof over the built root, stale AB6018/AB7001 prose

* validator: judge document→row MCP coherence by built-in host identity; integration expectations for AB6039/AB6040 and the mcp run refusal

* manifest: projections[].builtInHost records the shipped adapter identity; install/doctor and the installed harness key on it, the validator cross-checks it (AB6039), inspect reports it

* test: mcp-probe fixture records the claude adapter identity

* fixtures record built-in identity; advanced-adapter MCP documents own servers the manifest never rowed; inspection projections in the workbench proof

* validator: the coherence lane reuses the contracts the target-contract lane fetched; no registry re-entry after evidence snapshots

* docs: architecture page describes manifest v2 (#597 follow-up); export ArtifactManifestRouteContract types; cli test imports at top

* feat(workbench): project manifest application explorer

* Prove artifact-manifest paths stay relocatable when the composite root moves.

The writer already emitted root-relative POSIX paths; the parser now also
rejects a Windows drive-letter prefix, and a real build proves the bytes
never encode the machine and every reader still works after rename.

* paths: one relocatable-path rule (core/paths isRelocatablePosixPath) shared by the manifest parser and the writer

* refactor(build): serialize manifest from compiler plans

* docs: describe authoritative manifest generation

* manifest: split operational compiler facts into compiler.recordVersion 1

Keep timings, cache keys, adapter revisions, source-input hashes, and other
run metadata out of the public artifact contract so a compiler refactor never
forces a manifestVersion bump.

* readers: consume manifest.compiler for operational facts

Move validator, pack inventory, eval, inspection, and the installed harness
onto compiler.project / provenance / adapters / agentSkills so consumer
surfaces keep reading only the public contract.

* docs: document the public contract vs compiler record

Restructure the artifact-manifest pages and the targets outline around
manifestVersion and compiler.recordVersion, and mention routes.contracts[]
in the authoritative-manifest changeset.

* test: read provenance and compiler.project after the manifest split

Leftover fixture helpers still typed adapter facts onto projections and
asserted files[].sourceInputs / manifest.project on the public contract.

* docs(architecture): no separate coherence pass; AB6010 carries adapter identity

* test: explorer fixture follows the compiler record split

* rstest: drop the deleted coherence test from the integration list

* docs(architecture): public contract vs compiler record; explorer projection

* Add inspect --artifact so a copied composite root is read through the manifest alone.

* fix: trust manifest inventory for installs

* docs: describe authoritative install inventory

* manifest: MCP/hooks pointers come from the adapter runtime and hook contracts; explorer tolerates route-less servers; tests follow the compiler split

* deslop: reuse errorMessage from core/errors, drop dead projectionFor export, fix orphaned doc comment

- manifest-file.ts: replace private describe helper with the existing
  errorMessage from core/errors.ts (helper-before-writing rule)
- manifest.ts: delete projectionFor, exported with no importer anywhere
  (the coherence test defines its own throwing variant)
- manifest-routes.ts: move the artifactRoutesFor doc comment off
  artifactRouteContractFor, where it was stranded as a second docblock

Co-authored-by: Zack Jackson <ScriptedAlchemy@users.noreply.github.com>

* fix: reindex derived artifact variants

* docs: explain variant manifest reindexing

* deslop: drop type-bypassing casts, restating comments, and repetitive changeset prose

* manifest: carry the web section through v2 — schema, referenced-path rule, doctor reads it from the parsed manifest

* tests: preflight artifact graph reads hook rows from the manifest, not the removed sidecar

* tests: compiler-evidence fixture supplies the route graph the manifest writer requires

* review: doctor --from keeps AB7001 from the identity reader; mcp run launches the host document's own line, cross-checked against the manifest entry; relocatable proof covers web.apps[].entry

* tests(packed): provenance lives on compiler.provenance; the packed MCP fixture declares its portable projection identity and server row

* Address second self-review: keep AB7001 in doctor, launch host MCP lines verbatim, reject drive paths in schema, cover web entries in relocation proof

* feat(manifest): serialize definePrebuilt payloads as distribution.payloads[] (#630 absorbed)

* chore: deslop merge delta (gpt-5.6-sol-medium, 1 edit)

* Address merge-delta review: payload host-filtering proof, transport on inspection MCP rows, index-based payload location, diagnostics prose

* web-host: read declared projections from manifest v2 projections[].host (dev /web route 404 after #628 merge)

* feat(manifest): record event route execution

* Pin a strict closed-key inventory for manifest v2 and surface payload runtimeDependencies on inspect.

An old closed reader rejects any unknown key, so adding an optional public field is not compatible — the inventory fixture fails until the version bumps, and artifact-only consumers read packages from distribution.payloads[].runtimeDependencies.

* test: add combined manifest proof

* manifest-key-inventory: merge narrowed row properties over the shared $def; regenerate v2 inventory with routes.events[].execution

* combined proof: assert routes.events[].execution from the built manifest

* docs: readers refuse any other manifestVersion in either direction

* manifest: one launch record for compiled MCP servers

`executables.mcpServers[]` rows of kind `compiled` carry `launch`
({ args, entry, env, worker? }) in place of `entry`; `args[]` records the
author's declaration as `artifact` root-relative paths (plugin-root-anchored)
or `literal` values whose tokens the launcher expands. `web.apps[]` drops its
copied `entry`/`args`/`env` and names the compiled server instead; the parser
cross-checks the reference, the files[] rows, and artifact arguments.

The launch types live in `web-host/manifest.ts` (bundled into every plugin
bin) and `build/manifest.ts` imports them; `readWebManifestDocument` returns
`{ hosts, launches, web? }` so `<plugin> web` resolves the App's launch
through the record. Schema, docs (en+zh), changeset clause, and the packed,
relocatable, and unit proofs follow.

* combined proof: assert executables.mcpServers[].launch; bare plugin-data token

* manifest: prebuilt MCP servers carry the same launch record (kind 'prebuilt'), so web Apps on definePrebuilt servers keep working through one record

* deslop: 5 edits

* Honor manifest launch records in mcp run fallback; web reader requires manifestVersion 2; pin proof wording; prebuilt args/env launch coverage

* Rewire the read-only state-root proof to the manifest launch record

* Remove the prebuilt launch test's temporary home

* Anchor the manifest-only mcp run fallback on the durable plugin root; document the lean web reader and optional web key

* Document the lean web reader as the one non-parser consumer

* Locate the declared state root through the installed manifest's MCP pointer; admit the compile evidence record

* Deslop the state-root rewire; state the inherited state-root fallback

* fix(manifest): lean web reader rejects unsupported versions, duplicate server identities, malformed launch and projection rows; reindex refuses compiled files and compile evidence

* test(workbench): avoid terminal close locator race

* test(manifest): combined proof checks compile evidence survives reindex and install

* chore: deslop pass over the reader/reindex delta

* fix(manifest): launch records name indexed bytes only in both readers; copies re-measure to the verified inventory

* test(manifest): match server-name diagnostics

* fix(manifest): the runtime-owned state root is reserved everywhere — AB4741 for payloads, files[] parser and schema, installers

* fix(manifest): one files[] path rule for both readers — never the manifest, the runtime state root, or the install receipt entry

* chore: deslop the post-review reader/evidence delta

---------

Co-authored-by: Ubuntu <zack@ubuntu-main.local>
Co-authored-by: Zack Jackson <ScriptedAlchemy@users.noreply.github.com>
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.

1 participant