1.0.0-beta-5 could create a project. What it could not do was survive the next five minutes.
(status) — the first thing bb quickstart printed — was undefined. The module bb quickstart scaffolded answered 404. Devtools reached no classpath. bb scaffold ai died resolving wagoe-ai. The quickstart’s last step pointed at a path in a module a generated project does not enable. Most of this release’s forty-two fixes are on that one path.
Read the security section before upgrading, and the two breaking changes below it.
Security: any Bearer token authenticated
Any value in an Authorization: Bearer header was accepted (BOU-374). JWT validation ran, and its verdict was discarded. Upgrade if you use JWT auth.
A second one from the same sweep: session authentication put the whole user record on :user and stripped only :password-hash, so MFA secrets and backup codes reached every handler (BOU-373). The same fix moved authentication ahead of the middleware that depends on it — :tenant-membership was nil on every request, so require-tenant-member refused all of them.
Breaking: modules emit Reitit route data
The normalized route format is gone, along with IRouter and the translation layer between them (BOU-331, ADR-037). A route map becomes route data:
["/users" {:get {:handler users/list-users}}]
Prefixing, versioning and the interceptor stack are shape-agnostic and still apply. bb scaffold generates the new form.
Breaking: a scaffolded module’s web routes mount under /web
Their URLs move, from /<path> to /web/<path> (BOU-330). That was the documented contract all along; this is the release that keeps it. The same change makes routes a contribution rather than a slot in platform — a library can serve HTTP without platform knowing it exists, and a module names its own :web-prefix.
Two smaller ones. Feature flags take the environment as a parameter, so 1-arity calls move to wagoe.platform.shell.feature-flags (BOU-302). And :wagoe/router settings now reach the router (BOU-357): :adapter is gone, and an unrecognised :coercion fails the boot instead of silently handing you Malli.
Added: two routes that can both match a request fail the boot
Reitit resolves an ambiguous pair by rule; you find out which rule at 3am. The boot now refuses the pair instead (BOU-356). :wagoe/router {:conflicts nil} restores the old behaviour if you need to ship first and fix after.
The first version of that check was too strict — it required every segment to separate a pair, so an application using both admin and search would not start at all: /web/admin/search/:index-id against admin’s generic /web/admin/:entity/:id (BOU-392). It now decides at the first segment that separates them, which is what Reitit does. Genuinely ambiguous pairs and catch-alls still fail.
Added: examples/shop, a generated application in the repository
A real generated project you can read and run, regenerated by bb example:regen, booted and asserted in CI (BOU-300). Documentation drifts from the scaffolder because nothing runs it; this cannot.
Added: wagoe doctor
One diagnostic front door — environment, config, commands, setup — ending in a single next action (BOU-324). A healthy project now produces no diagnostic noise: four checks were warning about things that are not problems, which teaches readers to skim past the ones that are.
Added: com.wagoe/wagoe-config
Config loading and its typed accessors become a library (BOU-306), so libraries stop resolving wagoe.config at runtime. That makes it 28 application libraries, 31 published artifacts. The monorepo’s own wagoe.config is now wagoe.system-config — two namespaces of that name on one classpath shadow each other.
Fixed: the first-run path
-
(status),(modules),(routes)and(fix!)now exist in a generateddev/user.clj(BOU-319). The quickstart had been naming helpers that were not there. -
(status)points at the admin UI that is running (BOU-394). In a generated project it named no admin URL; in the monorepo REPL it advertised/admin, which 404s — read from a config key that never exists, so the fallback was the only branch it ever took. -
(status)and(modules)list modules, not Integrant keys (BOU-399). One module used to arrive as several of its parts —admin-routes, admin-schema-provider, …rather thanadmin. -
The dev dashboard sees the system you actually started (BOU-400). It read
integrant.repl.state/system, which only a REPL(go)fills, so every ordinary server start left it reporting "3 components · all healthy" while forty more ran unseen. -
bb scaffold aiworks in a generated project (BOU-401). It never resolvedwagoe-ai;bb setup aifailed the same way, silently. -
A scaffolded module booted but served nothing (BOU-312). The first-run smoke now curls the module it scaffolded and requires a 2xx.
-
bb scaffold generateno longer overwrites an edited module without asking (BOU-308) —--forcewas declared, threaded, and read by nothing.field,endpointandadapternow fail when the module is not there, rather than leaving a migration behind for a column the schema would reject (BOU-364). -
The MCP server advertised seven resources and could serve none in a project (BOU-320). The knowledge base ships in the jar.
-
A generated project no longer ships
bb deploy(BOU-325), which published Wagoe’s libraries to Clojars from a user’s project.
Fixed: what the screen was telling you
Form validation errors never reached it (BOU-381) — htmx discarded the 400 that carried them, so pressing Create or Save did nothing at all. When they did arrive they arrived as two unrelated styles of complaint on one submission (BOU-393): a password error in the form-level panel, an email error beside it as inline text.
"Your code" in an error now means your code (BOU-395). The stack-trace filter recognised the framework by listing four of its libraries, so the rest were reported to you as yours, while your own namespaces were folded away under "Framework (N frames)". An error thrown in an application’s own code answered "No user code frames found".
Also: admin tables format dates and timestamps rather than printing the database value (BOU-382); static assets revalidate instead of caching blind, so a deployed fix is no longer invisible for days (BOU-389); a malformed request is a 400 naming the fields instead of a 500 (BOU-321); and the dev dashboard names your database rather than its Java class (BOU-396).
Fixed: the brand assets still drew Boundary
The four assets behind brand-logo were renamed to wagoe-* and kept drawing the old mark and wordmark (BOU-379). bb check:no-boundary cannot see artwork.
Removed
wagoe.platform.shell.interfaces.http.middleware — a parallel correlation-id, logging and exception stack no application ran (BOU-372). wagoe.platform.shell.interfaces.http.routes — a second, documented route builder nothing wired, which applied no security stack (BOU-358). And realtime’s `UserJWTAdapter, which reached into wagoe.user.shell.auth undeclared and threw from Clojars (BOU-305); supply your own IJWTVerifier.
The gates that were not gates
The e2e suite had 52 tests sitting behind if: false (BOU-297). A pull request from a fork ran no tests at all, leaving the required context pending rather than failing — and pending is not red (BOU-315). A tag on an untested commit could publish 31 immutable artifacts; publishing now requires a green All Tests Passed on the tagged commit (BOU-314).
New ones: bb check:error-shape fails an untyped boundary throw (BOU-323) — 58 of them now say what kind of failure they are, and the allowlist it shipped with, 81 findings on 18 August, is empty. bb check:isolation builds each library on its own in CI, proving "independently publishable" rather than asserting it (BOU-304). bb check:versions covers documentation as well as source, 96 locations rather than 59 (BOU-317) — the ungated half was the half users copy from.
Version alignment
All 31 artifacts bumped to v1.0.0-beta-6 to maintain lockstep versioning.
Upgrade
Re-run the installer to pick up the latest release:
curl -fsSL https://get.wagoe.org | bash
Upgrade promptly if you use JWT authentication. Migrating an existing project means rewriting module routes to Reitit data, and expecting scaffolded web routes at /web/<path>. Existing projects keep booting across the namespace change — module discovery falls back to wagoe.<module>. Direct callers of the user library’s two auth shell namespaces should read UPGRADING.md; their results now carry a typed error.