CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 12s
CI & Build / TypeScript typecheck (push) Successful in 52s
CI & Build / integration (push) Successful in 1m1s
CI & Build / Python tests (push) Successful in 1m55s
CI & Build / Build & push image (push) Successful in 36s
Step 1 of the Scribe -> Grimoire rename: every capitalised "Scribe" in prose
becomes "Grimoire" ("Fabled Scribe" -> "Fabled Grimoire") across the UI,
the PWA manifest and page title, the MCP instructions and tool docstrings,
hook and skill text, docs and README, with the tests that assert on that
prose moving with it. Old migrations keep their wording.
Identifiers are untouched here: the package, plugin name, MCP server key,
hook file names, env vars, the .scribe marker and FabledScribe repo/image
names move in steps 2 and 3, along with prose that quotes them.
The sweep matched "Scribe" only as a whole word, so describe/subscribe and
friends are untouched. The longer name took the MCP instructions one
character over their 1,600 budget; two phrases are shortened to fit.
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 `scribe.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.
|