feat(routes): CLI surface projections of MCP tools — colocated <tool>.cli.ts, mapInput, AB4843–AB4845 (#596) - #616
Conversation
…840–AB4842 (#596 lane M6)
…rojections Add tool:harness/submit to the route-harness fixture with its CLI projection module (parked as _submit.cli.ts until the M1 compiler classifies projection modules; rename to submit.cli.ts at integration), the cli-dispatch-projection projection-pool suite, and a cli-routes-build describe that builds a temp project with src/mcp/demo/tools/submit.tsx + submit.cli.ts and runs the generated bin and inspect --routes. Existing pins updated for the new fixture tool: test-harness-manifest, contract-matrix-fixtures, mcp-in-memory, cli-dispatch, packed-stdio-projection.
Discover `src/mcp/<server>/tools/<tool>.cli.{ts,tsx}` as projection
modules paired with the sibling tool route (never a route of their own;
AB4840 for orphan, misplaced, or duplicate modules; skipped silently when
the server is not generated). `extractCliProjection` validates the closed
`CliProjectionConfig` key set through the unchanged route-config grammar
and the `mapInput` export (AB4841), and binds flags, positionals, and
command segments to the tool's contract (AB4842).
`compileProjectedCliCommands` compiles one command per projected tool
under the CLI-route argv policy: `cli-argv.ts` takes a per-key override
policy (`name`, `aliases`, `description`, `default`, `required: false`)
applied inside `cliOptionFor` so kebab-case, reserved-name (`yes` when
confirming), and collision rules judge the final spellings, and reports
the canonical-required keys it relaxed. Projected tools leave the bulk
`routes.mcpCommands` projection (AB4822 when an include pattern reaches
only them); the AB4813 collision pass covers projected commands with the
projection recovery wording. A tool without a static contract is parsed
again under the `Tool route <path> (CLI projection <module>)` label so
AB4814/AB4838/AB4839 name it.
The CLI surface exists whenever cli routes, the bulk projection, or
projections exist (AB4801 third arm); `projectionSources` rides the
surface and the normalized generated bin outside the digest identity.
… and Workbench (#596) Copy command.projection and option.aliases onto the browser catalog so a colocated <tool>.cli.ts is visible beside usage; leave projectionSources on the compiler surface only.
…d; document duplicate-projection AB4840 and conventional-CLI AB4804 cases
🦋 Changeset detectedLatest commit: 0b55643 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
commit: |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 339d65666d
ℹ️ 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".
…B4842 for a confirming key yes and positional spellings; AB4840 message shape (#616) - CompiledCliProjection.defaults: canonical key -> the projection's own flags.<key>.default literal (sorted, present only when declared), so the shell applies projection defaults alone before mapInput and a zod .default() stays zod's. CompiledCliOption.defaultValue keeps the effective default for help. Mirrored on RouteManifestCliProjection and the Workbench strict decoder; CliProjectionFlagDefault exported from contracts/routes. - A tool contract key `yes` on a confirming projection is AB4842 against the projection module whatever the key is spelled (CliOptionPolicy .reservedKeys), no longer AB4814 against the tool. - flags.<key>.name / .aliases on a key config.positionals consumes is AB4842; description, default, and required: false stay legal there. - AB4840 orphan and duplicate messages use the common `CLI projection <module> for tool:<server>/<tool>: <detail>.` shape; the misplaced form is `CLI projection <module>: <detail>.`; stray duplicated doc comment removed. docs/diagnostics.md rows and the package-entries pages (en, zh) describe the actual forms.
ScriptedAlchemy
left a comment
There was a problem hiding this comment.
Direction is right: one canonical operation with CLI as a surface projection is exactly the simplification #596/#592 need. Before merge I would tighten five things:
- Resolve the four open correctness threads first (Zod default semantics, Rstest projection loading, ambient/generator
mapInput, and confirmation enforcement in the public CLI shell). They are real behavior mismatches between the projected surface and canonical operation. - Do not defer the
agent().invocation.kindinconsistency if the bulkroutes.mcpCommandspath and explicit.cli.tsprojection are both CLI surfaces. A route should not observekind: 'tool'merely because it reached CLI through the bulk projection. Normalize this now or introduce a separatetransport/projectionfield; otherwise application rendering can branch differently depending on which CLI projection mechanism was used. - Avoid investing further in the old Workbench Routes page. Preserve the manifest/IR fields and tests, but #600 is deleting that page as a destination. Projection details belong in the selected operation's inspector in the new Application explorer. Any substantial new Routes-page-only UI should be minimized or omitted.
- Keep
mapInputexplicitly a surface adapter, not domain logic. Document that it should only reshape/default argv into canonical route input. Domain validation and behavior remain in the operation. This is important because the escape hatch is powerful enough to recreate duplicate CLI logic if left unconstrained conceptually. - Consider whether
.cli.tsshould be described as a colocated projection module rather than a special route convention everywhere in internals. The implementation correctly excludes it from route discovery; preserve that boundary so Application IR contains one operation plus projections, not a pseudo-route hanging off MCP discovery.
The cargo-hauler dry run is strong evidence for the feature; once the above is clean this should remove a meaningful amount of consumer duplication.
…e-exported mapInput (#616) scanRouteModuleExports records namedAmbient, namedGeneratorFunctions, and namedUnresolved (name -> specifier) and keeps declare-d bindings out of namedFunctions, so a projection module's mapInput compiles only when it is a synchronous, non-generator function with a runtime binding. A relative re-export is followed to where the function is declared; one the scan cannot follow is rejected.
- resolvePolicy computes the fallback label once instead of repeating the coalesce inside overrideError - parseCliCommandInput mutates its own copy like the generated bin it mirrors, instead of re-cloning for the confirmation strip and defaults - the generated projection loaders trust the compiled manifest (one projection command per tool) like every sibling loader table; the dedupe pass is gone - projectionCell inlines its only empty-cell text Co-authored-by: Zack Jackson <ScriptedAlchemy@users.noreply.github.com>
|
Owner review (08:30) — all five addressed at
Also per the new rule: a deslop lane (Sol, 52 edits) ran over the full diff before the reviewer pass, on top of your |
…less mapInput overloads; one render branch (#616) - parseMcpCommandInput leaves a non-confirming projection's canonical yes key untouched (the compiler reserves yes only when confirm is true) - scanRouteModuleExports counts a function declaration as a runtime binding only when it has a body, so a lone overload signature is AB4841 - the bin template's three identical openRenderedSession branches are one - tests: non-confirming yes passthrough, bodyless overload, AB4804 with routes.cli conventional beside a projection module - docs: bin/ trigger in project-structure names every routed-CLI source
…on diagnostics to AB4843–AB4845 #618 took AB4840/AB4841 for event preflight gates and declared providers, so the CLI surface projection codes move: AB4840→AB4843 (orphan/misplaced/duplicate module), AB4841→AB4844 (projection contract), AB4842→AB4845 (grammar binding).
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 <ScriptedAlchemy@users.noreply.github.com>
…rming projections (#616) - verified on tip: parseMcpCommandInput already strips yes only when command.mcp.confirm is true, and AB4842 reserves the canonical yes key on confirming projections, so stripping is exactly framework-owned - built executable: the projection fixture's submit tool declares an optional yes: z.boolean() with confirm: false; --yes reaches the tool through the canonical schema (and its absence stays absent) - shell: boolean --yes passes through untouched on a non-confirming projection; a renamed flag keeps its canonical yes key and --yes stays unknown when nothing confirms - compiler: a non-confirming projection may respell its yes key (flags: { yes: { name: 'assume' } }) with the canonical key intact Co-authored-by: Zack Jackson <ScriptedAlchemy@users.noreply.github.com>
… into feat/596-cli-surface-projection
|
Heads-up on codes: #618 (event preflight gates) merged to |
…ath in cliBinSourceInputs; re-pin non-web template hash
…ative-manifest; serialize CompiledCliProjection and option aliases into routes.cli.commands[]
Closes #596. Builds on #593 (#603). Design comment (posted before implementation): #596 (comment).
What changes
One operation, two surface projections. The MCP tool module stays the only executable route (
tool:<server>/<tool>: execution,inputSchema→RouteContract,resultSchema, domain errors, rendered document). Its MCP surface projection is the toolconfigas today. A new opt-in CLI surface projection is a colocated module that is never a route:Vocabulary follows #578/#592: host projection =
targets(claude/codex/cursor/portable); surface projection = MCP tool vs CLI command (the code's existingsurfaceaxis:AgentInvocationKind,CompiledCliSurface). The two compose orthogonally; the routed CLI bin is still emitted once per composite root (AB4765/AB4766unchanged).Compiler (
routes/cli-projection.tsnew leaf;graph.ts,cli-commands.ts,cli-argv.ts). Discovery excludessrc/mcp/*/tools/*.cli.{ts,tsx}from route classification before identity derivation (today such a file becomes the broken tooltool:<server>/<tool>.cli,AB4810) and pairs it with the sibling tool;_-prefixed parks it.configis read by the unchanged static grammar (extractRouteConfig: literals + string consts via the #593 resolver;satisfiesunwraps);mapInputpresence is recorded fromscanRouteModuleExports().namedFunctions.compileProjectedCliCommandsproduces aCompiledCliCommandwithrouteId= the tool id,mcp: { server, tool, confirm }provenance,path = command ?? [tool], andoptionsfrom the tool'sRouteContract.inputthrough the samecliOptionForpolicy assrc/cliroutes with a per-key override (name,aliases,description,default,required: false) applied inside the policy so kebab/reserved/collision checks run on final spellings and aliases (yesreserved when confirming). A tool with a projection leaves the bulkroutes.mcpCommandsset (one command per operation; anincludematching only such tools isAB4822naming the module).AB4813collisions cover projected commands with a projection recovery. A tool without a static contract but with a projection is judged like a CLI route:AB4814/AB4838/AB4839fire with the prefixTool route <path> (CLI projection <module>).AB4837framework-import judgement applies to the projection module.safeIdentitySegmentis now one shared export (two private copies removed).Projection IR (
routes/types.ts, additive;inspect --routesdumps it unchanged):optionsis the mapping ({ key: 'laneKey', option: 'lane' },{ key: 'tickets', option: 'ticket', repeated: true },{ key: 'argv', positional: 0, repeated: true }).defaultsholds onlyflags.<key>.defaultvalues; canonical Zod.default()s stay inCompiledCliOption.defaultValuefor help text and are applied byinputSchema.parse, never beforemapInput. The route manifest (RouteManifestCliCommand.projection,RouteManifestCliOption.aliases,RouteManifestCliProjectionexported fromagent-bundle/devand the browser contracts) and the Workbench strict decoder mirror the fields and are tested; the old Workbench Routes page is not enriched (#600 is dismantling it) — projection details surface later in the selected-operation inspector of the Application explorer, and a comment inroutes-page.tsxsays so.#592manifest v2 reservedroutes.cli.commands[].projection; the name is kept.NormalizedBinEntry.generatedCli.projectionSourcescarries the sources to the bin build.Runtime (
build/entry-shell.ts,cli-entry.ts,build/cli-bins.ts,build/package-build.ts,test/cli.ts,test/render.ts). The generated bin (npmdist/bin/<name>.jsand artifactbin/<name>.mjs, one template) imports each projection module beside its route module. The public shell'sparseMcpCommandInput(cli-entry.ts) is the single confirmation guard for every command withmcp.confirm, bulk or projected: no--yes→CliUsageError(exit 2) before any callback runs, andyesis stripped. ThenparseInput: (1) fillsprojection.defaultsfor absent keys — only those; (2) callsmapInputsynchronously — a throw is aCliInputError(exit 2), a non-function aTypeError; (3) validates the mapped input with the canonicalinputSchema(issues spelled--<option>), which is where canonical Zod defaults apply. Every command the CLI executable dispatches —src/cliroute, explicit projection, and the bulkmcpCommandsprojection — runs the route withinvocation: { kind: 'cli', operationId: 'tool:<server>/<tool>', surface: '<path>' }and providers{ kind: 'cli', props: { args, command } }; the generated MCP server still passeskind: 'tool'. Both projection mechanisms are CLI surfaces, so a route never observes a differentkinddepending on which produced the command (owner requirement; previously the bulk path passedkind: 'tool'). The worker route table (route.kind) is untouched, so layouts still wrap. Help printsMCP tool: <server>:<tool>andProjection: <module>and lists aliases. The two duplicated binsourceInputslists are onecliBinSourceInputshelper that includes projection sources; the projection module is bundled into the bin only, never the Flight worker or MCP entry. The test harness (invokeCli/render) loads projection modules through the generated Rstest registry like routes, layouts, and providers (rstest/setup-module.tsemitsprojectionLoadersas staticimport()s, registry version 7;loadCliProjectionModuleresolves lazily, file-URL import only without a registry, failures areAgentTestError) so.cli.tsxand.js-specifier value imports work under Rstest's module graph; it applies the identical steps and parity is pinned inentry-shell.test.ts.Not forced, not leaked. No projection module → no command (bulk
mcpCommandsunchanged for the rest).ToolConfig,tools/list, annotations,_metagain nothing.Diagnostics (
docs/diagnostics.md; the reference page renders from it)Codes are
AB4843–AB4845: #618 (event preflight gates, #595) landed first and holdsAB4840/AB4841, so the projection codes moved up by three on the merge (1297c46dc); the design comment and earlier review threads sayAB4840–AB4842for the same three diagnostics.AB4843.cli.{ts,tsx}undersrc/mcp/<server>/tools/with no sibling tool (orphan), underresources//prompts//apps/, or a second projection module for the same tool. Recovery: rename to match the tool or prefix_. Reserved undersrc/mcp/**only.AB4844configmissing/outside the static grammar (includes theAB4805/AB4806reason), a key outside the closed set, wrong field shape,required: false/defaulton a canonical-required key withoutmapInput;mapInputthat cannot be a runtime synchronous mapper — ambientdeclare function/declare const, generator, async generator, async function, a re-export the scan cannot follow, or not statically a function (a relativeexport { mapInput } from './x'is followed and judged at its declaration; overloads accepted).AB4845flags/positionalsname a key absent from the tool's contract; aname/alias is not kebab-case, reserved (help/json/ndjson/version), or collides; a canonical key namedyeson a confirming tool (whatever itsname);name/aliaseson a positional key; acommandsegment is not a safe identity segment.Message shape
CLI projection <module> for tool:<server>/<tool>: <detail>.,sourcePath= the projection module. All errors: a projection that cannot compile has no correct partial output.AB4804also names projection modules whenroutes.cli: 'conventional';AB4814/AB4838/AB4839gain the relabelled tool-route prefix.Tests
tests/cli-projection.test.ts(27 cases): pairing without a route/contract binding; defaultcommand/aliases; renamed/repeated/positional/defaulted/relaxed options; confirmation + metadata defaults;AB4843orphan/misplaced/parked;AB4844;AB4845; bulk exclusion +AB4822;AB4813; relabelledAB4814/AB4838;AB4837; digest stability across absolute roots (and change on a rename); custom-server skip; canonicalyeson a confirming tool; positionalname/aliases;AB4843duplicate.cli.ts+.cli.tsx; oneitper rejectedmapInputform (ambient function/const, generator, async generator, async arrow/declaration, const-not-function, unfollowable re-export) plus an accepted-forms table.entry-shell.test.tspins the bin template's projection import,parseInputorder (defaults < mapInput < validation) anddefaults: { laneKey: 'main' }only. Manifest (route-manifest-routes.test.ts) and Workbench decoder (route-manifest-client,routes-model) tests cover the mirrored fields;test-harness-manifest.test.tspinssubmitas an explicit projection outside the bulk set.src/mcp/harness/tools/submit.tsx+submit.cli.tsx(value-importinglib/submit-helpers.js— the shapes a native file-URL import cannot load under Rstest) (argvpositional,laneKey → --lane,tags → --tagde-duplicated bymapInput,cwdrelaxed and derived,confirm: false).tests/projection/cli-dispatch-projection.test.tsinvokes the operation once per surface —invokeMcpTool('submit', …)andinvokeCli(['submit', '--lane', …, '--', …])— and asserts equal structured results, then tests each mapping separately (rename, repeated → array,--passthrough, derivedcwd,mapInputthrow → exit 2, no--yes, help with short path/spellings/Projection:line,invocation.kind === 'cli'observed by a provider).cli-dispatch-projection.test.ts'uses cli invocation kind for bulk and explicit CLI projections while MCP remains tool' invokes one route through the bulk projection, an explicit projection, andinvokeMcpTooland assertscli/cli/tool.cli-dispatch.test.ts'requires confirmation for a projected mutation before dispatch and strips --yes from canonical input' tests the public shell with a spy callback.cli-routes-build.test.ts: a temp project withsrc/mcp/demo/tools/submit.tsx+submit.cli.tsand a confirmingpurge.tsx+purge.cli.tsbuilds; the bin'ssubmit --helpshows the short path and renamed flags; argv round-trips through--json;cwdwith a Zod.default('.')reachesmapInputasundefined(bin returns the derived root);purgewithout--yesexits 2 with the shared message, with--yessucceeds and noyesreaches the tool;inspect --routesshowsprojection.layout-build.test.tsnow asserts a bulk-projected tool observesinvocation: 'cli'from the bin while the layout'swrapped: route.kindstaystooland the MCP call reportstool. Full gate green after integration (1272862e5): build, typecheck, lint,lint:release, unit, route-unit, projection, integration (1131), docs site (parity + dead links).Docs
docs/diagnostics.md,docs/entry-conventions.md; website en + zh:guide/authoring/package-entries.mdx(new "Project one tool as an idiomatic command"; bulk-projection exclusion),guide/authoring/mcp.mdx,guide/start/project-structure.mdx,guide/development/workbench.mdx,reference/configuration.mdx. Changesetagent-bundle: minor— reserving.cli.{ts,tsx}undersrc/mcp/**changes the meaning of a tool file that is legal today.Consumer proof: cargo-hauler dry-run
Harness
/tmp/596-dryrun/run.sh(never touches the checkout): packagent-bundle/@agent-bundle/runtime/rsc-markdown-streamfrom this branch (339d65666), scratch-copy cargo-hauler, addsrc/mcp/hauler/tools/hauler_request.cli.ts+hauler_status.cli.ts(contents as in the design comment: status = renames only, request =argvpositional +mapInputderivingcwdand splitting--afterlists with cargo-hauler'sparseTicketList), deletesrc/cli/request.tsxandsrc/cli/status.tsx, theninspect --routes --json,build, and the bin.cli:request,cli:status,tool:hauler/hauler_request,tool:hauler/hauler_statuscli:request/cli:status; no misclassified…request.clirequest/statuscli:request/cli:statustool:hauler/hauler_request· projectionsrc/mcp/hauler/tools/hauler_request.cli.ts/tool:hauler/hauler_status· projection…/hauler_status.cli.tsstatusoptions--lane(keylane),--status(status),--ticket(ticket), …--lane(keylaneKey),--status(statuses),--ticket(tickets),--command-contains,--cwd,--limit,--session— identical spellings, canonical keysrequest --helpUsage: cargo-hauler request [options] <argv...>,--after … --cwd --host --session, no--yesMCP tool: hauler:hauler_request/Projection: …hauler_request.cli.ts; no--yes(confirm: false)inspect/buildstate: ready, noAB48xx)status --jsonrestart it with hauler daemon restart); the failure is raised insideloadStatusResult, i.e. the projected command reached the canonical operationEvery flag cargo-hauler pins in
README.md,src/skills/**, andtests/route-unit/cli-dispatch.test.ts(--lane,--ticket,--status,--after … -- …) stays valid; itstests/schema-compat.test.ts(asserting the CLI copy equals the protocol enum) becomes moot. The pair's remaining CLI-vs-MCP difference — follow-up wording (hauler resultvshauler_result) — is a rendering choice the tool makes fromagent().invocation.kind, now'cli'under the projection.Deferred
Short
-xaliases (the shell rejects single-dash tokens), asyncmapInput(rejected today withAB4844; the shell's render is synchronous), deprecatingToolConfig.exitCode, projections for resources/prompts, a positional-specific description field (flags.<key>.descriptionapplies to a key that is positional), and the Workbench operation inspector's projection view (#600).Deslop
Deslop:
gpt-5.6-sol-medium, 52 edits (cba686084: 40 comments restating code incli-projection.ts,cli-argv.ts,cli-commands.ts, fixtures and tests; 12 type/flow simplifications — discriminated unions instead of optional-field results +!, narrowedvalidateFlagfields instead of casts, one flags/relaxed loop,projectedCommandscarrying its narrowed projection,mapInputoutput keptunknown,parseCliCommandInputtaking the schema instead ofmodule.inputSchema!). Plus the owner's own pass92346d8dd(in-place mutation inparseCliCommandInput,resolvePolicylabel once, no loader dedupe).Self-review
Reviewer ≠ author model throughout (Grok never reviewed). Owner review 08:30 → all four correctness threads fixed and resolved inline, plus (A)
invocation.kindnormalized (not deferred) and (B) Workbench Routes page not enriched — see the reply comment.Pass 1 on the pre-owner-review diff (
339d65666):gpt-5.6-sol-medium(compiler half) +claude-fable-5-1-thinking-max(runtime half). Findings, all fixed: defaults conflation (→CompiledCliProjection.defaults); harness projection loading (→ registryprojectionLoaders); canonicalyeson a confirming tool (→AB4845); positionalname/aliases(→AB4845);AB4843message shapes (→ one shape); deadGeneratedCliBinSurfacecasts; two--yesmessages (→confirmationRequiredMessage);mapInputTypeError wording. Re-run on920441ca0: both APPROVE, no new findings.Pass 2 on the owner-fix diff (
1272862e5, after deslop):claude-fable-5-1-thinking-high(runtime/shell/harness) +gpt-5.6-sol-medium(compiler/IR/docs).parseMcpCommandInputstrippedyesfrom every projected command, not only confirming ones, contradicting the compiler (yesreserved only whenconfirm) and dropping a non-confirming tool's canonicalyesvalue. Fixed8f035fe45: strip only whenmcp.confirm; test 'hands a non-confirming projection its own canonical yes key untouched'.openRenderedSessionbranches in the bin template after the kind normalization. Fixed: one call.export function mapInput(a: A): B;with no implementation) was counted as a runtime binding. Fixed:scanRouteModuleExportsadds a function declaration only when it has a body; test +AB4844row.project-structure.mdxsaidbin/appears only whensrc/cli/**exists. Fixed en/zh: any routed-CLI source (src/cli/**, bulkroutes.mcpCommands, projection module).AB4804withroutes.cli: 'conventional'beside a projection module. Fixed: test added.runGeneratedCliEntry;kind: 'cli'in template and harness with MCP stilltoolandroute.kinduntouched; projection defaults only pre-mapInput; registry loader keys unique by construction (AB4843); deslop edits behavior-preserving; one changeset; every added file has a production importer.Pass 3 (re-run after fixes) on
13bb3caa1:claude-fable-5-1-thinking-high— all three runtime fixes verified (non-confirmingyespassthrough test uses the exact reproduced shape; one render branch; thebodyguard yields identical export sets for every valid TS shape,route-graph.test.ts59/59), no concrete merge risks.gpt-5.6-sol-medium— compiler fixes and en/zh parity verified, forward-note wording confirmed future-tense, no concrete merge risks. Zero unresolved review threads. After pass 3: mergedorigin/main(#618, #624) with a code renumber only (AB4840–42→AB4843–45, no logic change), plus the owner's0901ae92dregression tests for the non-confirmingyespassthrough; full gate green on1297c46dc(build, typecheck, lint,lint:release, unit, route-unit, projection, integration 1132, docs site) and the owner's three test files re-run under the renumbered codes atb63d5b969. Then merged #620 (web surface;cliBinSourceInputskeeps projection sources and gains the config path main added; the non-web template hash pin inentry-shell.test.tsmoves to this template's bytes) and #626; full gate green again on3690b2aff(all pools, integration 1132, docs site); CI green ondc76050de(onepacked-release.e2eflake —getByRole('heading', { name: 'Skills' })also matches the Skills page'sSource skillsh2, unrelated to this PR — passed on re-run). Then merged #623 (import-only conflict inpackage-build.ts); full gate green on96b1f159a.