docs(architecture): add routes_health, logging_config, /healthz to module + route maps

claude-code 2026-04-19 12:32:09 -06:00
parent 8f3bd78a14
commit dd3ffd3453

@ -2,7 +2,9 @@
Small FastAPI app rendering Jinja2 templates with HTMX-driven partial
updates. SQLite is the only storage. SQLAlchemy 2.x is the ORM; Alembic
manages schema. `uv` manages Python deps.
manages schema. `uv` manages Python deps. Structured JSON logging to
stdout is the runtime-observability contract; see
[Operations](Operations).
## Modules
@ -11,12 +13,15 @@ src/quartermaster/
main.py FastAPI app factory
routes.py Budget configuration HTTP handlers
routes_month.py Monthly view HTTP handlers
routes_health.py GET /healthz (unauthenticated, separate router)
service.py Budget queries, totals, target, zero-amount, group views
month_service.py Month snapshot, deviation, per-month CRUD, lifecycle, group views
groups.py Group enum, labels, section->group mapping, default open
models.py SQLAlchemy models, Section enum, MonthState enum
db.py Engine, session, PRAGMA foreign_keys=ON
config.py DB URL resolution
logging_config.py Loads LOG_CONFIG from logconfig.json; AccessLogFilter
logconfig.json dictConfig source-of-truth (tests + uvicorn both consume)
templates/ Jinja2 templates (base, index, month, partials)
static/
app.css Full stylesheet (Barlow Condensed + ledger layout)
@ -195,7 +200,7 @@ Colour tone:
GET / index page
POST /sections/{section}/entries add entry
DELETE /entries/{entry_id} remove entry
POST /entries/{entry_id}/notes update entry notes
POST /entries/{entry_id}/save update entry (name, amount, notes)
POST /debt-target set / clear debt target
```
@ -229,6 +234,18 @@ All mutation routes return the section partial plus OOB swaps for:
OOB is used so section-level changes keep the page-level summary widgets
accurate without a reload.
### Health (`src/quartermaster/routes_health.py`)
```
GET /healthz liveness + DB reachability (unauthenticated)
```
Separate router, zero app-level dependencies. Success: 200
`{"status":"ok"}`. Failure: 503
`{"status":"error","detail":"<exception-class-name>"}` — class name
only, no message or traceback leaked. A failed probe emits a
structured warning log with `event=healthz_failed`, `error_class=<cls>`.
## HTMX conventions
* Month entry rows carry inline inputs for `name` and `planned` plus a
@ -252,6 +269,23 @@ accurate without a reload.
and the server returns 204 + `HX-Redirect: /month/{year_month}` so the
page re-renders cleanly in the new state.
## Observability
See [Operations](Operations) for the Logs and Health sections. Brief
summary:
* `src/quartermaster/logconfig.json` is the single source of truth for
the `logging.config.dictConfig`. Loaded into `LOG_CONFIG` in
`logging_config.py` at import time; same file read by uvicorn CLI
via `--log-config`.
* Formatter: `pythonjsonlogger.json.JsonFormatter` with `rename_fields`
mapping `asctime/levelname/name``timestamp/level/logger`.
* `AccessLogFilter` in `logging_config.py` enriches uvicorn access
records with `event="http_request"`, `method`, `path`, `status`,
`client_ip`.
* Five seed app events at the main mutation sites plus one
`healthz_failed` event at the probe.
## Visual identity
* **Typography**: Barlow Condensed (300-800 + italic) imported from
@ -276,7 +310,7 @@ accurate without a reload.
5-column summary grid (caret / name / planned / applied / actions).
The 2px progress bar rides the summary's bottom border.
* Budget entry rows use `table.entries` with a 4-column grid.
* `.target-section` with a burgundy left bar and `↳` margin glyph
* `.target-section` with a burgundy left bar and margin glyph
* `.state-badge` tracked-caps label with bullet separators
## Testing
@ -286,4 +320,11 @@ accurate without a reload.
share the same in-memory engine across threads via `StaticPool`.
* Backup script tests shell out and assert on filenames and sqlite
round-trips.
* Full suite runs in under 4 seconds.
* Logging tests instantiate the JSON formatter from `LOG_CONFIG`
directly, push a record through a `StringIO` handler, assert on the
parsed output. `AccessLogFilter` is tested with a synthetic uvicorn
`LogRecord`. Seed events are tested via `caplog`. The dictConfig
smoke test saves / restores logger state in a try/finally so it
cannot leak `propagate=False` to subsequent tests.
* `/healthz` is tested with and without a failing session.
* Full suite (148 tests) runs in under 6 seconds.