CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 12s
CI & Build / TypeScript typecheck (push) Successful in 56s
CI & Build / integration (push) Successful in 1m4s
CI & Build / Python tests (push) Successful in 1m55s
CI & Build / Build & push image (push) Successful in 44s
Step 2 of the Scribe -> Grimoire rename. - src/scribe -> src/grimoire, and every import, patch target, module path, hypercorn entry point and `python -m grimoire.restore` with it; pyproject, uv.lock and the frontend package are named grimoire. - Image git.fabledsword.com/bvandeusen/fabledgrimoire; CI's Postgres service runs as grimoire / grimoire_test. - Compose files default the DB user, database and (prod) volume to grimoire, each overridable from .env, so an install created as Scribe keeps its data by setting POSTGRES_USER, POSTGRES_DB and PGDATA_VOLUME. - Backups are written as grimoire-<ts>.tar with format grimoire-backup-tar. Archives named scribe-* with the scribe-backup-tar manifest still restore and are still counted at the destination (folder and S3), with tests. - Exports, the MCP auth realm, the ASGI scope key and the persistent logger carry the new name. Kept on purpose: the secret-sealing key label stays "scribe.secrets.v1", because every stored secret was derived under it. Plugin identity (plugin name, hooks, env vars, .scribe marker, skills) is step 3. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
88 lines
4.2 KiB
Markdown
88 lines
4.2 KiB
Markdown
# Development
|
|
|
|
## Running it
|
|
|
|
```bash
|
|
docker compose up --build # app + PostgreSQL at http://localhost:5000
|
|
docker compose up --build app # rebuild after a change
|
|
docker compose down -v # stop and wipe the database
|
|
```
|
|
|
|
The image builds the frontend, so a frontend change needs a rebuild too. For quicker frontend work, run Vite against the running app; it proxies `/api` to port 5000:
|
|
|
|
```bash
|
|
cd frontend && npm ci && npm run dev # http://localhost:5173
|
|
```
|
|
|
|
## Checks
|
|
|
|
CI runs these on every push; locally they need `uv sync --extra dev` and `cd frontend && npm ci`.
|
|
|
|
```bash
|
|
make lint # ruff check src/
|
|
make typecheck # vue-tsc --noEmit
|
|
make test # pytest tests/
|
|
make check # all three
|
|
```
|
|
|
|
Unit tests run without a database. Tests marked `integration` need a real PostgreSQL and run in CI's integration job, against a schema built by the real migrations.
|
|
|
|
## Database migrations
|
|
|
|
The container runs `alembic upgrade head` on every start, after waiting for the database to accept connections.
|
|
|
|
```bash
|
|
docker compose exec app alembic revision -m "short_description"
|
|
```
|
|
|
|
- Number revisions sequentially (`0123_...`). Revision ids must fit in 32 characters, the width of `alembic_version.version_num`.
|
|
- Write both `upgrade()` and `downgrade()`.
|
|
- Adding a value to a `CHECK`-constrained column means dropping and re-adding the constraint in the same migration.
|
|
- Keep migrations to schema changes. Filling a derived table from a large one belongs in a background backfill after startup, where its cost doesn't hold up the boot.
|
|
- A migration that depends on application code (a key derivation, a serialiser) copies what it needs rather than importing it, so a later edit can't change what an old migration does.
|
|
|
|
## The plugin
|
|
|
|
`plugin/` ships straight from git: installs fetch it through `.claude-plugin/marketplace.json`, and there is no build step. After changing anything under `plugin/` or `.claude-plugin/`, run `make mint-plugin` before committing. Claude Code only refreshes an installed plugin when its version changes, and CI fails a plugin change that didn't bump it.
|
|
|
|
The hooks must run on a bare machine: POSIX shell, awk, sed and the usual coreutils, plus `git` and `curl`. They fail open, so a slow or unreachable Grimoire never blocks Claude Code.
|
|
|
|
## CI and releases
|
|
|
|
CI runs on Gitea Actions (workflow in `.forgejo/workflows/ci.yml`) in the shared `ci-python:3.14` image ([`ci-requirements.md`](../ci-requirements.md) lists what it relies on). Six jobs: TypeScript typecheck, plugin hooks, Python lint (plus design-token and dangling-style checks), unit tests, integration tests, and the image build, which waits on all of them except the plugin job.
|
|
|
|
| Push | Image tags |
|
|
|------|-----------|
|
|
| `dev` | `:dev`, `:<sha>` |
|
|
| `main` | `:latest`, `:<sha>` |
|
|
| a `v*` tag | `:latest`, `:<version>`, `:<sha>` |
|
|
|
|
`:<sha>` never moves, so it is the rollback point. Images publish to `git.fabledsword.com/bvandeusen/fabledscribe`, with a registry layer cache at `:cache`. The build needs the `REGISTRY_USER` and `REGISTRY_TOKEN` (a token with `write:packages`) repository secrets.
|
|
|
|
Work happens on `dev`. A release merges `dev` into `main` with a plain merge commit; a dated `vYYYY.MM.DD.HHMM` tag, matching the version the image reports, can mark it.
|
|
|
|
## Conventions
|
|
|
|
### Backend
|
|
|
|
- Services open their own session: `async with async_session() as session:` (from `grimoire.models`).
|
|
- Routes and MCP tools stay thin; the logic lives in `services/`, shared by both.
|
|
- Access checks go through `services/access.py`, never inline ownership checks.
|
|
- Every wait on something outside the process has a deadline.
|
|
|
|
### Frontend
|
|
|
|
- API calls go through the typed helpers in `frontend/src/api/client.ts`.
|
|
- Pinia stores for shared state, `ref()` for state local to a component; composables for reusable behaviour.
|
|
- Colours, spacing and type come from the design system's tokens in `theme.css`, which is generated, so CI rejects an unknown `var()`.
|
|
|
|
### Commits
|
|
|
|
```
|
|
type(scope): what the change does
|
|
|
|
Why, and anything a reader tracing this change later needs.
|
|
```
|
|
|
|
Types: `feat`, `fix`, `refactor`, `docs`, `chore`, `test`. Comments in the code describe how it works today; the history of a change belongs in its commit message.
|