Files
Kun Chen 2498e75f6a fix(daemon): move test evidence out of system temp storage (#735)
* fix(daemon): keep run evidence off shared /tmp and reap it ourselves

The test step wrote evidence to os.TempDir()/no-mistakes-evidence. The
daemon's service unit exports only HOME, PATH, and proxy variables, so
TMPDIR is unset and that resolved to the shared /tmp - which Ubuntu has
mounted as a systemd tmpfs since 24.10, putting every screenshot and
rendered-HTML artifact in RAM.

Nothing in this program ever removed it, either. The directory was a
fixed name that accumulated one subdirectory per run, cleaned only by an
OS timer we do not control and that has no equivalent on Windows. On one
developer machine that was 876 run directories over ten days, 823 of them
empty: the test step creates the directory before the agent decides
whether it has anything to write.

Evidence now lives at <NM_HOME>/evidence/<runID>, owned by internal/paths
like every other location. The app root is disk backed on macOS, Linux,
and Windows alike, so there is no runtime.GOOS branch and the macOS path
is not regressed - it improves, since /var/folders is periodically purged
and that silently broke the local artifact paths older PR bodies fall
back to.

Cleanup is now ours, in three layers: a finished run's empty directory is
removed at run cleanup, a reaper bounds the directory by age and count
oldest-first after every run and at daemon startup, and the
pre-relocation temp directory is drained under the same policy. All three
reuse the existing pending/running guard, so a run still in flight is
never touched.

test.evidence gains local_root, retention, and max_runs. They are
global-only: a repository does not get to name a filesystem path this
machine's daemon writes to, nor set the retention budget for a directory
every repository on the machine shares. There is deliberately no
environment variable - the service unit preserves only proxy keys, so an
env-gated value would be dropped on the next update.

Also removes the second, independent copy of the evidence path in the
agent steering preamble, which rebuilt it from os.TempDir() on its own.
The executor now resolves the path once and both consumers read it.

* no-mistakes(review): Protect unowned directories from evidence reaping

* no-mistakes(document): Clarify managed evidence storage scope

* no-mistakes: apply CI fixes

* no-mistakes: apply CI fixes

* fix(daemon): clean run evidence at the recovered-run completion boundary

Evidence ownership claims cleanup runs after each run, but only the
fresh-run defer called cleanupRunEvidence. A run parked at an approval
gate when the daemon stopped is finished by resumeRecoveredRun, which has
its own completion defer, so a resumed run kept its empty evidence
directory until some later run or a restart happened to sweep it.

There are exactly two completion boundaries and both are now covered. The
regression test drives resumeRecoveredRun and asserts the directory is
gone afterwards; every outcome of a resumed run shares that defer, so the
boundary is the thing worth asserting on.

* ci: give the test job a budget the Windows leg actually fits in

The Windows leg was cancelled at timeout-minutes twice in a row with no
verdict, on a suite where every package that reported had passed. It is
process-spawn bound rather than compute bound, so it compiles every test
binary and then runs the git-backed packages at roughly 10x their Linux
cost, and 25 minutes no longer covered that.

An evidence-free cancellation is worse than a slow job: it is reported as
a check failure and reads as a defect in the change under test, which is
exactly how it was read here.

The cap stays a runaway guard rather than a target. Linux and macOS
finish far inside it, 40 minutes still keeps a wedged runner well short
of the six-hour default, and go test -timeout is deliberately unchanged
so a genuine hang still produces a goroutine dump first.

This does not close the coherence gap behind it: -timeout bounds each
test binary while timeout-minutes bounds the whole job including compile,
so total wall can still exceed the cap with no binary reaching its own
deadline. Bounding per-job wall properly is follow-up work.

* chore: pin review auto-fix to the product default in this repo

The code default for auto_fix.review is 0 so review findings park for a
human decision, but this repo carried no auto_fix block and therefore
inherited whatever an operator set globally.

With it enabled, a one-line CI timeout change drew a speculative review
finding that the auto-fixer answered with a new DB table, a schema
migration, and a new package, then spent three rounds finding errors in
its own invention. None of it was asked for, and a parked finding would
have surfaced the question instead of quietly answering it.

Only review is pinned. The deterministic steps keep whatever the operator
configured, because a lint or test finding has an objective checker and
does not need a human to decide whether the fix belongs.

* no-mistakes(review): Reject evidence roots inside managed worktrees

* no-mistakes(document): Clarify evidence storage constraints
2026-08-14 14:27:42 -07:00

38 lines
2.0 KiB
YAML

# no-mistakes' own dogfood config.
# Local Test is intentionally empty: the Test step is targeted validation of the
# requested intent (agent-driven smallest relevant checks + evidence), never a
# repository-wide regression suite. Broad race-enabled Go coverage stays in
# remote CI (`go test -race ./...` in .github/workflows/ci.yml) and remains
# mandatory before a PR is ready. See docs/src/content/docs/reference/repo-config.md
# (commands.test) and docs/src/content/docs/reference/pipeline-steps.md (Test).
# Lint and format stay explicit so those gates stay deterministic.
commands:
lint: "make lint"
format: "gofmt -w ."
# Pin review auto-fix to the product default so this repo does not inherit an
# operator's global override. Review findings park for a human decision; other
# steps keep their configured behavior.
auto_fix:
review: 0
# Documentation ownership map for the document step's placement policy.
document:
instructions: |
Configuration keys are owned by docs/src/content/docs/reference/global-config.md
(global fields) and docs/src/content/docs/reference/repo-config.md (repo fields).
Environment variables and the telemetry local/remote split are owned by
docs/src/content/docs/reference/environment.md.
The daemon lifecycle model (singleton lock, lifecycle guard, crash recovery)
is owned by docs/src/content/docs/concepts/daemon.md.
Agent-driving guidance is owned by the skill body in internal/skill/skill.go
and the live axi output strings; docs/src/content/docs/guides/agents.md keeps
only the canonical invariant sentences pinned by internal/cli/axi_guidance_test.go.
Guides and troubleshooting pages explain purpose and symptoms and link to the
owners above instead of restating their tables, examples, or key semantics.
AGENTS.md holds invariants plus pointers to the authoritative implementation
and regression tests. Do not duplicate generic command catalogs, but retain
its project-specific verification sequence and other load-bearing operating
guidance.