diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..08c80de --- /dev/null +++ b/.editorconfig @@ -0,0 +1,11 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +indent_style = space +indent_size = 4 + +[*.{yml,yaml,json}] +indent_size = 2 diff --git a/AGENTS.md b/AGENTS.md index 4e60af7..b4a1d3e 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -2,6 +2,22 @@ This repo is a Copier template. `template/` holds the generated project as `.jinja`. +## Run every gate (`just ci`) + +```bash +just ci # fmt-check + lint + typecheck + test + audit, then "ci: all gates passed" +``` + +`just ci` is the complete local gate — `ci: fmt-check lint typecheck test audit` ending `@echo "ci: all gates passed"`, with `verify` a bare alias (`verify: ci`). It **mirrors** `template/justfile.jinja`'s `ci`: the template chains `audit` when `enable_dependency_audit` is on (it is), so the faithful maintainer recipe includes it. `test` is the heavy generation matrix, so `just ci` needs a roomy `TMPDIR` — the default 4G tmpfs `/tmp` can overflow (export e.g. `TMPDIR=/path/to/roomy/dir` first). The fast inner loop is `just fmt-check lint typecheck` (one invocation, no matrix, no network); there is deliberately **no** `check:` recipe (the template ships none). `audit` is non-hermetic (it queries the OSV/PyPI advisory DB), so `just ci` reaches the network exactly as `just audit` does; scanners (`just scan`) deliberately stay **off** `ci` (CI-only), matching the template. + +This maintainer `just ci` is distinct from the **downstream's** `just ci` referenced under "Run the tests": `just test` renders each project in the answer matrix and runs *its* `just ci` — a different, generated recipe. + +CI does **not** run `just ci`. It keeps parallel per-gate jobs (`test`/`typecheck`/`lint`/`scan`), each inlining its command; only `test` fans out over the OS×Python matrix, and the `scan` job runs `uvx`/`mise exec` (not `uv run`); because every gate is enforced independently (audit via the `scan` job's `pip-audit` step), `just ci` here is the local reproduction of the PR gate, not the CI entry point. Pointing the CI jobs at these recipes (the gap-audit "candidate F") is deferred: the `scan` job aggregates pip-audit + semgrep + gitleaks as three steps that don't map onto the `audit`+`scan` recipe split, so it is a real restructure, not a one-line swap, and the follow-up must reconcile the recipe encoding with the parallel jobs. + +**Forward-sync:** when the policy-tests (gap #4) and property-tests (gap #9) layers land, add `policy`/`fuzz` to **both** the `ci` recipe **and** this section, to keep mirroring the template's conditional `ci` (`fmt-check lint typecheck test{% if enable_property_tests %} fuzz{% endif %}{% if enable_policy_tests %} policy{% endif %}{% if enable_dependency_audit %} audit{% endif %}`). + +Deliberate divergences from `template/justfile.jinja`'s `ci`: **none in the gate list** — `ci: fmt-check lint typecheck test audit`, `verify: ci`, and the `@echo` are exact. The divergences are structural: the maintainer's CI never runs `just ci` (parallel per-gate jobs — only `test` matrixed — with inline `uv run`/`uvx`/`mise exec` commands, vs the downstream `ci.yml`'s single `just ci`); there is no `check:` fast split (use `just fmt-check lint typecheck`); and candidate F is deferred. (The `audit` recipe's dropped `--no-dev` is a separate divergence documented under "Dependency audit", not a `ci`-list divergence.) + ## Run the tests ```bash @@ -55,12 +71,12 @@ Because nothing here re-derives the pins (no Renovate; the generation drift test ## Dependency audit ```bash -just audit # out-of-band dependency vulnerability audit: pip-audit over the full locked graph +just audit # dependency vulnerability audit: pip-audit over the full locked graph (also a `just ci` member) ``` -`just audit` exports the fully-resolved lockfile (`uv export --frozen --no-emit-project --no-hashes -o requirements-audit.txt`) and runs `uvx pip-audit@2.10.1 -r requirements-audit.txt`. It is out-of-band (chained into no recipe), but CI enforces it: the `pip-audit` step in the `scan` job of `.github/workflows/test-template.yml` is a blocking PR gate. Unlike semgrep — which scans 0 files here — pip-audit is the **substantive** dependency gate: it audits the real ~35-package dev+transitive graph (copier / pytest / ruff / basedpyright / pre-commit / plumbum / pyyaml + transitives), because **`--no-dev` is dropped**. Never restore `--no-dev`: `package = false` puts every dep in the dev group, so `--no-dev` would export 0 packages and pass vacuously. `requirements-audit.txt` is `rm`'d on success, gitignored, and lingers (harmlessly) only after a failed run. pip-audit is the one **non-hermetic** step in the `scan` job — it queries the OSV/PyPI advisory DB, so this gate can turn red on a newly-published CVE (or an advisory-DB outage) independent of any code change; a `--frozen` **export** failure instead means `uv.lock` is stale (re-lock), distinct from a pip-audit non-zero (a CVE). pip-audit runs via `uvx` (no pyproject dep, like semgrep/zizmor). +`just audit` exports the fully-resolved lockfile (`uv export --frozen --no-emit-project --no-hashes -o requirements-audit.txt`) and runs `uvx pip-audit@2.10.1 -r requirements-audit.txt`. It is chained into `just ci` (the local aggregate — see "Run every gate") and independently enforced in CI: the `pip-audit` step in the `scan` job of `.github/workflows/test-template.yml` is a blocking PR gate. Unlike semgrep — which scans 0 files here — pip-audit is the **substantive** dependency gate: it audits the real ~35-package dev+transitive graph (copier / pytest / ruff / basedpyright / pre-commit / plumbum / pyyaml + transitives), because **`--no-dev` is dropped**. Never restore `--no-dev`: `package = false` puts every dep in the dev group, so `--no-dev` would export 0 packages and pass vacuously. `requirements-audit.txt` is `rm`'d on success, gitignored, and lingers (harmlessly) only after a failed run. pip-audit is the one **non-hermetic** step in the `scan` job — it queries the OSV/PyPI advisory DB, so this gate can turn red on a newly-published CVE (or an advisory-DB outage) independent of any code change; a `--frozen` **export** failure instead means `uv.lock` is stale (re-lock), distinct from a pip-audit non-zero (a CVE). pip-audit runs via `uvx` (no pyproject dep, like semgrep/zizmor). -Deliberate divergences from the template's dependency-audit layer: `--no-dev` is dropped (above); pip-audit runs via `uvx pip-audit@2.10.1` in both the recipe and CI with **no** pyproject dep (the template adds `pip-audit>=2.10` to its dev group and runs `uv run pip-audit` locally); it is folded into `test-template.yml`'s `scan` job as a step (the template ships it in a standalone `scan.yml`); and it is out-of-band, not a `just ci` dependency (the template makes `audit` gating — the maintainer has no `ci` recipe). +Deliberate divergences from the template's dependency-audit layer: `--no-dev` is dropped (above); pip-audit runs via `uvx pip-audit@2.10.1` in both the recipe and CI with **no** pyproject dep (the template adds `pip-audit>=2.10` to its dev group and runs `uv run pip-audit` locally); it is folded into `test-template.yml`'s `scan` job as a step (the template ships it in a standalone `scan.yml`); and, like the template, `audit` is chained into `just ci` (see "Run every gate") while additionally enforced in CI as the `pip-audit` step in the `scan` job. Because nothing here re-derives the pin (no Renovate; the generation drift test reads only the *rendered* downstream), **bump every literal by hand, against the template.** pip-audit (`2.10.1`) has three maintainer sites — the `just audit` recipe, the `pip-audit` step in `test-template.yml`, and the prose above — synced to `template/.github/workflows/…scan.yml….jinja` (the only exact-version template site; the template justfile uses unpinned `uv run pip-audit` and template pyproject floors `pip-audit>=2.10`). No `mise.toml` or `pyproject.toml` pip-audit literal exists (uvx-run, unlike gitleaks). **Sync only the pin *value* — never the export flags:** the template's `uv export` keeps `--no-dev`, but the maintainer must not (it exports 0 packages here — see above), so a mechanical sync against the template would silently neuter the gate. (Mirrors the semgrep/gitleaks pin-sync note and the pre-commit "bump both `rev:` pins together" rule.) diff --git a/docs/superpowers/plans/2026-07-07-dogfood-editorconfig-ci.md b/docs/superpowers/plans/2026-07-07-dogfood-editorconfig-ci.md new file mode 100644 index 0000000..7d10427 --- /dev/null +++ b/docs/superpowers/plans/2026-07-07-dogfood-editorconfig-ci.md @@ -0,0 +1,75 @@ +# Dogfood editorconfig + aggregate ci Implementation Plan + +Design: `docs/superpowers/specs/2026-07-07-dogfood-editorconfig-ci-design.md`. +Closes Phase-1 item #4 (gap #7 editorconfig + gap #3 aggregate `just ci`) from +`docs/superpowers/plans/2026-07-01-dogfood-gap-audit.md` §7. + +## Global Constraints + +- Branch `chore/dogfood-editorconfig-ci`, cut from `main` after PR #8 (dependency-audit) merged. +- Commits: Conventional Commits, GPG-signed, author `Ashlen `, **no** AI-attribution trailer. +- Mirror the template; document forced divergences. `audit` **is** a `ci` member (template chains it). +- No file under `template/`; no CI/dep/mise/CHANGELOG change; no inline `# noqa`. +- The full matrix (`just test`, hence `just ci`) needs a roomy `TMPDIR` (default 4G tmpfs `/tmp` overflows). + +### Task 0: Commit the design spec + this plan + +Add both docs under `docs/superpowers/{specs,plans}/2026-07-07-dogfood-editorconfig-ci*`. +Commit: `docs(plans): add dogfood editorconfig + aggregate ci design + plan`. + +### Task 1: root `.editorconfig` (gap #7) + +`cp template/.editorconfig .editorconfig` — byte-for-byte (11 lines, glob `[*.{yml,yaml,json}]`, trailing `\n`). +Verify identical: `diff template/.editorconfig .editorconfig` → no output. +No generation-test change (root file, not under `template/`). +Commit: `chore(editorconfig): dogfood the shipped .editorconfig at repo root`. + +### Task 2: `ci` + `verify` recipe + audit-comment edit (gap #3) — justfile only + +Insert after `default:` (before `# Run the template…` / `test:`): + +```just +# The complete local gate — every PR-blocking check, reproducible locally. +# Mirrors template/justfile.jinja's `ci` (audit is a member — the template chains it, +# and audit is itself a PR-blocking check here via the CI `scan` job's pip-audit step). +# `test` is the full generation matrix: give it a roomy TMPDIR (the default 4G tmpfs +# /tmp can overflow) — see "Run every gate" in AGENTS.md. Scanners stay CI-only (off `ci`). +ci: fmt-check lint typecheck test audit + @echo "ci: all gates passed" + +verify: ci +``` + +Then fix the `audit` recipe's comment block (audit is now a `ci` member, so it is no longer "out-of-band"): +- header: `# Out-of-band dependency vulnerability audit: …` → `# Dependency vulnerability audit: …` +- CI-enforcement line: `# Enforced in CI by the `scan` job, not a `ci` recipe (there is none).` → `# Chained into `just ci` (mirrors the template) and independently enforced in CI by the `scan` job's pip-audit step.` + +Leave `scan`'s comment unchanged (scan stays off `ci` — still true). +Commit: `feat(ci): dogfood aggregate 'just ci' + 'verify' recipe`. + +### Task 3: AGENTS.md — new section + two live-site edits + +1. New `## Run every gate (`just ci`)` section after the intro line (before `## Run the tests`). Contents per design §Wiring/AGENTS.md. +2. Rewrite the Dependency-audit section's stale `ci` wording: the `just audit` fence comment (drop "out-of-band", note it is a `just ci` member), `AGENTS.md:61` (out-of-band → chained into `just ci`), and `AGENTS.md:63` (drop "the maintainer has no `ci` recipe"; state audit is chained into `just ci` like the template). +Commit: `docs(agents): document 'just ci' (audit mirrored in, scanners CI-only)`. + +### Finishing: full verification + PR + +Run on a clean tree (roomy TMPDIR): +1. `TMPDIR= just ci` → ends `ci: all gates passed` (subsumes a separate `just test`; audit needs network). +2. `just --show verify` → `verify: ci` (inspect, don't re-run the matrix). +3. `just precommit` → clean (eof/trailing-whitespace on the new `.editorconfig`, justfile, AGENTS.md). +4. `diff template/.editorconfig .editorconfig` → empty. +5. `grep -nF -e 'there is none' -e 'no `ci` recipe' justfile AGENTS.md` → empty, and `grep -ni 'out-of-band' justfile AGENTS.md` → only `scan`/generic references (audit no longer "out-of-band"). +6. `git status --short` → empty; `requirements-audit.txt` absent. +7. zizmor N/A (no workflow touched) — state in PR. +Then push, open PR (base `main`), hand off merge to the operator. + +## Self-review notes (author check against the spec) + +- `audit` in `ci` (not dropped) — matches template L7. ✔ +- `.editorconfig` verbatim, `{yml,yaml,json}` glob (no `toml`). ✔ +- No `template/` file → no generation-test change. ✔ +- All falsified live sites updated — `audit` recipe header + CI-enforcement comments, and the Dependency-audit fence + two prose clauses; `scan`'s "out-of-band"/"not `ci`" wording untouched (still true). ✔ +- No `check:` split; candidate F deferred; dated docs frozen. ✔ +- No CI/dep/mise/CHANGELOG change. ✔ diff --git a/docs/superpowers/specs/2026-07-07-dogfood-editorconfig-ci-design.md b/docs/superpowers/specs/2026-07-07-dogfood-editorconfig-ci-design.md new file mode 100644 index 0000000..52cb53f --- /dev/null +++ b/docs/superpowers/specs/2026-07-07-dogfood-editorconfig-ci-design.md @@ -0,0 +1,122 @@ +# Dogfood editorconfig + aggregate ci — design (2026-07-07) + +## Goal + +Close two Phase-1 dogfooding gaps from `docs/superpowers/plans/2026-07-01-dogfood-gap-audit.md` §7 item #4 in one bundle, both instantiating template layers the maintainer already ships but does not run on itself: + +- **Gap #7 — editorconfig.** `template/.editorconfig` is shipped verbatim and unconditionally into every downstream; the maintainer repo carries no root `.editorconfig`, yet is full of files it governs (`tests/*.py`, 2-space YAML/TOML in `pyproject`/`mise`/`copier`/workflows). The template enforces it **passively** (no editorconfig-checker in its pre-commit/justfile/CI), so full parity is met by dropping the verbatim file at root — values already match repo conventions, zero conflict. +- **Gap #3 — aggregate `just ci`.** `template/justfile.jinja` ships `ci: fmt-check lint typecheck test{…conditional fuzz/policy/audit}` + `@echo "ci: all gates passed"` and `verify: ci`. Every gate the aggregate would chain already runs here as a standalone recipe and a PR-blocking CI job; only the unified local entry point is absent. + +Governing principle (as at every prior gap): **mirror the template; diverge only where the maintainer surface forces it, and document each forced divergence in AGENTS.md.** + +### Empirical anchors (verified against the tree, not roadmap prose) + +- `template/justfile.jinja:7` **chains `audit` into `ci`** via `{% if enable_dependency_audit %} audit{% endif %}` — and that toggle is on/dogfooded. So the faithful maintainer mirror **includes `audit`**: `ci: fmt-check lint typecheck test audit`. (An earlier draft dropped `audit` on a "non-hermetic forces divergence" premise; that premise is factually wrong — it is the same pip-audit the template chains anyway — and was reversed in review.) +- `template/.editorconfig` glob is `[*.{yml,yaml,json}]` — **not** `{…,toml}` (roadmap §115 misstates this). `tests/test_generation.py:96` asserts `"toml" not in editorconfig`, so a verbatim `cp` is both correct and keeps the downstream assertion coherent. +- The template ships **no** `check:` recipe; the fast inner loop is the ad-hoc `just fmt-check lint typecheck`. + +## Decisions + +- **D1 — `audit` is a member of `ci`.** Mirror the template exactly: `ci: fmt-check lint typecheck test audit`. Dropping it would be an unforced, taste-based divergence the governing principle disqualifies; `audit` is itself a PR-blocking check here (the `scan` job's `pip-audit` step), so the local aggregate must reproduce it. +- **D2 — `test` (the full generation matrix) stays in `ci`.** It is the maintainer's analog of the template's `test`; omitting it defeats the gap. Because it is heavy and fills the default 4G tmpfs `/tmp`, document the roomy-`TMPDIR` requirement (recipe comment + AGENTS section + verification). +- **D3 — no `check:` fast split.** The template ships none; a named recipe would be an unforced divergence with zero new capability. Document the one-liner `just fmt-check lint typecheck` instead. +- **D4 — `verify: ci` added verbatim.** Free exact parity with the template's bare alias. +- **D5 — candidate F (point CI jobs at `just` recipes) is deferred.** Not folded into this bundle. The real cost is restructuring the `scan` job — pip-audit + semgrep + gitleaks are three steps that do not map onto the `audit`+`scan` recipe split (different tool subsets + `mise` handling), so F is a genuine restructure on the zizmor-gated workflow, not a one-line swap. Acknowledged consequence: shipping `ci` now creates a second encoding of the gate set (recipe vs the four parallel CI jobs) that can drift; the follow-up F PR must reconcile it. +- **D6 — bundle editorconfig + ci in one PR.** Roadmap §176 bundles them; both are trivial and both touch AGENTS.md. (editorconfig alone has no dependency on anything and could ship standalone, but bundling stays coherent.) +- **D7 — scanners stay off `ci` (CI-only), unchanged.** Both template and maintainer keep `scan` out of `ci`; the `scan` recipe/prose "off `ci`" wording remains true and is left untouched. +- **D8 — `ci` does not chain `precommit`.** Local-only, no CI job — matches the template. + +## Wiring + +### `.editorconfig` (root, gap #7) + +Byte-for-byte `cp template/.editorconfig .editorconfig` (11 lines, trailing newline, glob `[*.{yml,yaml,json}]`). No generation-test change: this is a maintainer-root file, not a `template/` file, so the "NEVER add a `template/` file without an assertion" rule is inapplicable; `test_generation.py:92-96` continues to assert the *rendered downstream* file. + +### `justfile` (gap #3) + +Insert immediately after the `default:` recipe, mirroring the template's ci-first ordering: + +```just +# The complete local gate — every PR-blocking check, reproducible locally. +# Mirrors template/justfile.jinja's `ci` (audit is a member — the template chains it, +# and audit is itself a PR-blocking check here via the CI `scan` job's pip-audit step). +# `test` is the full generation matrix: give it a roomy TMPDIR (the default 4G tmpfs +# /tmp can overflow) — see "Run every gate" in AGENTS.md. Scanners stay CI-only (off `ci`). +ci: fmt-check lint typecheck test audit + @echo "ci: all gates passed" + +verify: ci +``` + +`@echo "ci: all gates passed"` and `verify: ci` are exact template strings. The operator's absolute TMPDIR is **not** hardcoded in the committed comment. + +### `justfile` — live-site consistency edits (introducing `ci` falsifies them) + +The `audit` recipe's comment block is rewritten in two places, because `audit` is now a `ci` member (no longer "out-of-band"): + +- header: `# Out-of-band dependency vulnerability audit: pip-audit over the FULL locked graph.` → `# Dependency vulnerability audit: pip-audit over the FULL locked graph.` +- CI-enforcement line: `# Enforced in CI by the `scan` job, not a `ci` recipe (there is none).` → `# Chained into `just ci` (mirrors the template) and independently enforced in CI by the `scan` job's pip-audit step.` + +The `scan` recipe's comment (`# Out-of-band secret + SAST scan … not `ci`.`) is **left unchanged** — scan genuinely stays off `ci`, so its wording remains true. This is the crux of the mirror decision: `audit` follows the template onto `ci` (and sheds "out-of-band"); `scan` does not. + +### `AGENTS.md` — new section + two live-site edits + +New `## Run every gate (`just ci`)` section inserted after the intro line (before `## Run the tests`), documenting: the recipe shape (`fmt-check lint typecheck test audit` + echo; `verify` alias); the roomy-`TMPDIR` requirement and the `just fmt-check lint typecheck` fast subset; that `audit` is chained in (mirrors the template) while scanners stay CI-only; the maintainer-vs-downstream `just ci` disambiguation; a **forward-sync note** (when gap #4 policy / #9 fuzz land, both the recipe and this section must gain `policy`/`fuzz` to keep mirroring the template's conditional `ci`); the CI note (maintainer CI keeps parallel per-gate jobs + matrix and spells `uv run …` inline, does not run `just ci`; candidate F deferred); and the forced-divergences paragraph. + +The Dependency-audit section's stale `ci`-related wording is rewritten (all falsified by the new `ci` recipe, since `audit` is now a `ci` member): +- fence comment `just audit # out-of-band dependency vulnerability audit…` → `just audit # dependency vulnerability audit … (also a `just ci` member)` +- `AGENTS.md:61` "It is out-of-band (chained into no recipe), but CI enforces it: the `pip-audit` step…" → "It is chained into `just ci` (the local aggregate) and independently enforced in CI: the `pip-audit` step…" +- `AGENTS.md:63` "…and it is out-of-band, not a `just ci` dependency (the template makes `audit` gating — the maintainer has no `ci` recipe)." → "…. Like the template, `audit` is chained into `just ci` (see "Run every gate"); it is additionally enforced in CI as the `pip-audit` step in the `scan` job." + +The `scan` section's "out-of-band" wording (fence + prose) is **unchanged** — scan stays off `ci`. + +### CI / generation-test / dep / mise / CHANGELOG + +**None.** No `test-template.yml` edit (F deferred). No `pyproject.toml`/`mise.toml`/`tests/` change. No CHANGELOG entry — the existing `[Unreleased]` `.editorconfig`/`ci` lines describe the **template product** (rendered downstream, consumed via `copier update`); this PR touches only the maintainer's own dogfood surface, matching gaps #1/#2/#5. + +## Documented divergences (mirror-except-where-surface-differs) + +- **`ci` gate list: no divergence** — the maintainer mirrors `ci: fmt-check lint typecheck test audit` (audit included because `enable_dependency_audit` is on); `verify: ci` and the echo line are exact. +- **Structural divergence (not in `ci` itself):** the maintainer's CI never runs `just ci`; it keeps parallel per-gate jobs (`test`/`typecheck`/`lint`/`scan`) and an OS×Python matrix, each spelling `uv run …` inline, whereas the template's downstream `ci.yml` runs the single command `just ci`. So `just ci` here is the local reproduction of the PR gate, not the CI entry point. +- **No `check:` split** — the template ships none; the fast subset is the ad-hoc `just fmt-check lint typecheck`. +- **Candidate F deferred** — see D5. +- (The `audit` recipe's dropped `--no-dev` is a separate, already-documented divergence in the Dependency-audit section, not a `ci`-list divergence.) + +## Acceptance + +- `TMPDIR= just ci` runs fmt-check + lint + typecheck + test + audit and ends with `ci: all gates passed`. +- `just --show verify` shows `verify: ci` (thin alias; not re-run). +- `just precommit` clean (the new root `.editorconfig` is not `^template/`-excluded, so it must end in `\n`; the verbatim copy does). +- Root `.editorconfig` byte-identical to `template/.editorconfig`. +- Every falsified live site updated — the `audit` recipe's header + CI-enforcement comments, and the Dependency-audit section's fence + two prose clauses; `grep -ni 'out-of-band' justfile AGENTS.md` returns **only** `scan`/generic references, and `grep -nF -e 'there is none' -e 'no `ci` recipe' justfile AGENTS.md` returns nothing. +- Clean tree; `requirements-audit.txt` gone (rm'd by the audit step on success). +- CI green on the PR (no workflow change; zizmor N/A). + +## Out of scope / tracked follow-up + +- **Candidate F** (CI jobs call `just` recipes) — deferred to a follow-up that must reconcile the `scan`-job restructure with the new recipe encoding (D5). +- **`check:` fast split** — deliberately not added (D3). +- **~8 stale "no `ci` recipe" mentions in dated `docs/superpowers/` files** — left frozen as historical snapshots, consistent with the pin-sync convention (which enumerates only recipe + CI job + "the prose above", never design docs). Not swept. +- Later Phase-1 items: policy-tests (#4, closes #8) → renovate (#6) → property-tests (#9). The policy/fuzz layers will trip the AGENTS forward-sync note. + +## Explicit no-touch (guard rails) + +- No file added under `template/` (so no generation-test obligation). +- No `test-template.yml`, `pyproject.toml`, `mise.toml`, `uv.lock`, `tests/`, or CHANGELOG change. +- No `scan`/`audit` recipe body change; no scanner "off `ci`" wording change (still true). +- No inline `# noqa`; no Action SHA bump. + +## Files touched (maintainer root only) + +- `.editorconfig` (new, verbatim) +- `justfile` (add `ci`/`verify`; edit the audit-recipe comment) +- `AGENTS.md` (new "Run every gate" section; rewrite two audit-section clauses) +- `docs/superpowers/specs/2026-07-07-dogfood-editorconfig-ci-design.md` + `docs/superpowers/plans/2026-07-07-dogfood-editorconfig-ci.md` (this pair) + +## Endorsed decisions (verified in adversarial review — do not reopen) + +- `audit` **is** in `ci` (D1) — reversal of the earlier draft; template L7 chains it. +- `.editorconfig` is a verbatim `cp` with glob `{yml,yaml,json}` (no `toml`); roadmap §115 is wrong. +- No generation-test change (root file, not `template/`). +- Only the **two live sites** are swept; dated docs stay frozen. +- Candidate F and the `check:` split are deliberate scope reductions, not omissions. diff --git a/justfile b/justfile index bd8682e..c7a71d5 100644 --- a/justfile +++ b/justfile @@ -3,6 +3,16 @@ set shell := ["bash", "-eu", "-o", "pipefail", "-c"] default: @just --list +# The complete local gate — every PR-blocking check, reproducible locally. +# Mirrors template/justfile.jinja's `ci` (audit is a member — the template chains it, +# and audit is itself a PR-blocking check here via the CI `scan` job's pip-audit step). +# `test` is the full generation matrix: give it a roomy TMPDIR (the default 4G tmpfs +# /tmp can overflow) — see "Run every gate" in AGENTS.md. Scanners stay CI-only (off `ci`). +ci: fmt-check lint typecheck test audit + @echo "ci: all gates passed" + +verify: ci + # Run the template generation + update tests test: uv run pytest @@ -42,10 +52,10 @@ scan: # `git` (not `dir`): scan committed history like CI, catching secrets committed then deleted. gitleaks git . --redact --exit-code 1 -# Out-of-band dependency vulnerability audit: pip-audit over the FULL locked graph. +# Dependency vulnerability audit: pip-audit over the FULL locked graph. # `--no-dev` is dropped (unlike the template): package=false puts every dep in the # dev group, so the template's --no-dev would export 0 packages and pass vacuously. -# Enforced in CI by the `scan` job, not a `ci` recipe (there is none). +# Chained into `just ci` (mirrors the template) and independently enforced in CI by the `scan` job's pip-audit step. audit: uv export --frozen --no-emit-project --no-hashes -o requirements-audit.txt uvx pip-audit@2.10.1 -r requirements-audit.txt