The Build Pipeline
The fixed phase sequence that turns a project into a ColdBox application.
On this page
The Build Pipeline
bxAgents build runs a fixed sequence of phases, once, producing a plain ColdBox application. Nothing here runs again at request time - that's the whole point of build-time assembly. This page walks the phases in the exact order BuildPipeline.bx runs them.
flowchart TD
A["1 Β· Resolve config<br/><small>AgentConfigResolver</small>"] --> B["2 Β· Discover<br/><small>ProjectDiscoverer</small>"]
B --> C{"3 Β· Validate<br/><small>ProjectValidator</small>"}
C -->|"any error"| X["Build throws.<br/>.build/app is never written or touched"]
C -->|"clean<br/><small>warnings never block</small>"| D["4 Β· Generate"]
D --> D1["1 Interceptors"] --> D2["2 Gateways"] --> D3["3 MCP"] --> D4["4 Router"]
D4 --> D5["5 Web UI"] --> D6["6 Core app skeleton"] --> D7["7 Tools/skills copy"] --> D8["8 Runtime libraries"] --> D9["9 Scheduler"]
D9 --> E["5 Β· Normalize + write<br/><small>ManifestNormalizer</small>"]
E --> F[".build/manifest.json<br/>+ .build/app - a plain ColdBox application"]
style C fill:#fff3cd,stroke:#856404
style X fill:#f8d7da,stroke:#721c24
style F fill:#d4edda,stroke:#155724
Validation is the gate: it collects every error rather than failing fast, and nothing is generated until it comes back clean.
(Packaging into a .bxa is a deliberately separate step - see Deployment & Secrets - so a fast build β inspect β build again loop never pays a packaging cost it doesn't need.)
1. Resolve config
AgentConfigResolver loads the agent definition (Agent.yaml, or the class-based Agent.bx), applies the active environment's overrides (the environments: block of an Agent.yaml, or the override method and configure() of an Agent.bx), then deep-merges in boxlang.json/boxlang-{env}.json/miniserver.json/miniserver-{env}.json if present. Produces the single resolved config struct every later phase reads from.
2. Discover
ProjectDiscoverer walks the project root and enumerates every convention folder (tools/, skills/, subagents/, gateways/, mcp/, interceptors/, modules/) into raw { name, path, type } entries. schedules/ is the one exception - it's not a list of entries, just a single hasScheduler/schedulerPath pair, since it holds one real ColdBox scheduler file rather than a set of BxAgents-defined config entries. Pure discovery - no interpretation of file contents happens yet.
3. Validate
ProjectValidator runs every validator and collects every error (never fail-fast) plus any warnings: duplicate tool/skill/model/subagent names, duplicate agent names across the whole subagent tree (see subagents/), circular subagent/module references, the two gateway entry shapes, remote MCP config completeness, and model/provider validity. If any errors were collected, the build throws immediately here - no .build/app is written or touched. Warnings (e.g. a schedules/ folder with no Scheduler.bx in it) never block the build.
4. Generate
Only reached once validation is clean. In order:
- Interceptors -
InterceptorSplittercopiesagent-scope interceptors into.build/app/interceptors,runtime-scope ones into a separate.build/runtime-interceptorsdirectory, outside the app (BxAgents does not register those for you). - Gateways -
GatewayGeneratoremitsaiGatewayRegistry().register(...)statements for channel-adapter entries, and writes a small.build/app/handlers/*.bxwebhook handler for eachwhatsapp-cloud,teams,twilioorgithubentry. If any entry is a push-style gateway (e.g.type: "telegram"), it also produces the start-up statement that builds a single bx-aiGatewaySession(bundling every push-style gateway) bound to the project's root agent. - MCP -
McpGeneratorcopies localmcp/*servers into.build/app/mcpand emits theirmcpServer(...).registerTool(...)registration statements. - Router -
RouterGeneratorwrites.build/app/config/Router.bx: the reserved/__bxagentsagent route (unlessagentApi.enabledisfalse), oneroute(path).toAi(...)/toMCP(...)per exposure entry, a singleroute("/gateways").toAiGateway()if the project has any channel-adapter gateway, and one webhook route perwhatsapp-cloud(GET+POST),teams,twilioorgithubpush-style gateway present. - Web UI -
WebUiGeneratorruns for anyexposes: "webui"entry, writing the static<path>/index.html,handlers/ChatUi.bx(the twenty-action API),models/ChatDb.bx(the SQLite store and its forward-only migrations, built at boot by a start-up statement rather than on whichever request touches the database first), the user models (User,UserService,PasswordHasher), and - only whenapiKeyEnvVaris set -interceptors/WebUiAuthGate.bx. Whenusersare declared it also writes the account seeder,interceptors/WebUiLoginGate.bxandhandlers/ChatAuth.bx. It returns the resolved database config, which the next step needs. - Core app skeleton -
ColdBoxAppGeneratorwritesApplication.bx,config/ColdBox.bx,config/WireBox.bx,agent/GeneratedAgentFactory.bx, andindex.bxm, threading in every statement gathered above (gateway registrations, theGatewaySessionstart, MCP registrations, the web UI's store warm-up and account seeding, and - iftools/has any files - a bareaiToolRegistry().scan("tools")call) intoApplication.bx'sonApplicationStart(). Interceptors are registered there too, onegetInterceptorService().register(...)per agent-scope interceptor and web UI gate: a declarativeinterceptorsarray inconfig/ColdBox.bxnever reaches ColdBox's interceptor service, so the generated config declares none. Every generated agent also now always receives a checkpointer (withCheckpointer(...), defaulting to acache-backedaiMemory()if the project declares nocheckpointerconfig and the class set none of its own) - without one, human-in-the-loop approval flows through any gateway other thanclifail outright.config/WireBox.bxmaps every agent in the tree (root + every subagent) under its own declaredname, not just the fixed root"GeneratedAgent"alias - see schedules/. Each agent node'sinstructions.mdis copied into the app as part of this step, mirroring the source layout underagent/- the root project's to.build/app/agent/instructions.md, each subagent's to.build/app/agent/subagents/<name>/instructions.md- so every node keeps its own and the built app carries the file it reads at runtime. The factory reads all three of its runtime paths (instructions.md, its owntools/, and any class-based agent's copiedAgent.bx) through a small generatedresolveAppPath()helper that resolves them relative to the factory's own location on disk, falling back to the build-time absolute path if that self-location doesn't resolve - which is what makes a packaged.bxagenuinely relocatable rather than runnable only on the machine that built it. For a project with awebuiexposure it also turns session management on, registers the SQLite datasource (naming it as the app default viathis.datasource), creates the database's parent directory inonApplicationStart()- SQLite creates the file but never the folder holding it - and pins qb's grammar inconfig/ColdBox.bx. - Tools/skills copy -
ToolsSkillsCopierwipes and rewrites.build/app/toolsand.build/app/skillsverbatim from your project's own folders. - Runtime libraries -
RuntimeLibsCopiercopies the ColdBox runtime bundled inside BxAgents into.build/app:coldbox/plusmodules/qb,modules/cbsecurity,modules/cbauth,modules/cbmailservicesand their dependencies. Alib/folder in your project (same layout) is copied afterwards and wins, which is how you add or override a library. The bundled copy exists only in an installed or built module, so a source checkout skips this step with a warning. - Scheduler -
SchedulerGeneratorcopiesschedules/Scheduler.bxthrough to.build/app/config/Scheduler.bxuntouched, if present - no generation, it's real ColdBox code you wrote yourself.
5. Normalize + write the manifest
ManifestNormalizer produces the canonical, hash-stamped internal manifest from the discovery + resolved-config data, and the pipeline writes it to .build/manifest.json.
Idempotency
Rebuilding an unchanged project produces byte-identical output, down to the manifest's per-file content hashes - the entire point of paying the assembly cost once, at build time, rather than deferring any of this work into request handling.