ADR 0004: Template App Pattern for New Applications¶
Status: Accepted Date: 2026-04-29
Context¶
SCALR needs to be able to create new apps quickly and reliably, including via AI agents triggered by non-technical users. The two failure modes to avoid are:
- Skeletons that don't run. Many template generators produce code that requires manual wiring before it boots. Agents stumble in that gap.
- Drift between docs and reality. If "how to create an app" lives only in docs, the docs go stale and agents follow outdated steps.
Decision¶
template-app/ is a fully working, runnable application. Not a skeleton, not a code-gen template — a real app that says "Hello, {user.name}" after you log in. cd template-app && docker compose up and it works.
New apps are created by copying the template via a single deterministic script, infra/scripts/create-app-from-template.sh. The script:
- Validates inputs (slug, kind, uniqueness).
- Copies
template-app/to the right location underapps/<kind>s/<slug>/. - Replaces placeholders (
{{APP_NAME}},{{APP_SLUG}}, etc.). - Allocates ports, generates DB names and entitlement keys.
- Updates the root
docker-compose.ymlto include the new app. - Boots the new app.
- Runs the new app's smoke test.
- Reports success or surfaces the failure.
Both humans (make new-app) and agents (via shell tool) call the same script.
Consequences¶
Positive: - The template is itself tested in CI: every push runs the scaffold script with test inputs and boots the result. If the template breaks, no new apps can be reliably created — so this CI job is treated as a release blocker. - One mechanism to maintain. Humans and agents share the same path. - The script does the mechanical work; agents focus on creative work (interpreting the user's intent into models and UI). This separation makes agents far more reliable. - Agents can't forget a step, can't typo a placeholder, can't allocate a duplicate port — the script does that.
Negative: - The template carries the cost of being kept current. If we change how apps wire into compose, the template (and the script) must be updated too. - A "demo" app in the repo has resource cost — its compose definition runs alongside everything else. Mitigated by keeping it minimal and not including it in the root compose by default.
Why one template, not many¶
See 0002-locked-stack.md. With one stack, one template is enough. If we ever need multiple stacks, we add template-app-<stack>/ rather than parameterizing the existing one.