Retornatus

Presets

retornatus init can write a starting .retornatus/config.toml from a preset. Presets are TOML files inside the installed package (retornatus/bootstrap/presets/). They are not hard-coded command branches, and they are not the Spec Guardrails .specs/design.md workflow.

Without --preset, init behaves as it does today: a minimal config, the directory tree, and the idempotent .gitignore block (local index, runtime cache, private keys, and env files). Public keys under .retornatus/keys/*.pub stay committable.

An existing config.toml is left in place unless you pass --force-config. --force alone still recreates the minimal config when no preset is selected. It does not apply a preset over a file that is already there.

Commands

retornatus init --list-presets
retornatus preset show python
retornatus preset show python-platform
retornatus preset show fastapi
retornatus preset show django
retornatus preset show rag
retornatus preset show worker
retornatus init --preset python
retornatus init --preset python-platform
retornatus init --preset fastapi
retornatus init --preset django
retornatus init --preset rag
retornatus init --preset worker
retornatus init --preset python-platform --force-config

An unknown name exits with code 2 and lists the presets that ship in this install.

preset show prints the config init --preset would write, including the comment block of suggested commands.

python

For a Python 3.11+ repository whose proof is tests and static checks.

CheckCommandEvidence type
pytestuv run pytest -qtest_result
ruffuv run ruff check src app testslint_result
mypyuv run mypylint_result

[governance.scope] code_globs is src/, app/, and tests/**. Those globs describe the roots the ruff command names. They do not change gate scope. If app/ or src/ is not a directory in your tree, edit the ruff argv or the command fails.

[assurance] required_checks means verify accepts execution evidence only when the argv matches one of these commands, the exit code is 0, the recorded commit is HEAD, and the worktree is clean. See Gates.

python-platform

For people who ship Python backend + DevOps + AI from one repository: services, Compose or Terraform or Helm or GitHub Actions, and LLM, RAG, agents, MCP, or an offline eval suite.

Use python when pytest, ruff, and mypy are the whole bar. Use python-platform when deploy files or model behavior are normal work. It extends python, so the three checks and the code globs above are included.

This preset does not turn Retornatus into a live observability product. It adds two path-triggered rules to verify.

When a rule runs

verify looks at Task resources (the Change's scoped paths) and at git worktree changes (staged, unstaged, and untracked). A committed file counts when a Task lists it. If nothing in that set matches the globs, the rule's status is not required and it does not affect the verdict.

verify --json includes a surfaces array only when [surfaces] is in config. Each item has name (ship or ai), status (not required, satisfied, or unsatisfied), matched_paths, required_checks, and missing.

Ship surface

Triggered by:

GlobSuggested command
**/Dockerfiledocker build .
**/docker-compose.ymldocker compose config --quiet
**/*.tf, /terraform/terraform validate
/charts/, /helm/helm template .
/.github/workflows/actionlint

Those commands are comments in the generated config. verify uses them as the packaged defaults until you write [[surfaces.ship.checks]]. A checks table you write replaces the whole default list, so copy every check you still want.

Each matching check needs executed evidence: evidence run, exit code 0, argv equal to that check. One narrative note covers the Change, not one note per tool. Record it with subject ship rollback (override with note_subject):

retornatus evidence add -c C-0001 -t repository_observation \
  -s "ship rollback" --source "Redeploy the previous image tag" --producer owner

Placeholder text (tbd, todo, n/a, (fill in)) does not count. A self-reported command does not count as the infra check.

AI surface

Triggered by /prompts/, /mcp/, /evals/, /tests/eval/, **/llm, **/rag, and **/embed.

The default eval command is written in config and is what verify requires:

uv run pytest tests/eval -m "not live"

Change surfaces.ai.run when the suite lives somewhere else. The same Change also needs a narrative note with subject ai fallback describing how the feature degrades when the model is down or wrong. Placeholder text does not count.

Override the globs

[surfaces.ship]
globs = ["deploy/**", "**/*.tf"]

[surfaces.ai]
globs = ["prompts/**", "evals/**"]

Narrowing globs drops paths that no longer match. A path that matches globs but no check is unsatisfied until you configure a command for it.

fastapi

For a FastAPI service. It extends python-platform, so the pytest, ruff, and mypy checks, the src/, app/, and tests/ code globs, and the ship and AI surfaces above are included. src/ already covers src/<package>/.

Extra [governance.scope] code_globs:

