From 24cc125f4cc67c5cd4bed113965fedae9440b89f Mon Sep 17 00:00:00 2001 From: Zack Jackson <25274700+ScriptedAlchemy@users.noreply.github.com> Date: Sat, 5 Sep 2026 09:59:15 +0000 Subject: [PATCH 1/3] fix(diagnostics): reallocate event preflight codes to AB4850/AB4851 AB4840-AB4842 are owned by the CLI surface projections of #596 (PR #616); this branch's event-preflight export diagnostic moves to AB4850 and the required-provider declaration diagnostic to AB4851 so the ranges stay disjoint. Both codes are unused on main and on the #616 branch. Co-authored-by: Zack Jackson --- .changeset/595-event-preflight-gates.md | 2 +- docs/diagnostics.md | 10 +++++----- packages/agent-bundle/src/routes/contract.ts | 12 ++++++------ packages/agent-bundle/src/routes/graph.ts | 6 +++--- packages/agent-bundle/tests/route-graph.test.ts | 14 +++++++------- website/docs/en/guide/authoring/hooks.mdx | 4 ++-- website/docs/zh/guide/authoring/hooks.mdx | 4 ++-- 7 files changed, 26 insertions(+), 26 deletions(-) diff --git a/.changeset/595-event-preflight-gates.md b/.changeset/595-event-preflight-gates.md index 35cf8e776..608085317 100644 --- a/.changeset/595-event-preflight-gates.md +++ b/.changeset/595-event-preflight-gates.md @@ -2,4 +2,4 @@ "agent-bundle": patch --- -Allow an event route under `src/events/**` to declare a `preflight` gate — `export { default as preflight } from './.js'`, a sync or async function that receives the frozen `{ canonical, host, signal, terminal }` context and returns `'execute'`, `{ outcome: 'continue' }`, or `{ outcome: 'deny', reason }` — which the generated hook entry runs on the canonical event before the rendered route runtime, React, or any application provider loads. `inspect`, `validate`, `build`, and `dev` report `AB4840` when `preflight` is declared inline, exported more than once, re-exported from a bare package or under a binding other than `default`, unresolvable, cyclic, or not a function, naming the route module and, once a re-export was found, its specifier. Allow an executed event route to declare the provider keys it requires (`config.providers: ['', …]`) so only that subset resolves, in the existing deterministic key/source order with `processLifetime` seeded first; a route without a declaration still resolves every conventional provider, `[]` mounts `processLifetime` alone, and `AB4841` reports a malformed declaration, a duplicate key, the reserved `processLifetime`, or a key that matches no discovered `src/providers/*` module — unknown keys list the project's provider keys. Export `EventPreflight`, `EventPreflightContext`, `EventPreflightResult`, `validateEventPreflightResult`, and `eventFamilyAllowsPreflightDeny` from `agent-bundle`, `agent-bundle/api`, and `agent-bundle/routes`. Export the payload-free `EventTraceEvent` union, `createEventTracer`, and `installEventTraceObserver` for developer tooling. (#618) +Allow an event route under `src/events/**` to declare a `preflight` gate — `export { default as preflight } from './.js'`, a sync or async function that receives the frozen `{ canonical, host, signal, terminal }` context and returns `'execute'`, `{ outcome: 'continue' }`, or `{ outcome: 'deny', reason }` — which the generated hook entry runs on the canonical event before the rendered route runtime, React, or any application provider loads. `inspect`, `validate`, `build`, and `dev` report `AB4850` when `preflight` is declared inline, exported more than once, re-exported from a bare package or under a binding other than `default`, unresolvable, cyclic, or not a function, naming the route module and, once a re-export was found, its specifier. Allow an executed event route to declare the provider keys it requires (`config.providers: ['', …]`) so only that subset resolves, in the existing deterministic key/source order with `processLifetime` seeded first; a route without a declaration still resolves every conventional provider, `[]` mounts `processLifetime` alone, and `AB4851` reports a malformed declaration, a duplicate key, the reserved `processLifetime`, or a key that matches no discovered `src/providers/*` module — unknown keys list the project's provider keys. Export `EventPreflight`, `EventPreflightContext`, `EventPreflightResult`, `validateEventPreflightResult`, and `eventFamilyAllowsPreflightDeny` from `agent-bundle`, `agent-bundle/api`, and `agent-bundle/routes`. Export the payload-free `EventTraceEvent` union, `createEventTracer`, and `installEventTraceObserver` for developer tooling. (#618) diff --git a/docs/diagnostics.md b/docs/diagnostics.md index efb5d5883..749fcfdd9 100644 --- a/docs/diagnostics.md +++ b/docs/diagnostics.md @@ -30,7 +30,7 @@ even when no error diagnostic was reported. | `AB4765`–`AB4766` | Artifact-hosted routed CLI: a target without the `cli` capability omits `bin/.mjs`; a host-emitted file collides with it (see below). | | `AB477x` | MCP App view compilation (`AB4770`: compile error with file, line, column and the bundler message; `AB4771`: compile warning; `AB4772`: emitted-size advisory; see below). | | `AB490x`/`AB492x` | Conventional host components (#100 stage 2): rules `src/rules/*.mdc` (`AB4900`–`AB4908`) and commands `src/commands/*.md` (`AB4920`–`AB4928`), including per-host feature-set enforcement (`AB4907`/`AB4908`, `AB4927`/`AB4928`); see below. | -| `AB48xx`/`AB494x` | Route graph, state, layout (`AB4830`–`AB4832`), generated route declarations outside the TypeScript program (`AB4834`), route render budgets (`AB4835`), tool task support (`AB4836`), a route module that value-imports a compiler-carrying framework entry (`AB4837`), a CLI route `inputSchema` reference the static resolver cannot follow (`AB4838`) or that cycles (`AB4839`), an event route's `preflight` gate export (`AB4840`), an event route's declared provider keys (`AB4841`), and provider conventions (see below). | +| `AB48xx`/`AB494x` | Route graph, state, layout (`AB4830`–`AB4832`), generated route declarations outside the TypeScript program (`AB4834`), route render budgets (`AB4835`), tool task support (`AB4836`), a route module that value-imports a compiler-carrying framework entry (`AB4837`), a CLI route `inputSchema` reference the static resolver cannot follow (`AB4838`) or that cycles (`AB4839`), an event route's `preflight` gate export (`AB4850`), an event route's declared provider keys (`AB4851`), and provider conventions (see below). | | `AB5000` | General CLI and adapter failures (see below). | | `AB60xx` | Built-artifact validation, including schema documents and referenced files (`AB6005`: an emitted JavaScript module — a host-pack module or a package build `dist` bundle (`dist/bin/*.js`, the Flight workers, the `lib` entry), prebuilt payloads excepted — has an import that is neither a Node built-in nor a relative or `file:` specifier resolving to a listed regular file inside its tree, or a non-literal dynamic import; a `dist` finding names `dist/`; `AB6011`/`AB6012`: a target's required pinned-schema document is missing or invalid; `AB6025`: a manifest-declared `logo` path is missing from the artifact or escapes the deploy tree; `AB6034`: emitted Skill Markdown has no instruction body; `AB6035`–`AB6038`: Agent Plugins portable validation, see below). | | `AB6200`–`AB6202` | Workbench artifact inspection over published epochs: `AB6200` the epoch does not validate or its provenance is inconsistent, `AB6201` an epoch reference could not be released, `AB6202` unsafe runtime metadata (see below). | @@ -1221,7 +1221,7 @@ the route names, never a second event route. A `preflight` declared inline in the route module (`export const preflight = …`, `export function preflight`) is rejected too: evaluating the route module evaluates its rendering and provider imports, the very cost the gate exists to avoid. Every rejected form -is `AB4840`, once per route on the route module; the route compiles without a +is `AB4850`, once per route on the route module; the route compiles without a gate beside the error, and because the diagnostic is an error the build fails instead of silently taking the expensive path. @@ -1239,7 +1239,7 @@ the generated `AgentBundleProviders` declares; `processLifetime` is not one of them and must not be declared. The declaration is judged when the route graph compiles: a declaration that is not an array of string literals, a key listed twice, the reserved `processLifetime`, or a key naming no discovered provider -module is `AB4841`, once per route with every defect in one message; a +module is `AB4851`, once per route with every defect in one message; a declaration with any defect selects nothing, so the build fails rather than resolving a provider set the author did not write. @@ -1285,8 +1285,8 @@ resolving a provider set the author did not write. | `AB4837` | error | A route module of any kind except an App — a `src/cli/**` command, a `src/scripts/**` script, a tool, resource, or prompt route of a generated server, an event route — a layout, or a provider, or a module one of them reaches through relative value imports, imports `agent-bundle`, `agent-bundle/api`, `agent-bundle/config`, `agent-bundle/eval`, `agent-bundle/rstest`, `agent-bundle/test`, or `agent-bundle/test/browser` as a value (a static import whose binding is read at run time, `import 'agent-bundle/api'`, `import('agent-bundle/api')` with a literal specifier, or a non-type re-export). Those entries carry the compiler, and the generated executable is self-contained (#387): the bundler would inline the compiler and fail on the framework's runtime-relative module references (`Module not found: Can't resolve '../events'`), or the artifact validator would reject the inlined compiler's non-literal dynamic imports with `AB6005` — either way naming a generated file instead of the route (#558). Judged statically when the route graph compiles, so `inspect`, `validate`, `build`, and `dev` all report it, once per module, naming the route and the helper the import lives in. `import type`, `type`-qualified specifiers, and imports used only in type positions are elided by the bundler and never reported; routes of a server that is not generated (`custom`/`command`/`remote`, or an `AB4800` conflict) or of a CLI that is not generated (`conventional`, or an `AB4801` conflict) are never bundled, so they are not judged; likewise a layout that no bundled rendered route composes through (a worker imports only the layouts its routes reach: the tool, resource, and prompt routes of a generated server, the rendered `.tsx` commands of a generated CLI, and rendered `.tsx` scripts), and a provider in a project whose only executables are plain `.ts` scripts, which are bundled from their own source and mount none. Spawn the framework instead of importing it: serve an MCP App from a routed command with `spawnServeApp` from `agent-bundle/serve-app-command`, which runs `agent-bundle serve-app` as a child process; keep other framework calls in host processes (`package.json` scripts, a hand-written `.mjs` run from the checkout). The bundle-safe entries stay allowed: `agent-bundle/app` (the browser MCP App client, a leaf with no Zod, Node, or compiler import), `agent-bundle/routes`, `agent-bundle/launch-env`, `agent-bundle/meta`, `agent-bundle/mcp-apps`, `agent-bundle/mcp-entry`, `agent-bundle/cli-entry`, `agent-bundle/terminal-capability`, and `agent-bundle/serve-app-command`. | | `AB4838` | error | A CLI route's `inputSchema` references a binding the static resolver cannot follow. The message is `CLI route inputSchema: .` — the chain is the reference path from `inputSchema`, each step ``, or ` ()` when it crosses into another module (`inputSchema -> statusInputSchema (src/lib/protocol-schemas.ts) -> requestStatusSchema -> requestStatuses`), and the reason names the boundary: a specifier that `is not a relative module path`, one that `resolves outside the project` or `does not resolve to a module inside the project` (missing or unreadable), a target module that does not declare a top-level `export const `, a binding that is not a top-level `const` (`let`/`var`, destructuring, a function, a class, a default or namespace import — the message says what it is), an identifier that `is neither a top-level const in this module nor a named import from a relative module`, or a dynamic initializer — one that is neither a method chain, an object or array literal, nor a static literal (`whose initializer is a call expression`, `a function expression`, `a template literal with substitutions`). Reported on the route module; the recovery names the supported forms — relative imports inside the project, `export const`, alias chains — then says to inspect again. Only CLI routes raise it, because only there the static contract is load-bearing: an MCP, script, or event route whose schema the resolver cannot follow compiles without a static contract, as an out-of-grammar inline schema does, and the runtime derives its MCP JSON Schema from the real zod object. A reference that resolves but whose schema leaves the grammar is `AB4814`. | | `AB4839` | error | A CLI route's `inputSchema` reference chain is cyclic — `a` → `b` → `a`, within one module or across several: every visited `#` is recorded and revisiting one stops the walk. The message is `CLI route inputSchema: is a reference cycle.` and prints the cycle; it is reported on the route module, with the same recovery and the same CLI-only rule as `AB4838`. | -| `AB4840` | error | An event route's `preflight` gate (#595) is not the one physically cheap form the compiler can bundle on its own. Rejected: `preflight` declared inline in the route module (`export const preflight = …`, `export function preflight`) or exported more than once; re-exported under a binding other than `default` (`export { gate as preflight } from './gate.js'`, `export { preflight } from './gate.js'`); re-exported from a non-relative specifier (a bare package such as `'@scope/gate'`); a relative target that is missing, unreadable, or part of a re-export cycle; a target default export that cannot be followed through an acyclic chain of relative default re-exports; or a target default export that is not a function the scan can see. The message names the route module and, once a re-export was found, its specifier. Judged statically when the route graph compiles, so `inspect`, `validate`, `build`, and `dev` all report it, once per route with the route module as `sourcePath`; the route compiles without a gate beside the error, and the build fails rather than silently taking the expensive rendered path. Write exactly `export { default as preflight } from './.js'` in the route module, and make that module default-export one sync or async function receiving `{ canonical, host, signal, terminal }` and returning `'execute'`, `{ outcome: 'continue' }`, or `{ outcome: 'deny', reason }`. | -| `AB4841` | error | An event route's static required-provider declaration (#595) does not select a known set of conventional providers: `config.providers` is not an array of provider-key strings; a key is declared more than once; a key is the reserved `processLifetime`; or a key matches no conventional provider the route graph discovered under `src/providers/`. The message names the route and every offending key. Declare each key exactly once, spelled as the camel-cased stem of its `src/providers/.*` module, drop `processLifetime`, declare `[]` to mount `processLifetime` alone, or omit `config.providers` to preserve the all-provider compatibility default. | +| `AB4850` | error | An event route's `preflight` gate (#595) is not the one physically cheap form the compiler can bundle on its own. Rejected: `preflight` declared inline in the route module (`export const preflight = …`, `export function preflight`) or exported more than once; re-exported under a binding other than `default` (`export { gate as preflight } from './gate.js'`, `export { preflight } from './gate.js'`); re-exported from a non-relative specifier (a bare package such as `'@scope/gate'`); a relative target that is missing, unreadable, or part of a re-export cycle; a target default export that cannot be followed through an acyclic chain of relative default re-exports; or a target default export that is not a function the scan can see. The message names the route module and, once a re-export was found, its specifier. Judged statically when the route graph compiles, so `inspect`, `validate`, `build`, and `dev` all report it, once per route with the route module as `sourcePath`; the route compiles without a gate beside the error, and the build fails rather than silently taking the expensive rendered path. Write exactly `export { default as preflight } from './.js'` in the route module, and make that module default-export one sync or async function receiving `{ canonical, host, signal, terminal }` and returning `'execute'`, `{ outcome: 'continue' }`, or `{ outcome: 'deny', reason }`. | +| `AB4851` | error | An event route's static required-provider declaration (#595) does not select a known set of conventional providers: `config.providers` is not an array of provider-key strings; a key is declared more than once; a key is the reserved `processLifetime`; or a key matches no conventional provider the route graph discovered under `src/providers/`. The message names the route and every offending key. Declare each key exactly once, spelled as the camel-cased stem of its `src/providers/.*` module, drop `processLifetime`, declare `[]` to mount `processLifetime` alone, or omit `config.providers` to preserve the all-provider compatibility default. | | `AB4940` | error | A conventional provider module has no default export or its default export is not a function. Default-export a factory receiving `{ invocation, plugin, signal }`. | | `AB4941` | error | Two provider filenames derive the same camel-cased provider key. Rename one file so every provider key is unique. | | `AB4942` | error | A provider filename derives the reserved `processLifetime` key. Rename the file so its camel-cased key does not collide with the framework-owned provider. | diff --git a/packages/agent-bundle/src/routes/contract.ts b/packages/agent-bundle/src/routes/contract.ts index e40ed90f9..9bfeb019e 100644 --- a/packages/agent-bundle/src/routes/contract.ts +++ b/packages/agent-bundle/src/routes/contract.ts @@ -25,7 +25,7 @@ const unwrappedExpression = (expression: ts.Expression): ts.Expression => { }; const diagnostic = ( - code: 'AB4810' | 'AB4811' | 'AB4830' | 'AB4840' | 'AB4940', + code: 'AB4810' | 'AB4811' | 'AB4830' | 'AB4850' | 'AB4940', message: string, sourcePath: string, recovery: string, @@ -360,7 +360,7 @@ export const discoverEventRoutePreflight = ( if (preflightExports !== 1 || declarations.length !== 1) { return Object.freeze({ diagnostics: Object.freeze([diagnostic( - 'AB4840', + 'AB4850', `Event route module ${relativePath} exports preflight, but it is not exactly one named default re-export from a separate module.`, sourcePath, eventPreflightRecovery, @@ -372,7 +372,7 @@ export const discoverEventRoutePreflight = ( if (!isRelativeSpecifier(declaration!.specifier)) { return Object.freeze({ diagnostics: Object.freeze([diagnostic( - 'AB4840', + 'AB4850', `Event route module ${relativePath} re-exports preflight from non-relative specifier ${JSON.stringify(declaration!.specifier)}.`, sourcePath, eventPreflightRecovery, @@ -384,7 +384,7 @@ export const discoverEventRoutePreflight = ( if (followed === undefined) { return Object.freeze({ diagnostics: Object.freeze([diagnostic( - 'AB4840', + 'AB4850', `Event route module ${relativePath} re-exports preflight from ${JSON.stringify(declaration!.specifier)}, but that target is missing, unreadable, or cyclic.`, sourcePath, eventPreflightRecovery, @@ -397,7 +397,7 @@ export const discoverEventRoutePreflight = ( return Object.freeze({ candidateSource: followed.source, diagnostics: Object.freeze([diagnostic( - 'AB4840', + 'AB4850', `Event route module ${relativePath} re-exports preflight from ${JSON.stringify(declaration!.specifier)}, but its default export cannot be followed through an acyclic relative module chain.`, sourcePath, eventPreflightRecovery, @@ -408,7 +408,7 @@ export const discoverEventRoutePreflight = ( return Object.freeze({ candidateSource: followed.source, diagnostics: Object.freeze([diagnostic( - 'AB4840', + 'AB4850', `Event route module ${relativePath} re-exports preflight from ${JSON.stringify(declaration!.specifier)}, whose default export is not a function.`, sourcePath, eventPreflightRecovery, diff --git a/packages/agent-bundle/src/routes/graph.ts b/packages/agent-bundle/src/routes/graph.ts index 202edffa4..9fa1f89a2 100644 --- a/packages/agent-bundle/src/routes/graph.ts +++ b/packages/agent-bundle/src/routes/graph.ts @@ -127,7 +127,7 @@ const eventProviderDeclarationDiagnostics = ( 'Declare config.providers as a distinct array of conventional provider keys, omit it to resolve every provider, or use [] to resolve none.'; if (!Array.isArray(declared) || declared.some((key) => typeof key !== 'string')) { return [routeError( - 'AB4841', + 'AB4851', `Event route ${route.provenance.relativePath} config.providers must be an array of provider-key strings.`, recovery, route.source, @@ -136,7 +136,7 @@ const eventProviderDeclarationDiagnostics = ( const problems = validateRequiredProviderKeys(declared, providerKeys); if (problems.length === 0) return []; return [routeError( - 'AB4841', + 'AB4851', `Event route ${route.provenance.relativePath} has invalid config.providers: ${problems .map(requiredProviderKeyProblemMessage) .join(' ')}`, @@ -755,7 +755,7 @@ export const compileRouteGraph = async ( diagnostics: Object.freeze([ ...discovery.diagnostics, routeError( - 'AB4840', + 'AB4850', `Event route ${toPosixPath(relative(projectRoot, source))} re-exports preflight from another conventional event route.`, 'Move preflight to a separate support module that is not itself an event route.', source, diff --git a/packages/agent-bundle/tests/route-graph.test.ts b/packages/agent-bundle/tests/route-graph.test.ts index 015277d80..087049aa9 100644 --- a/packages/agent-bundle/tests/route-graph.test.ts +++ b/packages/agent-bundle/tests/route-graph.test.ts @@ -2287,7 +2287,7 @@ it('rejects a conventional event route reused as another route preflight', async expect(graph.events.map((route) => route.id)).toEqual(['event:session/start', 'event:tool/before']); expect(graph.events.find((route) => route.id === 'event:tool/before')?.preflight).toBeUndefined(); expect(graph.diagnostics).toEqual(expect.arrayContaining([ - expect.objectContaining({ code: 'AB4840', sourcePath: join(root, 'src/events/tool/before.ts') }), + expect.objectContaining({ code: 'AB4850', sourcePath: join(root, 'src/events/tool/before.ts') }), ])); }); @@ -2316,11 +2316,11 @@ it('rejects event preflights that are inline, non-relative, unresolvable, cyclic code, source: sourcePath?.slice(root.length + 1).replaceAll('\\', '/'), }))).toEqual([ - { code: 'AB4840', source: 'src/events/agent/start.ts' }, - { code: 'AB4840', source: 'src/events/agent/stop.ts' }, - { code: 'AB4840', source: 'src/events/compact/after.ts' }, - { code: 'AB4840', source: 'src/events/compact/before.ts' }, - { code: 'AB4840', source: 'src/events/session/end.ts' }, + { code: 'AB4850', source: 'src/events/agent/start.ts' }, + { code: 'AB4850', source: 'src/events/agent/stop.ts' }, + { code: 'AB4850', source: 'src/events/compact/after.ts' }, + { code: 'AB4850', source: 'src/events/compact/before.ts' }, + { code: 'AB4850', source: 'src/events/session/end.ts' }, ]); }); @@ -2351,7 +2351,7 @@ it('validates event route provider declarations against conventional provider ke expect(graph.events.find((route) => route.id === 'event:tool/before')?.config).toMatchObject({ providers: ['projectAuth'], }); - expect(graph.diagnostics.filter(({ code }) => code === 'AB4841').map(({ sourcePath }) => + expect(graph.diagnostics.filter(({ code }) => code === 'AB4851').map(({ sourcePath }) => sourcePath?.slice(root.length + 1).replaceAll('\\', '/'))).toEqual([ 'src/events/session/start.ts', 'src/events/tool/after.ts', diff --git a/website/docs/en/guide/authoring/hooks.mdx b/website/docs/en/guide/authoring/hooks.mdx index a8db18058..5d2ec6f18 100644 --- a/website/docs/en/guide/authoring/hooks.mdx +++ b/website/docs/en/guide/authoring/hooks.mdx @@ -268,7 +268,7 @@ specifier resolves to the `.ts`/`.tsx` source, as route imports do. A gate decla route module (`export const preflight = …`, `export function preflight`) is rejected: evaluating that module evaluates its rendering and provider imports, the very cost the gate exists to avoid. -Every rejected form is `AB4840`, reported once per route on the route module by `inspect`, +Every rejected form is `AB4850`, reported once per route on the route module by `inspect`, `validate`, `build`, and `dev`: `preflight` declared inline or exported more than once; re-exported under a binding other than `default` (`export { gate as preflight } from './gate.js'`); re-exported from a bare package specifier; a relative target that is missing, unreadable, or part @@ -354,7 +354,7 @@ Keys are the camel-cased stems of the `src/providers/.*` modules the route (`retry-policy.ts` is `retryPolicy`), the same keys `AgentBundleProviders` in the generated `.agent-bundle/routes.d.ts` lists. `processLifetime` is not one of them — it is seeded whatever the route declares — and must not be declared. The declaration is judged when the route graph -compiles, so `inspect`, `validate`, `build`, and `dev` all report `AB4841`, once per route with +compiles, so `inspect`, `validate`, `build`, and `dev` all report `AB4851`, once per route with every defect in one message: a `providers` that is not an array of string literals, a key listed twice, the reserved `processLifetime`, or a key that matches no discovered provider — a misspelling, a stem camel-cased differently, or a module discovery skips (an `_`- or `.`-prefixed diff --git a/website/docs/zh/guide/authoring/hooks.mdx b/website/docs/zh/guide/authoring/hooks.mdx index c266525db..75975c854 100644 --- a/website/docs/zh/guide/authoring/hooks.mdx +++ b/website/docs/zh/guide/authoring/hooks.mdx @@ -239,7 +239,7 @@ provider。通过相对路径重新导出,编译器可以构建独立的 `pref 再加载。直接在渲染式路由模块中声明函数,只是逻辑上先执行;由于求值该模块也会求值其静态渲染代码并加载 provider,实际开销并不会降低。通过裸包名导入、形成循环、导出非函数,或采用其他无法静态追踪的方式提供 `preflight`,都会导致构建错误;编译器不会悄悄退回高开销的路由路径。 -这些无效形式由 `inspect`、`validate`、`build` 和 `dev` 以 `AB4840` 报告。 +这些无效形式由 `inspect`、`validate`、`build` 和 `dev` 以 `AB4850` 报告。 `preflight` 可以是同步或异步的,并且恰好只有三种结果: @@ -285,7 +285,7 @@ notices、lineage 存储、渲染式布局、应用 provider、React/RSC 辅助 provider 是否按需加载由声明决定。`preflight` 不会实例化任何应用 provider。事件路由执行后,运行时只会 加载并解析它静态声明的 provider 子集。选中的 provider 在每次请求中仍只实例化一次,并按确定的键名/来源 顺序依次执行;`processLifetime` 会先注入,任何失败都会终止该请求。键名重复或未知都会导致构建错误。 -无效的 `config.providers` 声明由 `AB4841` 报告。 +无效的 `config.providers` 声明由 `AB4851` 报告。 声明方式是 `export const config = { providers: ['projectPolicy'] }`。provider 键由 `src/providers/` 下的文件名转换为 camelCase;空数组表示不选择任何应用 provider。 From 545344253bcdc4fadcf2704a05fb47cd94a08307 Mon Sep 17 00:00:00 2001 From: Zack Jackson <25274700+ScriptedAlchemy@users.noreply.github.com> Date: Sat, 5 Sep 2026 09:59:21 +0000 Subject: [PATCH 2/3] test(events): assert wrapper envelope validation structurally The wrapper assertion pinned the exact generated line including the temporary variable holding the parsed stdin value; it now matches any identifier while still requiring validateNativeEventEnvelope to receive the baked { canonicalEvent, nativeEvent, target } constants. Co-authored-by: Zack Jackson --- packages/agent-bundle/tests/claude-hook-event-name.test.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/agent-bundle/tests/claude-hook-event-name.test.ts b/packages/agent-bundle/tests/claude-hook-event-name.test.ts index 31afed58d..f704f2594 100644 --- a/packages/agent-bundle/tests/claude-hook-event-name.test.ts +++ b/packages/agent-bundle/tests/claude-hook-event-name.test.ts @@ -174,7 +174,11 @@ it('bakes the pinned Claude hook_event_name into every Claude event-route wrappe expect(wrapper!.nativeEvent, entry.route).toBe(expectedNativeEvent); expect(wrapper!.virtualSource, entry.route).toContain(`const nativeEvent = ${JSON.stringify(expectedNativeEvent)};`); expect(wrapper!.virtualSource, entry.route).toContain('const target = "claude";'); - expect(wrapper!.virtualSource, entry.route).toContain('validateNativeEventEnvelope(nativeInput, { canonicalEvent, nativeEvent, target })'); + // The wrapper must validate the parsed stdin value through + // validateNativeEventEnvelope with the baked constants before any use; + // the local variable holding that value is an implementation detail. + expect(wrapper!.virtualSource, entry.route) + .toMatch(/validateNativeEventEnvelope\([\w$]+, \{ canonicalEvent, nativeEvent, target \}\)/u); const native = await nativeEnvelope(entry.native); expect(native.hook_event_name, entry.route).toBe(expectedNativeEvent); From d33985591e6a9981171ad0118e8e769fd8905021 Mon Sep 17 00:00:00 2001 From: Zack Jackson <25274700+ScriptedAlchemy@users.noreply.github.com> Date: Sat, 5 Sep 2026 10:03:01 +0000 Subject: [PATCH 3/3] docs(changeset): document the AB4840/AB4841 -> AB4850/AB4851 renumber Co-authored-by: Zack Jackson --- .changeset/625-event-preflight-code-realloc.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/625-event-preflight-code-realloc.md diff --git a/.changeset/625-event-preflight-code-realloc.md b/.changeset/625-event-preflight-code-realloc.md new file mode 100644 index 000000000..d82aec989 --- /dev/null +++ b/.changeset/625-event-preflight-code-realloc.md @@ -0,0 +1,5 @@ +--- +'agent-bundle': patch +--- + +Renumber the event-route diagnostics that landed with #618: the `preflight` gate export defect moves from `AB4840` to `AB4850`, and the `config.providers` required-provider declaration defect moves from `AB4841` to `AB4851`. `AB4840`–`AB4842` belong to the CLI surface projections of #596; suppressions or tooling that matched the old codes on an unreleased `main` must match `AB4850`/`AB4851` instead. (#625)