docs(dev): update Run block, project layout, conventions for logging + /healthz
parent
dd3ffd3453
commit
cbd2e11a64
1 changed files with 76 additions and 38 deletions
|
|
@ -22,11 +22,21 @@ file elsewhere.
|
|||
## Run the app
|
||||
|
||||
```sh
|
||||
uv run uvicorn quartermaster.main:app --reload
|
||||
uv run uvicorn quartermaster.main:app \
|
||||
--log-config src/quartermaster/logconfig.json \
|
||||
--reload
|
||||
```
|
||||
|
||||
Open http://127.0.0.1:8000.
|
||||
|
||||
The `--log-config` flag emits structured JSON to stdout (Loki-ready
|
||||
when deployed). Omit it if you'd rather read logs in uvicorn's default
|
||||
human-readable format during local dev; production must use the config.
|
||||
|
||||
Health probe (unauthenticated): `curl http://127.0.0.1:8000/healthz`
|
||||
returns `{"status":"ok"}` on a healthy DB, 503 with the exception
|
||||
class name in `detail` otherwise.
|
||||
|
||||
## Run tests
|
||||
|
||||
```sh
|
||||
|
|
@ -34,7 +44,7 @@ uv run pytest
|
|||
```
|
||||
|
||||
Tests use an in-memory SQLite engine; no separate migration step
|
||||
needed. Full suite runs in under 4 seconds.
|
||||
needed. Full suite runs in under 6 seconds.
|
||||
|
||||
## Developing against a throwaway DB
|
||||
|
||||
|
|
@ -62,12 +72,15 @@ src/quartermaster/
|
|||
main.py FastAPI app factory
|
||||
routes.py Budget configuration handlers
|
||||
routes_month.py Monthly view handlers + lifecycle transitions
|
||||
routes_health.py GET /healthz (separate router, unauthenticated)
|
||||
service.py Budget service: entries, target, zero-amount, groups
|
||||
month_service.py Month service: snapshot, deviation, lifecycle, groups
|
||||
month_service.py Month service: snapshot, deviation, lifecycle, groups, postings
|
||||
groups.py Group enum, labels, section->group mapping
|
||||
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 for JSON stdout logs
|
||||
templates/
|
||||
base.html Barlow Condensed imports + favicon
|
||||
index.html Budget config page
|
||||
|
|
@ -100,9 +113,14 @@ tests/
|
|||
test_groups.py Group mapping + subtotals + default-open
|
||||
test_notes.py Notes field + snapshot copy
|
||||
test_month_lifecycle.py Transitions + close gate + edit locking
|
||||
test_postings.py Posting CRUD + derived applied
|
||||
test_template_edit_isolation.py Template edit doesn't leak into snapshots
|
||||
test_backup_script.py Backup script shell behaviour
|
||||
test_logging.py JSON formatter, access filter, dictConfig, seed events
|
||||
test_health.py /healthz 200 success + 503 on DB failure
|
||||
CLAUDE.md DB safety rule, repo-level instructions
|
||||
docs/
|
||||
superpowers/ Specs and plans (per-session design notes)
|
||||
wiki/ Cloned wiki (gitignored)
|
||||
mockups/ Design history + mockup HTML
|
||||
```
|
||||
|
|
@ -124,8 +142,10 @@ chore/<issue>-<slug> tooling, deps, docs
|
|||
|
||||
Each commit is one logical change. Typical PRs in this repo have 3-6
|
||||
commits grouped by layer (data model, service, routes, templates,
|
||||
tests, docs). Commit subjects under 72 chars, body under 80, trailers
|
||||
include `Refs #<issue>` and the Claude co-author line.
|
||||
tests, docs), or more for TDD-driven feature work where each task
|
||||
gets its own failing-test / implementation / commit triple. Commit
|
||||
subjects use conventional-commits prefixes (`feat:`, `fix:`, `chore:`,
|
||||
`test:`, `docs:`, `refactor:`), under 72 chars. Body under 80.
|
||||
|
||||
### Merge flow
|
||||
|
||||
|
|
@ -138,6 +158,9 @@ PRs merge via the Forgejo API (or the web UI merge button), NOT
|
|||
4. Close the tracking issue manually via the API (the `Closes #N`
|
||||
keyword is unreliable on this Forgejo instance).
|
||||
|
||||
Exception: purely local direct-to-main merges (rare) rebase onto
|
||||
origin/main first to keep linear history.
|
||||
|
||||
### Adding a schema change
|
||||
|
||||
1. Edit `src/quartermaster/models.py`.
|
||||
|
|
@ -161,6 +184,21 @@ affects the HTML, also:
|
|||
4. Claude cannot run a real browser from the CLI; the human eyeball is
|
||||
the final check before merge.
|
||||
|
||||
### Adding a structured log event
|
||||
|
||||
1. Pick a namespaced module logger if one doesn't exist in the file
|
||||
you're touching: `logger = logging.getLogger("quartermaster.<module>")`
|
||||
placed after all imports.
|
||||
2. Use `logger.info` (or `.warning`/`.error`) with `extra={"event": "...",
|
||||
...additional_fields}`. The `event` name should be a concise
|
||||
snake_case verb_noun.
|
||||
3. Place the log after the commit / refresh in service-layer functions,
|
||||
before `return`, so it only fires on durable success.
|
||||
4. Write a test using `caplog` that asserts a record with the expected
|
||||
`event` attribute is emitted. See `tests/test_logging.py` for the
|
||||
pattern.
|
||||
5. Document the new event in the table in [Operations](Operations).
|
||||
|
||||
### Design workflow
|
||||
|
||||
UI experiments live under `docs/mockups/` as self-contained HTML files
|
||||
|
|
|
|||
Loading…
Reference in a new issue