GlobLayout it names
routers/**Routers at the repository root
api/**An api/ package at the repository root
schemas/**Pydantic schemas at the repository root
alembic/**Alembic migrations at the repository root

Those globs describe roots. They do not change gate scope. Nested routers under app/ or src/ are already covered by the inherited globs.

Suggested evidence

These commands are comments in the generated config. They are not [assurance] required_checks. httpx, the application import path, and Alembic may be absent, so verify does not demand them.

SuggestionCommandWhy it stays a comment
httpxuv run pytest -qSame argv as the inherited pytest check. Write the tests with fastapi.testclient.TestClient or httpx ASGITransport. httpx may be absent
OpenAPIuv run python -c "import json; from app.main import app; print(json.dumps(app.openapi()))"Example export. The module path is yours (app.main here). Write openapi.json and diff it in review
Alembic SQLuv run alembic upgrade head --sqlOptional alternative to alembic check. Prints SQL and does not apply it. Alembic may be absent

Alembic migrations are a ship surface

alembic/ and /alembic/** are added to the ship globs. A Task resource or a worktree path under Alembic makes the ship rule run. verify then requires one narrative note with subject ship rollback (the subject inherited from python-platform). For a migration, that note has to describe the downgrade, for example alembic downgrade -1 or restoring the previous revision. Placeholder text does not count.

The packaged Alembic check is optional:

[[surfaces.ship.checks]]
name = "alembic"
optional = true
globs = ["alembic/**", "**/alembic/**"]
run = ["uv", "run", "alembic", "check"]

optional = true covers those paths so verify does not report no ship check covers. It does not require alembic check to have been executed. alembic check needs a database, and Alembic may not be installed. The command stays in the comment block next to alembic upgrade head --sql.

Docker, Compose, Terraform, Helm, and workflow paths still require their executed checks. A [[surfaces.ship.checks]] table you write still replaces the whole packaged list, including this optional Alembic check, so copy every check you still want.

django

For a Django project. It extends python-platform, so the pytest, ruff, and mypy checks, the src/, app/, and tests/ code globs, and the ship and AI surfaces above are included. src/ already covers src/<package>/.

Extra [governance.scope] code_globs:

GlobLayout it names
manage.pyThe Django entry script at the repository root
/settings.pySettings modules one directory down (config/settings.py, myproject/settings_local.py)
*/urls.pyURLConf modules one directory down (myproject/urls.py)
apps/**An apps/ package
*/migrations/**Migrations one directory down (polls/migrations/)
templates/**Project templates
static/**Project static files

Those globs describe roots. They do not change gate scope. Nested modules under app/ or src/ are already covered by the inherited globs. */migrations/ is only a scope glob. The ship surface uses the wider /migrations/**.

Suggested evidence

These commands are comments in the generated config. They are not [assurance] required_checks. Django, pytest-django, and a project-specific settings module may be absent, so verify does not demand them.

SuggestionCommandWhy it stays a comment
check --deploypython manage.py check --deployDeployment system checks. Django may be absent
pytest-djangouv run pytest -qSame argv as the inherited pytest check. Install pytest-django and set DJANGO_SETTINGS_MODULE (or pass --ds). The plugin may be absent
manage.py testpython manage.py testDjango's test runner when pytest-django is not the suite. Django may be absent

python manage.py makemigrations --check --dry-run is also commented. It is the optional ship check below, not a second required check. It reports models that lack a migration and does not write files.

Migrations are a ship surface

/migrations/ is added to the ship globs. A Task resource or a worktree path inside a migrations directory makes the ship rule run. verify then requires one narrative note with subject ship rollback (the subject inherited from python-platform). For a migration, that note has to describe how to reverse it, for example python manage.py migrate polls 0001_initial back to the previous applied migration, or restoring that revision. Placeholder text does not count.

Spec Guardrails appendix B calls the same idea a migrate reverse plan, recorded next to the previous image. Retornatus keeps that plan as the rollback note. It does not run migrate.

The packaged migration check is optional:

[[surfaces.ship.checks]]
name = "migrations"
optional = true
globs = ["**/migrations/**"]
run = ["python", "manage.py", "makemigrations", "--check", "--dry-run"]

optional = true covers those paths so verify does not report no ship check covers. It does not require makemigrations --check --dry-run to have been executed. The command needs Django, and Django may not be installed. The command stays in the comment block next to check --deploy and the test-runner suggestions.

Docker, Compose, Terraform, Helm, and workflow paths still require their executed checks. A [[surfaces.ship.checks]] table you write still replaces the whole packaged list, including this optional migration check, so copy every check you still want.

rag

For a RAG, LLM, or MCP service. It extends python-platform, so the pytest, ruff, and mypy checks, the src/, app/, and tests/ code globs, and the ship and AI surfaces above are included. src/ already covers src/<package>/.

Extra [governance.scope] code_globs:

GlobLayout it names
prompts/**Prompt files at the repository root
evals/**An eval suite at the repository root
tests/eval/**Offline eval tests under tests/ (already inside the inherited tests/**)
mcp/**An MCP server package at the repository root
retrieval/**Retrievers at the repository root
rag/**A RAG package at the repository root
ingest*/**ingest/, ingestion/, and similar root directories
embeddings/**Embedding code at the repository root
vectorstore*/**vectorstore/, vectorstores/, and similar root directories
indexes/**, *_index/, vector-index/Retrieval indexes. A bare index/** is not used, because it also matches trees such as docs/index/
config/**/model, models.toml, models.yaml, models.yml, models.jsonConfig files whose names carry the model. This does not match src/**/models.py

