feat: keep run artifacts under hidden .pygienium/ and out of git

All per-check artifacts, all-summary, and export move from pygienium/ to
.pygienium/; ensureRunStateIgnored appends .pygienium/ to the target
repo's .gitignore on first run (opt out with --no-gitignore), so a run
never stages its own output. Export now reads a single canonical root.
This commit is contained in:
2026-08-09 16:45:29 -04:00
parent 288506e84d
commit d0e8ad5571
17 changed files with 183 additions and 82 deletions

View File

@@ -4,7 +4,7 @@
* Mirrors the spec scenario: run `/pygienium-all` on a small repo and assert:
* - every registered check runs exactly once in registry order;
* - run-state shows all checks complete and the overall run complete;
* - `pygienium/all-summary.md` is present and lists per-check outcomes;
* - `.pygienium/all-summary.md` is present and lists per-check outcomes;
* - `--only=alpha,gamma` narrows the candidate set preserving order;
* - interrupted/resumed runs re-dispatch non-terminal checks while skipping
* terminal ones, unless `--fresh` resets everything.
@@ -57,9 +57,9 @@ function fakeCheck(name: string): CheckDefinition {
fixAgentName: "fixer",
phaseId: "scan",
buildScanTask: (_cwd, scope) =>
`!write pygienium/checks/${name}/findings.md # ${name} findings\nscan-target:${scope.target}\n!echo ${name}-scan`,
`!write .pygienium/checks/${name}/findings.md # ${name} findings\nscan-target:${scope.target}\n!echo ${name}-scan`,
buildFixTask: (_cwd, _scope, findings) =>
`!write pygienium/checks/${name}/changes.md # ${name} changes\nbased-on:${findings.split("\n")[0] ?? ""}\n!echo ${name}-fix`,
`!write .pygienium/checks/${name}/changes.md # ${name} changes\nbased-on:${findings.split("\n")[0] ?? ""}\n!echo ${name}-fix`,
gate: () => undefined,
};
}
@@ -132,12 +132,16 @@ describe("/pygienium-all orchestrator (task 12)", () => {
await rm(cwd, { recursive: true, force: true });
});
it("parseAllArgs parses path, --fix, --fresh, and --only", () => {
it("parseAllArgs parses path, --fix, --fresh, --no-gitignore, and --only", () => {
const p = parseAllArgs("subdir --fix --only=alpha,beta --fresh", cwd);
expect(p.target).toBe(join(cwd, "subdir"));
expect(p.fix).toBe(true);
expect(p.fresh).toBe(true);
expect(p.gitignore).toBe(true); // default: keep the .gitignore guard on
expect(p.only).toEqual(["alpha", "beta"]);
const noGi = parseAllArgs("--no-gitignore", cwd);
expect(noGi.gitignore).toBe(false);
expect(noGi.target).toBe(cwd);
});
it("selectChecks preserves registry order for the --only subset", () => {
@@ -176,7 +180,7 @@ describe("/pygienium-all orchestrator (task 12)", () => {
expect(state?.recon.complete).toBe(true);
});
it("writes pygienium/all-summary.md listing per-check outcomes", async () => {
it("writes .pygienium/all-summary.md listing per-check outcomes", async () => {
registerCheck(fakeCheck("alpha"));
registerCheck(fakeCheck("beta"));
@@ -193,7 +197,7 @@ describe("/pygienium-all orchestrator (task 12)", () => {
expect(summary).toContain("changes:");
// Artifacts actually exist on disk.
const alphaFindings = await readFile(
join(cwd, "pygienium", "checks", "alpha", "findings.md"),
join(cwd, ".pygienium", "checks", "alpha", "findings.md"),
"utf8",
);
expect(alphaFindings).toContain("alpha findings");
@@ -233,7 +237,7 @@ describe("/pygienium-all orchestrator (task 12)", () => {
await captureStdout(() => handleAllCommand("--fix", stubCtx(cwd)));
expect(track.dispatched.length).toBe(4); // 2 checks × 2 phases
const firstAlphaFix = await readFile(
join(cwd, "pygienium", "checks", "alpha", "changes.md"),
join(cwd, ".pygienium", "checks", "alpha", "changes.md"),
"utf8",
);
@@ -255,7 +259,7 @@ describe("/pygienium-all orchestrator (task 12)", () => {
expect(state3?.status).toBe("complete");
// The fresh re-run overwrote alpha's changes.md (still valid content).
const alphaFix2 = await readFile(
join(cwd, "pygienium", "checks", "alpha", "changes.md"),
join(cwd, ".pygienium", "checks", "alpha", "changes.md"),
"utf8",
);
expect(alphaFix2).toContain("alpha changes");
@@ -278,7 +282,7 @@ describe("/pygienium-all orchestrator (task 12)", () => {
finishedAt: Date.now(),
};
// Pre-create alpha's on-disk artifacts so its completed entry has artifacts.
const alphaDir = join(cwd, "pygienium", "checks", "alpha");
const alphaDir = join(cwd, ".pygienium", "checks", "alpha");
await mkdir(alphaDir, { recursive: true });
await writeFile(
join(alphaDir, "findings.md"),