diff --git a/.cursor/rules/main.mdc b/.cursor/rules/main.mdc new file mode 100644 index 0000000..408d6b8 --- /dev/null +++ b/.cursor/rules/main.mdc @@ -0,0 +1,8 @@ +--- +description: SwimBlocks deck-eval-parser — agent guide pointer +alwaysApply: true +--- + +The full repo guide is in [AGENTS.md](../../AGENTS.md). It points at the canonical org-wide +rules in [swimblocks/.github](https://github.com/swimblocks/.github). Follow the work-loop in +the canonical AGENTS.md (issue → branch → PR → hand off; agents do not self-merge). diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 0000000..a43101c --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,5 @@ +# GitHub Copilot instructions + +The full agent / contributor guide for this repo lives in [AGENTS.md](../AGENTS.md). Read it +before suggesting changes. It points at the org-wide canonical rules in +[swimblocks/.github](https://github.com/swimblocks/.github). diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..4be1cba --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,62 @@ +# Agent guide — deck-eval-parser + +Parses Canadian swimming on-deck evaluation PDFs into structured data via per-province +templates. Handles both **fillable PDFs** (form widgets, deterministic) and **scanned PDFs** +(via a local Ollama vision model). Both paths converge on the same `PageExtraction` shape, +so the merge → JSON/CSV/XLSX pipeline downstream is path-agnostic. + +## Canonical rules + +The cross-repo rules for any SwimBlocks project live in the [`swimblocks/.github`](https://github.com/swimblocks/.github) +standards repo. Read these first: + +- [AGENTS.md](https://github.com/swimblocks/.github/blob/main/AGENTS.md) — distilled agent guide +- [CONTRIBUTING.md](https://github.com/swimblocks/.github/blob/main/CONTRIBUTING.md) — + long-form house rules +- [development.md](https://github.com/swimblocks/.github/blob/main/docs/development.md) — + setting up a new machine + +Everything below is **repo-specific** — quirks that the canonical guide doesn't cover. + +## Repo-specific quirks + +- **Two parse paths, one shape.** Fillable PDFs go through `src/pdf_io.read_widgets` + + `src/form_extract.extract_page`. Scanned PDFs get rasterized in `src/pdf_io.rasterize_page` + and handed to the vision model in `src/vision_extract.py`. Both emit `PageExtraction` + (`meet`/`session`/`rows` dicts of `FieldValue`). Never blend the paths within a single + PDF — if any page has widgets, the whole document goes through the form-field path. + Background: [docs/architecture.md](docs/architecture.md), [docs/pdf-parsing.md](docs/pdf-parsing.md). +- **Province templates are a registry.** Each province lives in `src/templates/.py` and + registers via `src/templates/__init__.py`. Today Swim Ontario is implemented; Quebec / + Alberta / BC and others are stubs that raise `NotImplementedError`. **Don't generalise** a + template hack into shared code — provincial differences are real and load-bearing. See + [docs/templates/](docs/templates/) and the per-province issues + ([#20–#28](https://github.com/swimblocks/deck-eval-parser/issues?q=is%3Aissue+is%3Aopen+label%3Atemplate)). +- **Canonical field names** live in `src/schema.py` (`MEET_FIELDS`, `SESSION_FIELDS`, + `ROW_FIELDS`, `ALL_FIELDS`). Every module imports from there; **don't sprinkle string + literals** for field names. +- **PyMuPDF widget naming has gotchas.** `[NNN]` disambiguator suffixes get stripped in + `read_widgets`; the widget name in each template's `widget_field_map` is the + post-suffix-strip form. See [docs/pdf-parsing.md](docs/pdf-parsing.md) before touching + field maps. +- **Ollama is a runtime dependency.** `src/ollama_runtime.py` manages the lifecycle (auto-pull + models, start/stop). GPU tier is detected in `src/gpu_detect.py` and influences which + vision model is chosen. Don't hand-call `ollama` from elsewhere; route through the runtime. +- **Synthetic fixtures, not real PDFs.** Real eval forms contain officials' names and emails + (PII). Test fixtures under `tests/fixtures/` are generated by + `tests/fixtures/form_field/make_synthetic_fixture.py` and friends. **Never commit real + scanned forms** as test fixtures — see issue [#29](https://github.com/swimblocks/deck-eval-parser/issues/29) + for the pseudonymisation pipeline. +- **No `pyproject.toml` for ruff yet** in this repo — lint config lives in `pytest.ini` and + ruff defaults. If you add ruff config, mirror the swimblocks/.github CI workflow's + expectations. + +## Where to start reading + +- [`README.md`](README.md) — user-facing overview + quick-start +- [`docs/architecture.md`](docs/architecture.md) — current state, end-to-end +- [`docs/pdf-parsing.md`](docs/pdf-parsing.md) — PyMuPDF / widget specifics +- [`docs/templates/README.md`](docs/templates/README.md) — how to add a provincial template +- [`docs/design/`](docs/design/) — design docs (the *why*) +- [`src/schema.py`](src/schema.py) — canonical field constants (shared vocabulary) +- [`main.py`](main.py) — CLI entry point diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..ff2c9c5 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,5 @@ +# Claude Code — deck-eval-parser + +Repo-specific guide and canonical org-wide rules: + +@AGENTS.md