Those globs describe roots. They do not change gate scope. Nested modules under app/ or src/ are already covered by the inherited globs.

AI surface

The AI globs from python-platform stay (/prompts/, /mcp/, /evals/, /tests/eval/, **/llm, **/rag, **/embed). The preset appends directory globs the filename patterns miss, plus model-name config:

GlobWhy it is here
/retrieval/Retriever packages anywhere in the tree
/rag/A rag/ package whose files are not themselves named rag
**/ingest*/**Ingest directories anywhere (ingest/, ingestion/)
/embeddings/An embeddings package. **/embed already matches a file named embeddings.py; it does not match embeddings/store.py
**/vectorstore*/**Vector-store packages (vectorstore/, vectorstores/)
/indexes/, **/*_index/, /vector-index/**Retrieval indexes. docs/index/ does not match
/config//model, **/models.toml, **/models.yaml, **/models.yml, **/models.jsonConfig that names a model. src/domain/models.py does not match

A Task resource or a worktree path that matches makes the AI rule run. verify then requires executed evidence of the inherited eval command:

uv run pytest tests/eval -m "not live"

Change surfaces.ai.run when the suite lives somewhere else. The same Change also needs a narrative note with subject ai fallback describing how the feature degrades when the model is down or wrong (for example, return a cached FAQ). Placeholder text does not count.

verify checks that this command was executed and that the fallback note exists. It does not score the golden set, the prompt snapshots, or the MCP server. A passing eval command is not a quality grade.

Suggested evidence

These commands are comments in the generated config. They are not [assurance] required_checks. The script path, the snapshot module, and the MCP SDK may be absent, so verify does not demand them.

SuggestionCommandWhy it stays a comment
Golden setuv run python scripts/eval_retrieval.pyExample retrieval-quality script. The path is yours. This is not the required eval command, and verify does not score it
Prompt snapshotsuv run pytest -q tests/test_prompt_snapshots.pyExample snapshot module. The path is yours and may be absent
MCP smokeuv run python -m mcp_serverExample server smoke test. The module is yours and the MCP SDK may be absent

Spec Guardrails appendix D records the same pair in design.md: an offline eval harness (pytest tests/eval/ -m "not live") and a fallback/degrade note. Retornatus keeps that pair as executed Evidence plus the ai fallback subject. It still does not call a live model.

worker

For a background job service: Celery, RQ, Dramatiq, arq, or scheduled jobs (Celery beat, APScheduler, cron-style schedules). It extends python-platform, so the pytest, ruff, and mypy checks, the src/, app/, and tests/ code globs, and the ship and AI surfaces above are included. src/ already covers src/<package>/.

Extra [governance.scope] code_globs:

