Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -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
22 changes: 19 additions & 3 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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.)

Expand Down
75 changes: 75 additions & 0 deletions docs/superpowers/plans/2026-07-07-dogfood-editorconfig-ci.md
Original file line number Diff line number Diff line change
@@ -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 <dev@anthes.is>`, **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=<roomy> 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. ✔
Loading
Loading