# ADR 0007: An interactive generator that configures, not templates

**Status:** accepted

## Context

[ADR 0004](0004-template-repo-not-cookiecutter.md) rejected cookiecutter
because "the template itself is dead code: it cannot be browsed
comfortably, cannot run, and cannot be CI-tested as-is, and template bugs
are only found by generating." That reasoning still holds. But the same
ADR left a door open in its own consequences: "A cookiecutter wrapper
could be generated mechanically later if a downstream community wants
one; nothing in this decision blocks it."

What it left behind was an onboarding path made of manual steps. Click
**Use this template**, clone, copy `.env.example`, run `make setup`, then
work through seven steps in the bring-your-own-domain tutorial and
hand-edit `domain/domain.yaml`. The only automation was
`scripts/init_domain.py`, which set a name and a description. Everything
that actually makes a project yours stayed manual: the ontology, the
credential mode, the corpus source, the parser and reranker choices, the
environment manager.

Cookiecutter Data Science, the nearest comparable tool, answers this with
a short questionnaire and no directory to create first. The gap was never
the idea of a wizard. It was that the obvious way to build one drags back
in exactly what ADR 0004 refused.

## Decision

Ship an interactive generator that is a post-fetch applier rather than a
template renderer.

`sci-rag-new` downloads this repository at a pinned tag, then rewrites
its configuration files in place. `sci-rag init` does the same inside a
checkout you already have. Both drive the same `run_wizard()`.

Four properties keep this inside ADR 0004 rather than around it:

* No placeholders. There is no `{{ }}` syntax and no `{{ }}` directory
  anywhere in the repository. A test asserts that a generated project
  contains no bare `{{` in any Markdown, TOML, YAML, or JSONL file.
* The template stays the runnable application. What the generator
  fetches is this repository, byte for byte: browsable, runnable, and
  CI-tested as itself. Nothing here becomes real code only after
  generation.
* The wizard is ordinary tested code. It lives in
  `src/sci_rag/scaffold/`, the unit suite covers it like anything else,
  and `.github/workflows/generated-projects.yml` exercises the result
  once per environment manager. The test suite finds template bugs; you
  do not have to generate a project and look.
* The package name still does not change. Derived projects keep the
  `sci_rag` import path, for the reason ADR 0004 gave: it preserves the
  ability to diff against, and pull improvements from, upstream.

The appliers round-trip their output through the same models the
application reads. `domain/domain.yaml` comes from serializing a
`DomainConfig`, so a generated profile cannot be something
`load_domain()` rejects, and an LLM-drafted ontology goes through that
same validation before anything reaches disk.

One `RunnerProfile` per environment manager renders uv, pixi, conda, and
venv+pip. The kit is manager-wired in five places (task commands, CI,
container, dev container, docs), and any disagreement between them
breaks a generated project on its first run.

## Consequences

* Onboarding is `pipx install sci-rag-kit`, then `sci-rag-new`. No
  repository to clone first, no file to hand-edit before the first run.
* The generator ships in the same distribution as the application, so
  installing it also installs `sci-rag`. That install is heavy for what
  is mostly a generator: fastapi, uvicorn, asyncpg, sqlalchemy, alembic,
  pgvector, google-genai, mcp, and pypdf all come along. Accepted
  knowingly. The same install is the tool the user wants next, pipx
  isolates it, and the scaffold package lazy-imports the runtime, so
  startup stays fast even though installation is not.
* `sci-rag-new` fetches the tag matching its own installed version. A
  given generator release always produces the same project, and
  upgrading the generator is the only way to change what a new project
  contains.
* The generator is now a compatibility surface. CI and the documentation
  both read its question names and answers-file format, so changing them
  follows the promises in [VERSIONING.md](../VERSIONING.md).
* The homepage session is generated from the question list rather than
  recorded by hand, so it cannot quietly drift from what the wizard asks.
* ADR 0004 is not superseded. This implements the escape hatch it
  described, under the constraints it set.

## Reversal conditions

Revisit if any of these stop being true:

* A generated project stays diffable against upstream. If it stops
  being so, the generator has started producing structure rather than
  configuration.
* A person will sit through the question set. Past that point the
  answers file is the primary interface and the interactive path is the
  fallback, which is a different tool.
* No placeholder syntax appears anywhere in the tree. That is the
  specific failure ADR 0004 refused, and the reason the no-`{{` test
  exists.
