diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 91e70e617..4c96fba08 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -8,6 +8,16 @@ updates: time: "09:00" timezone: "Australia/Perth" open-pull-requests-limit: 5 + # Batch low-risk updates so a solo maintainer reviews a couple of grouped PRs + # instead of up to ~5/week each triggering full CI. Majors stay individual — + # each major deserves its own review (per the AGENTS.md dependency protocol). + groups: + npm-production: + dependency-type: "production" + update-types: ["minor", "patch"] + npm-development: + dependency-type: "development" + update-types: ["minor", "patch"] - package-ecosystem: "github-actions" directory: "/" @@ -17,3 +27,9 @@ updates: time: "09:00" timezone: "Australia/Perth" open-pull-requests-limit: 5 + # One batched PR for all action SHA bumps keeps pinned versions uniform across + # workflows/composites (mitigates the action-version-skew finding) and keeps + # check:github-actions green in a single review. + groups: + github-actions: + patterns: ["*"] diff --git a/docs/README.md b/docs/README.md index 9a12c772d..27bbd73d1 100644 --- a/docs/README.md +++ b/docs/README.md @@ -18,6 +18,7 @@ npm run docs:check-links | [codebase-index.md](codebase-index.md) | Structured architecture map: layout, module map, Supabase schema, scripts, domain concepts | | [site-map.md](site-map.md) | **Generated** route map — regenerate with `npm run sitemap:update`, verify with `npm run sitemap:check` | | [agents-guide.md](agents-guide.md) | Human onboarding pointer; authoritative agent rules live in the root `AGENTS.md` | +| [scripts-index.md](scripts-index.md) | Curated map of `scripts/` and the `package.json` command surface by purpose | ## Architecture @@ -69,6 +70,7 @@ npm run docs:check-links ## Plans and workstreams (living) +- [maturity-backlog-workorders.md](maturity-backlog-workorders.md) — actionable work orders tracking the repository-maturity audit backlog - [framework-dependency-modernization-checklist.md](framework-dependency-modernization-checklist.md) — ordered Next.js 16, runtime, dependency, Turbopack, and verification migration program - [search-rag-master-plan.md](search-rag-master-plan.md) / [search-rag-master-context.md](search-rag-master-context.md) — search/RAG roadmap and shared context - [rag-hybrid-findings-and-todo.md](rag-hybrid-findings-and-todo.md) — hybrid retrieval findings backlog diff --git a/docs/agents-guide.md b/docs/agents-guide.md index a2e2e2271..bf745bf55 100644 --- a/docs/agents-guide.md +++ b/docs/agents-guide.md @@ -31,3 +31,22 @@ duplicate those rules, so it cannot drift from them. `npm run verify:pr-local` when the change is PR-ready (see [`docs/process-hardening.md`](process-hardening.md) for the full verification pyramid). + +## AI tooling map + +This repo intentionally uses several AI systems; the overlap is by design, not +accident. [`AGENTS.md`](../AGENTS.md) is the single source of truth — every system +below defers to it, so rules live in one place and cannot drift. + +| System | Owns | Where it is configured | +| ------------------------- | ---------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | +| **AGENTS.md** (canonical) | All agent rules, gates, safety boundaries | `AGENTS.md`; `CLAUDE.md` is a one-line `@AGENTS.md` import | +| **Codex** (OpenAI) | Primary PR code-review + automatic resolve | AGENTS.md "Codex review" sections, `docs/codex-review-protocol.md`, `docs/codex-prompt-playbook.md`, `.github/workflows/codex-autofix-review-comments.yml` | +| **Claude Code** | Interactive dev; scoped review subagents + workflow skills | `.claude/` (agents, skills, hooks), `.github/workflows/claude.yml` | +| **Cursor** | DB inspection via Supabase MCP; editor skills | `.cursor/` (skills, `mcp.json`) | +| **CodeRabbit** | Advisory PR review (never blocking) | `.coderabbit.yaml` (`commit_status: false`) | +| **`.agents/`** | Home-grown single-word skill catalogue | `.agents/skills/catalog.json`; list with `npm run skills` | + +Rule of thumb: change agent behaviour in `AGENTS.md`, then let each system inherit it. +Do not add a new AI system or grow the skill count without retiring something — the +breadth is already a maintenance cost for a single maintainer. diff --git a/docs/design-system.md b/docs/design-system.md index 184bc3484..343052eba 100644 --- a/docs/design-system.md +++ b/docs/design-system.md @@ -134,6 +134,11 @@ rung — never a new number. ## 7. Accessibility requirements +**Target: WCAG 2.2 AA.** The rules below are the concrete, enforced floor for meeting it; partial +automated coverage is provided by `tests/ui-accessibility.spec.ts` (reduced-motion, forced-colors, +focus, labels, selected axe-core checks) for the areas it covers, rather than enforcing the full +WCAG 2.2 AA standard, with manual checks for the rest. + - Every interactive element has a visible focus state: the global `:focus-visible` rule is the floor; use the `focusRing` recipe on custom controls. - Dialogs/popovers: use `Sheet` (focus handling is free). If something genuinely can't use it, diff --git a/docs/maturity-backlog-workorders.md b/docs/maturity-backlog-workorders.md new file mode 100644 index 000000000..981f9c1ee --- /dev/null +++ b/docs/maturity-backlog-workorders.md @@ -0,0 +1,192 @@ +# Maturity backlog — work orders + +Living tracker that turns the deferred backlog from +[`docs/audit/2026-07-20-repository-maturity.md`](audit/2026-07-20-repository-maturity.md) §10 +into actionable, sequenced work orders. Each item states its **outcome**, **approach**, **key +files**, **risk**, **verification**, and **status**. High-risk items are deliberately kept as +their own work order — the audit's rule is one dedicated PR + full-suite verification per +structural change, not a single mixed PR. + +**Status legend:** `DONE` (landed) · `READY` (scoped, safe to start) · `OPEN` (needs a +decision or a dedicated PR) · `PROVIDER-GATED` (touches live DB/CI/provider — needs explicit +confirmation) · `SATISFIED` (already true in the repo; no work needed). + +--- + +## Now — foundational, low-risk + +### N1 · Dependabot grouping — `DONE` + +- **Outcome:** fewer, batched dependency PRs instead of up to ~10/week for a solo maintainer. +- **Approach:** group npm minor/patch by dependency-type (production/development) and group all + github-actions bumps; majors stay individual. +- **Files:** `.github/dependabot.yml`. +- **Risk:** none (config only). +- **Verification:** valid YAML; takes effect on the next Monday cadence. +- **Landed in:** this PR. + +### N2 · Dependency-report workflow decision — `OPEN` + +- **Outcome:** either an active fortnightly dependency report, or one less dormant workflow. +- **Approach:** `.github/workflows/dependency-report.yml` currently ships `workflow_dispatch`-only + with its `schedule:` commented out. Choose: (a) uncomment the cron + run one dispatch to + confirm the rolling issue renders, or (b) delete the workflow and rely on Dependabot alerts. +- **Risk:** low. It is report-only (`npm audit` + a rolling GitHub issue), but enabling a + scheduled workflow that writes issues is a cadence/behaviour change, so it is left to the + maintainer rather than flipped unilaterally. +- **Recommendation:** enable it (option a) — it restores intended functionality and complements + Dependabot with an outdated-direct-deps view. Left `OPEN` pending your nod. + +--- + +## Next — high-value maturity (each its own PR) + +### X1 · Import-boundary ESLint rule — `READY` (with a correction) + +- **Outcome:** two prose invariants become CI-enforced instead of review-enforced. +- **Findings from verification (2026-07-20):** + - _Mockup invariant — viable._ All 36 `*mockup*` imports under `src/app/**` are confined to + `src/app/mockups/**` (which is 404 in production). A rule forbidding mockup imports + **outside** `src/app/mockups/**` has **zero current violations**. + - _Service-role invariant — invalid as originally stated._ 30+ server API routes legitimately + import the service-role admin client (`@/lib/supabase/admin`). The real invariant — "no + service-role client in the **client bundle**" — is already enforced by + `npm run check:client-bundle-secrets` + the `server-only` marker. Do **not** add a + "only `admin.ts` may import the service-role client" rule; it would wrongly break lint. +- **Approach:** add a `no-restricted-imports` (patterns: `**/*mockup*`, `@/components/*-mockups`, + `@/components/*-mockups/*`) block in `eslint.config.mjs`, scoped with an override that ignores + `src/app/mockups/**` and the `src/components/**mockups**` sources themselves. +- **Files:** `eslint.config.mjs` (+ a short note in `docs/frontend-architecture.md`). +- **Risk:** low-medium — must not flag the legitimate `src/app/mockups/**` routes. +- **Verification:** `npm run lint` reports **0** new errors; add a deliberately-wrong import in a + scratch file to confirm the rule fires, then remove it. + +### X2 · `src/lib` domain-directory extraction — rag pilot — `OPEN` + +- **Outcome:** the first real domain directory; unblocks directory-scoped boundary rules for the + rest of `src/lib` (197 flat files). +- **Approach:** `git mv` the `rag*.ts` cluster (~23 files) into a new `rag/` directory under + `src/lib/`; codemod `@/lib/rag*` importers; update the per-file paths in + `scripts/check-maintainability-budgets.mjs`. +- **Files:** ~23 `src/lib/rag*.ts` + every importer + the budgets script + `docs/codebase-index.md`. +- **Risk:** HIGH — broad import churn; keep it isolated with no behaviour change. +- **Verification:** `npm run typecheck` && `npm run test`; diff must be pure moves + import-path + rewrites (no logic changes). + +### X3 · Decompose the monoliths — `OPEN` + +- **Outcome:** shrink the three files the maintainability ratchet caps but never reduces: + `src/lib/rag.ts` (5,143), `src/components/ClinicalDashboard.tsx` (4,270), + `src/components/DocumentViewer.tsx` (3,166). +- **Approach:** extract cohesive units behind the existing budgets; `rag.ts` is the natural seam + once X2 lands (its ~23 siblings already exist). +- **Risk:** HIGH (behavioural surface). One file per PR. +- **Verification:** `npm run typecheck` + `npm run test` (+ `npm run verify:ui` for the components). + +### X4 · SAST-blocking on the parser/ingestion path — `PROVIDER-GATED (triage-first)` + +- **Outcome:** Semgrep ERROR findings block on the untrusted-PDF surface, not just advise. +- **Approach:** add a path-scoped Semgrep job (targets `worker/**`, `src/lib/ingestion*`, + `src/lib/extractors/**`, ingestion API routes) **without** `continue-on-error`. Triage the + current advisory findings on those paths first so the gate starts green. +- **Files:** `.github/workflows/sast.yml`. +- **Risk:** MEDIUM — could block on pre-existing findings if not triaged first. +- **Verification:** a Semgrep run over the scoped paths reports zero ERROR before flipping the gate. + +### X5 · ACL-migration consolidation review — `PROVIDER-GATED (DB owner)` + +- **Outcome:** a settled grant model instead of repeated `repair/reassert/enforce` migrations + (the 2026-07-19 cluster). +- **Approach:** DB-owner review of the repeated privilege-repair migrations; replace with a single + canonical grant migration if the churn reflects an unsettled policy rather than genuine drift. +- **Files:** `supabase/migrations/*` (privilege-repair set), `supabase/schema.sql`, `docs/database-drift-detection.md`. +- **Risk:** HIGH (schema/live-DB semantics). +- **Verification:** `npm run check:drift` + the CI `db-reset-verify` replay; **live-DB work is + confirmation-required** per the AGENTS.md provider boundary. + +### X6 · Raise coverage floors for clinical domains — `OPEN` + +- **Outcome:** higher targeted thresholds where correctness matters most (clinical-safety, + retrieval, answer) than the current global 38–50%. +- **Approach:** add per-path coverage thresholds in `vitest.config.mts`; add the targeted tests + needed to clear them (ratchet up, don't drop the bar retroactively). +- **Files:** `vitest.config.mts` (+ new `tests/*` specs). +- **Risk:** MEDIUM — needs real tests, not just a threshold bump. +- **Verification:** `npm run test:coverage` meets the new per-path floors. + +--- + +## Later — useful, non-essential + +### L1 · Archive one-shot scripts — `OPEN` + +- **Outcome:** `scripts/` shows live tooling, not historical residue. +- **Approach:** once their migrations have shipped, move one-shots (`check:m13-migration`, + `check:july8-live-batch`, completed dated `backfill:*`) to an `archive/` subfolder under + `scripts/` and drop their `package.json` entries. Verify each migration is live before removing its checker. +- **Risk:** low (but confirm each is truly retired). **Companion (done):** the discoverability half + of this item — a curated script map — ships now as + [`docs/scripts-index.md`](scripts-index.md). + +### L2 · Single-SHA-per-action uniformity — `OPEN` + +- **Outcome:** every third-party action pinned to one SHA across all workflow + composite files. +- **Approach:** extend `scripts/check-github-action-pins.mjs` (it already parses every workflow) + to assert one SHA per action name. Partly mitigated already by N1 (grouped action bumps land + together). +- **Files:** `scripts/check-github-action-pins.mjs`. +- **Verification:** `npm run check:github-actions`. + +### L3 · Single gate manifest — `OPEN` + +- **Outcome:** `verify:cheap:internal` and CI's `static-pr` step list derive from one source so a + gate can't be added to one and missed in the other. +- **Approach:** a small JS array of gate script names that `verify:cheap` iterates and a CI + self-test asserts against `ci.yml`. +- **Files:** `package.json`, `.github/workflows/ci.yml`, a new gate-manifest script under `scripts/`. +- **Verification:** the self-test fails if the two lists diverge. + +### L4 · Rotate the branch-review ledger — `OPEN` + +- **Outcome:** `docs/branch-review-ledger.md` (361 KB, append-only) stays navigable. +- **Approach:** archive entries older than a quarter into `docs/archive/branch-review-ledger-.md`; + keep the live ledger to the current quarter. +- **Risk:** low. + +### L5 · Documentation quick-wins — `DONE` / `SATISFIED` + +- **AI tooling map** — `DONE` this PR: which of the five agent systems owns which job, in + `docs/agents-guide.md`. +- **WCAG target** — `DONE` this PR: WCAG 2.2 AA named as the target in `docs/design-system.md` §7. +- **RPO/RTO** — `SATISFIED`: already documented in + [`docs/disaster-recovery-runbook.md`](disaster-recovery-runbook.md) (the recovery-layers table), + so the audit's "record RPO/RTO" item needs no new work. + +--- + +## Not recommended (disproportionate for this repo) + +A formal architecture-docs folder + numbered-ADR process · `CONTRIBUTING.md` / `CODE_OF_CONDUCT.md` / +`CHANGELOG.md` · microservice split · a second dependency bot · a new observability vendor · a +published OpenAPI/GraphQL contract (single internal client). Revisit the governance files only if +collaborators join — `AGENTS.md` + the PR template already carry that load. + +--- + +## Progress summary + +| Item | Priority | Status | +| ------------------------------ | -------- | ---------------------------------------------- | +| N1 Dependabot grouping | Now | **DONE** (this PR) | +| N2 Dependency-report decision | Now | OPEN (recommend enable) | +| X1 Import-boundary linter | Next | READY (mockup rule; service-role rule dropped) | +| X2 `src/lib` rag extraction | Next | OPEN (isolated PR) | +| X3 Monolith decomposition | Next | OPEN | +| X4 SAST-blocking on parser | Next | PROVIDER-GATED (triage-first) | +| X5 ACL-migration consolidation | Next | PROVIDER-GATED (DB owner) | +| X6 Coverage floors | Next | OPEN | +| L1 Archive one-shot scripts | Later | OPEN (index shipped) | +| L2 Action-SHA uniformity | Later | OPEN (mitigated by N1) | +| L3 Single gate manifest | Later | OPEN | +| L4 Ledger rotation | Later | OPEN | +| L5 AI map / WCAG / RPO-RTO | Later | **DONE / SATISFIED** | diff --git a/docs/scripts-index.md b/docs/scripts-index.md new file mode 100644 index 000000000..0652c8337 --- /dev/null +++ b/docs/scripts-index.md @@ -0,0 +1,87 @@ +# Scripts index + +Curated map of `scripts/` (~135 files) and the `package.json` script surface (~166 entries), +grouped by purpose. This is orientation, not an exhaustive per-file listing — the authoritative +command list is `package.json`, and `npm run docs:check-scripts` verifies every `npm run ` +referenced in docs resolves to a real script. + +Legend: **[live]** routine tooling · **[infra]** runner/guard plumbing · **[one-shot]** completed +migration/batch helper that is a candidate for an `archive/` subfolder under `scripts/` once its +migration has shipped (see `docs/maturity-backlog-workorders.md` L1). + +## Runner & guard infrastructure [infra] + +| Script | Role | +| ------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------- | +| `run-heavy.mjs` | Serializes heavy jobs behind a cross-process lock (`test-run-lock.mjs`) so builds/tests don't oversubscribe | +| `run-tsx.mjs`, `run-vitest.mjs`, `run-playwright.mjs`, `run-eval-safe.mjs` | Typed/test/e2e/eval entrypoint wrappers | +| `dev-free-port.mjs`, `ensure-local-server.mjs` | Project-stable localhost port selection + background server ensure | +| `check-node-engine.cjs`, `install-git-hooks.mjs`, `guard-push.mjs`, `guard-next-build.mjs` | Install/preflight guards | +| `ci-change-scope.mjs`, `ci-triage.mjs`, `pr-policy.mjs` | CI change classification + PR policy (self-tested via `check:ci-scope`/`check:ci-triage`/`check:pr-policy`) | +| `child-process-result.mjs`, `cli-utils.ts`, `productivity-core.mjs` | Shared helpers | + +## Verification gates [live] + +`verify:cheap` → `verify:pr-local` → `verify:ui` → `verify:release`. Building blocks: +`check-runtime.ts`, `check-github-action-pins.mjs`, `check-maintainability-budgets.mjs`, +`check-codebase-index-coverage.mjs`, `check-docs-links.mjs`, `check-docs-script-refs.mjs`, +`check-bundle-budget.mjs`, `check-type-scale.mjs`, `check-icon-scale.mjs`, +`check-design-system-contract.mjs`, `check-function-grants.mjs`, `check-owner-scope-api.mjs`, +`check-client-bundle-secrets.mjs`, `verify-pr-local.mjs`, `verify-release-offline.mjs`. + +## Ingestion, indexing & reindex [live] + +`import-documents.ts`, `reindex.ts`, `reindex-health.ts`, `check-indexing.ts`, +`recover-ingestion-queue.ts`, `cleanup-abandoned-reindex-generations.ts`, +`ingestion-autopilot.ts`, `backfill-smart-index.ts`. + +## Document intelligence & governance [live] + +`enrich-documents.ts`, `classify-documents.ts`, `backfill-gold-document-labels.ts`, +`audit-source-governance.ts`, `check-document-label-coverage.ts`, +`check-document-label-governance.ts`, `promote-public-documents-batch.ts`, +`audit-public-document-approvals.ts`, `production-readiness.ts`, `check-supabase-project.ts`, +`check-default-acl.ts`, `check-drift.ts`, `generate-drift-manifest.ts`, +`check-migration-history-alignment.ts`. + +## RAG evaluation [live] + +`eval-rag.ts`, `eval-rag-offline.mjs`, `eval-retrieval.ts`, `eval-quality.ts`, +`eval-answer-quality.ts`, `eval-search.ts`, `eval-search-api.ts`, `eval-assertions.ts`, +`compare-retrieval-eval.ts`, `retrieval-health.ts`, `profile-retrieval-rpcs.ts`, +`warm-retrieval-cache.ts`, `tune-search-weights.ts`, `check-rag-fixtures.mjs`. Golden fixtures: +`scripts/fixtures/rag-retrieval-golden.json`, `scripts/fixtures/assertion-golden.json`. + +## Registry / catalogue content [live] + +`seed-registry-records.ts`, `embed-registry-records.ts`, `reconcile-registry-governance.ts`, +`import-services-export.ts`, `import-differentials-export.ts`, `seed-differential-records.ts`, +`import-medications-export.ts`, `seed-medication-records.ts`. + +## Build & assets [live/infra] + +`build-worker.mjs`, `build-analyze.mjs`, `build-therapies-index.mjs`, `build-ranking-snapshot.ts`, +`generate-site-map.ts`, `generate-brand-assets.ts`, `generate-sample-documents.ts`, +`check-sample-extraction.ts`. + +## Maintenance & ops [live] + +`cleanup-storage.ts`, `purge-query-logs.ts`, `audit-tables.ts`, `supabase-recovery-status.ts`, +`promote-query-misses.ts`, `flake-ledger.mjs`, `sweep-branch-ledger.mjs`, `dependency-report.mjs`, +`set-site-administrator.ts`. + +## One-shot / dated — archive candidates [one-shot] + +Completed migration/batch helpers kept only for provenance; retire to an `archive/` subfolder under +`scripts/` once the underlying migration is confirmed live (work order L1): + +`check-m13-migration.ts`, `check-july8-live-batch.ts`, `check-retrieval-owner-migration.ts`, +`backfill-source-metadata.ts`, `backfill-text-normalization.ts`, `backfill-visual-intelligence.ts`, +`backfill-document-tags.ts`, `backfill-enrichment.ts`, `derive-unknown-status.ts`, +`reindex-image-generation-metadata.ts`, `measure-wrapped-dose-prevalence.ts`. + +## Workflow planners [infra] + +`external-workflow.mjs` (`workflow:run/status/verify/deps/clean-state/export/handoff`) and +`productivity-workflow.mjs` (`workflow:flightplan/triage/clinical-proof/design-sweep/rag-lab/ +operator-closeout/lifecycle`) — see `docs/productivity-workflows.md`.