ADR 0004: What the software template leaves out, and why¶
Context¶
On 2026-10-02 the owner found the software template incomplete and asked for a check of everything present and absent. The required structure was a Django backend, a feature-oriented Next.js frontend, repository-wide maintenance checks and explicit import boundaries. The frontend needed route groups, loading and error states, shared user-interface layers, server-only settings, and unit, component, integration, end-to-end, accessibility and smoke tests. The backend needed production settings, database constraints, vector search, API validation and deployment checks, among them a graceful shutdown on SIGTERM. Optional proxy, gateway and model components had to join the same build without changing those boundaries.
The frontend's 35 files were all present. What was missing were the required
shared layers and test kinds, part of the backend specification, and two
repository-level items. The audit added them, listed
in "Built after the audit" below. tests/functional/test_software_conformance.py
now checks every requirement on a fresh render. Each requirement the
template still does not meet is listed there in DEFERRED, with a reason
and the ADR that carries it. This ADR is that record.
Decision¶
- Empty shared layers carry a README, not invented code. The template
has one example feature. It has no honest use for
components/shared/,hooks/,lib/api/,lib/domain/orlib/format/. Each of those folders holds aREADME.mdsaying what belongs there and what it may import.public/is not created until an asset needs it, but a template has to show the shared code layout before it has an honest implementation. cruft checkruns asmake template-check, outsidemake ci. Putting it inmake lintwould need network access and credentials for the private foundry repository on every CI run, which a generated project's CI does not have. The target refuses a project thatcruft createdid not make. A foundry test shows that it fails when the template moves on, and passes aftercruft update.migrate --checkis not inmake lint. Against CI's fresh database, every migration is unapplied, so it would always fail. Run aftermigrate, it always passes. It means something only against a deployed database, as a deployment step.makemigrations --checkstays in lint and now starts the database first.- Dependabot covers the template's npm and Dockerfile pins only. Inside Foundry,
compose.yaml,pyproject.tomland the paper's TeX Live image sit in files that hold Jinja, so Dependabot cannot parse them. Those pins are raised by hand. The weekly template pin report now lists them for review. foundry's tests check that each image is pinned by digest, not that the digest is current. Whether Dependabot accepts the template's{{cookiecutter.repo_name}}directory is unverified until its first run. - No browser matrix. The template requires Playwright with axe and runs it on Chromium. WebKit and Firefox are not promised by this template.
- Components not built yet:
proxy-caddyandgateway-litellm, with the proxy strippingx-middleware-subrequest: built after this ADR. What they leave out is in ADR 0005.ml-pytorchas a software component, for a web app with a model inside it: built after this ADR. How it carries the methodology requirements, and what it leaves out, is in ADR 0006.desktop-electronandmobile-<stack>: these names are reserved so no existing path changes when the components arrive.
Built after the audit¶
| Requirement | Source | Now |
|---|---|---|
| Route groups | Context | app/(main)/page.tsx |
loading.tsx where the server waits |
Context | app/(main)/loading.tsx, using components/ui/state/Pending |
components/ui/, components/layout/ |
Context | Pending, ErrorNotice, AppShell with a skip link, used by the routes |
config/navigation.ts |
Context | the one route list; the navigation and the accessibility sweep read it |
lib/server/env.ts |
Context | requiredEnv, the one reader of server settings |
Test kinds integration/, a11y/, smoke/, fixtures/ |
Context | header wiring; axe on every listed route; the stack smoke script; shared test data |
| Graceful shutdown on SIGTERM | Context | make smoke-stack stops the frontend and requires exit 143, not a kill (137) |
VectorField and HnswIndex |
ADR 0002 | Note.embedding, an HNSW cosine index, similar_notes, and its test |
django.contrib.postgres |
needed by HnswIndex |
installed |
Every setting in .env.example |
ADR 0004 | 13 settings listed, and a test that refuses one read but not listed, or listed but not read |
cruft check |
ADR 0001 | make template-check in every template (decision 2) |
| Dependabot on the template's npm pins | ADR 0004 | Foundry's dependabot.yml (decision 4) |
Consequences¶
- A requirement left out of the template now has to be written into
DEFERRED, or the conformance test has no row for it and the next audit finds it. - The conformance rows cite Foundry's ADRs. When an ADR changes, the rows are rechecked against it.
make ciin a generated project does not tell anyone that the template has moved on.make template-checkdoes, when run by hand or by the plannedtemplate-updateskill.