GlobLayout it names
tasks/**A tasks/ package at the repository root
workers/**Worker processes at the repository root
jobs/**Job modules at the repository root
schedules/**Schedule definitions at the repository root
celery_app.py, celery.py, celeryconfig.pyThe Celery app and its config at the repository root
worker.pyAn RQ, Dramatiq, or arq entry module at the repository root

Those globs describe roots. They do not change gate scope. Nested modules under app/ or src/ are already covered by the inherited globs.

Task code is a ship surface

A job that runs twice, or a retry that fires after a deploy, is a production change even when no deploy file moved. The preset appends these ship globs:

GlobWhat it matches
/tasks/, **/tasks.pyCelery, Dramatiq, or RQ task modules (app/tasks.py, src/myapp/tasks/email.py)
/workers/, **/worker.pyWorker packages and entry modules (arq WorkerSettings, RQ or Dramatiq workers)
/jobs/Job modules
/celery_app.py, /celery.py, **/celeryconfig.pyThe Celery app, routing, and queue settings
**/beat*, /schedules/Periodic schedules (beat_schedule.py, schedules/cron.yaml)
/queues.py, /queues.toml, **/queues.y*mlQueue declarations and routing config

tests/test_tasks.py and docs/jobs.md do not match. A Task resource or a worktree path that matches makes the ship rule run.

Retornatus has two surface kinds, ship and AI. There is no third kind for jobs. The preset reuses the ship surface and sets its note subject to ship rollback and job retry. For task code, the note has to say how the job retries (attempts, backoff, dead-letter queue) and why running it again is safe (an idempotency key, an upsert, or a check before the side effect). If the Change also touches a Dockerfile or a workflow, the same note covers the rollback. Placeholder text does not count, and a note with the plain ship rollback subject does not satisfy this preset.

retornatus evidence add -c C-0001 -t repository_observation \
  -s "ship rollback and job retry" \
  --source "Redeploy the previous tag. send_invoice retries 3 times with backoff and skips invoices already sent, so a re-run is safe." \
  --producer owner

The packaged worker check is optional:

[[surfaces.ship.checks]]
name = "worker"
optional = true
globs = ["**/tasks/**", "**/tasks.py", "**/workers/**", "**/worker.py", "**/jobs/**", "**/celery_app.py", "**/celery.py", "**/celeryconfig.py", "**/beat*", "**/schedules/**", "**/queues.py", "**/queues.toml", "**/queues.y*ml"]
run = ["uv", "run", "celery", "-A", "app", "inspect", "ping"]

optional = true covers those paths so verify does not report no ship check covers. It does not require celery inspect ping to have been executed. The command needs a running broker and worker, the app path (app here) is yours, and Celery may be absent.

Docker, Compose, Terraform, Helm, and workflow paths still require their executed checks. A [[surfaces.ship.checks]] table you write still replaces the whole packaged list, including this optional worker check, so copy every check you still want.

Suggested evidence

These commands are comments in the generated config. They are not [assurance] required_checks. Celery, RQ, arq, a broker, and the import path may be absent, so verify does not demand them.

SuggestionCommandWhy it stays a comment
Eager tasksuv run pytest -qSame argv as the inherited pytest check. Run tasks in-process with Celery task_always_eager, RQ is_async=False, or the Dramatiq StubBroker
RQ burstuv run rq worker --burstWorker smoke test. Drains the queues once and exits. Needs a reachable Redis
arq checkuv run arq app.worker.WorkerSettings --checkHealth check for a running arq worker. The settings path is yours

verify checks that the note exists. It does not run the job twice, and it does not judge whether the retry policy is correct.

Limitations

LimitationWhat it means
Structural checks onlydocker compose config, terraform validate, helm template, and actionlint parse or render. They are not terraform plan review, a cluster dry-run, or a security audit
Not AppSecSecrets, threat models, and authz review stay on gate scope sensitive paths and on review_result / security_test claims
Eval quality is yoursverify checks that the configured command was executed and that a fallback note exists. It does not score the golden set
No live tracesProduction LLM telemetry is out of scope. The record is git plus Evidence
Framework presetsfastapi extends python-platform for API layouts and Alembic. django extends it for Django layouts and migrations. rag extends it for retrieval, prompt, eval, and MCP layouts. worker extends it for task, job, schedule, and queue layouts
Notes are declarationsThe rollback, retry, and fallback subjects must be non-empty and specific. Retornatus does not judge whether the plan would work or whether a job is really idempotent

What did not come over from Spec Guardrails

Spec Guardrails stored Ship Surface and AI Surface as sections in design.md and gated them with validate_ship_surface.py. Appendix D (RAG / MCP / eval) requires an eval harness field and a fallback/degrade field in that file. Retornatus keeps the path lists and the honest limits, and turns them into executed Evidence plus a note. There is no design.md checklist to satisfy. verify still does not grade the eval.

← Docs hub Product site →