diff --git a/k8s/apps/agent-pod/coordinator-configmap.yaml b/k8s/apps/agent-pod/coordinator-configmap.yaml index 7b1cab1..2c8c86d 100644 --- a/k8s/apps/agent-pod/coordinator-configmap.yaml +++ b/k8s/apps/agent-pod/coordinator-configmap.yaml @@ -752,31 +752,47 @@ data: return finish("branch-crashed"); } - if (i === 0 && !resuming) { - const gitignoreAdditions = [ - "", - "# agent-harness: build artifacts and vendored archives never belong in source control", - "*.tar.gz", - "*.tgz", - "*.crate", - "*.zip", - "*.bin", - "*.whl", - "vendor/", - "node_modules/", - "", - "# agent-harness: task/phase completion sentinel files, harness bookkeeping only", - ".task-result-*", - ".phase-result-*", - ".stage-done-*", - "", - "# agent-harness: PLAN.md is per-task planner scratch state, never a deliverable", - "PLAN.md", - ].join("\n"); - fs.appendFileSync(path.join(cwd, ".gitignore"), gitignoreAdditions + "\n"); + // Idempotent and run every phase, NOT gated on a fresh (non-resumed) + // start -- every run this session was a resume, so the old i===0 && + // !resuming gate meant this setup permanently never ran on poiman's + // branch, and portfolio's PLAN.md stayed tracked from before this rule + // ever existed (gitignore has no effect on an already-tracked file -- + // observed live: it kept getting swept back in by every `git add -A` + // regardless of the ignore rule). Check-and-fix on every phase instead + // of once-at-genesis so a repo that's missing either self-heals on its + // very next run rather than carrying the gap forever. + const gitignorePath = path.join(cwd, ".gitignore"); + const currentGitignore = fs.existsSync(gitignorePath) ? fs.readFileSync(gitignorePath, "utf8").split("\n") : []; + const requiredGitignoreLines = [ + "*.tar.gz", + "*.tgz", + "*.crate", + "*.zip", + "*.bin", + "*.whl", + "vendor/", + "node_modules/", + ".task-result-*", + ".phase-result-*", + ".stage-done-*", + "PLAN.md", + ]; + const missingGitignoreLines = requiredGitignoreLines.filter((line) => !currentGitignore.includes(line)); + if (missingGitignoreLines.length > 0) { + fs.appendFileSync( + gitignorePath, + "\n# agent-harness: build artifacts, vendored archives, and harness bookkeeping never belong in source control\n" + + missingGitignoreLines.join("\n") + + "\n" + ); await runGit(cwd, ["add", ".gitignore"]); await runGit(cwd, ["commit", "-m", "chore: broaden .gitignore for agent-run artifacts"]); } + const trackedFiles = await runGit(cwd, ["ls-tree", "-r", "HEAD", "--name-only"]); + if (trackedFiles.out.split("\n").includes("PLAN.md")) { + await runGit(cwd, ["rm", "--cached", "PLAN.md"]); + await runGit(cwd, ["commit", "-m", "chore: untrack PLAN.md (already gitignored, was committed pre-rule)"]); + } await runPhase(cwd, baseBranch, phaseBranch, phaseTasks, pool, repoId, pipelineSession);