diff --git a/scripts/loop/README.md b/scripts/loop/README.md index 9791aca..1d99ed7 100644 --- a/scripts/loop/README.md +++ b/scripts/loop/README.md @@ -264,7 +264,7 @@ reason a future reader may see the parameter and wonder where it came from. | `validatePush` | `entry.mjs` | `pr-opened` without a valid proposal (wrong branch, empty title, another task's number, or `writeLevel=report`) is downgraded to `failed`, so a task can never sit in `waiting-merge` waiting for a PR nobody opened. | | `ensureLoopPrBody` | `planActions` | Repairs the PR body's `Closes #` / `loop-task: #` markers. The router requires both, and a missing marker would make the loop's own PR look external — no acceptance row, and its merge would close the task by the wrong path. | | PR number recorded | `finishRun` | The number comes back from `gh pr create` and lands in `task.prs` + a `pr-created` timeline row. | -| git identity | `agentEnv` | `GIT_AUTHOR_*`/`GIT_COMMITTER_*` are injected under `auto`; a fresh runner has no global git config, and the commit itself is local (no credential involved). | +| git identity | `agentEnv` | `GIT_AUTHOR_*`/`GIT_COMMITTER_*` are injected under `auto` as GitHub Actions' own bot, `github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>` — the same identity the host's PR comes from. A fresh runner has no global git config, and the commit itself is local (no credential involved). The earlier `loop@users.noreply.github.com` belonged to the real, unrelated user `@loop`, so every loop commit was falsely attributed to them (#52, #56). | Note what is **not** load-bearing: the host-versus-agent split. It exists because the writes must be derived from the agent's result file in one place that can enforce the diff --git a/scripts/loop/shared/agent.mjs b/scripts/loop/shared/agent.mjs index 84845fa..3c5a4df 100644 --- a/scripts/loop/shared/agent.mjs +++ b/scripts/loop/shared/agent.mjs @@ -191,9 +191,17 @@ export function agentEnv(writeLevel, base = process.env) { // Product stages commit the branch the host pushes, and the identity must not come // from the sandbox's global git config (there is none on a fresh runner). Set it // here rather than making the agent discover it: a commit is a local operation. + // + // The identity is GitHub Actions' own bot (`github-actions[bot]`), matching the PR + // author the host opens with the job token. The previous + // `loop@users.noreply.github.com` resolved to the real, unrelated account @loop + // (id 1519971), so every loop commit was falsely attributed to a stranger (#52, + // #56). A bot noreply address is not a user account, so it cannot be squatted the + // way a bare login can. if (writeLevel === 'auto') { - env.GIT_AUTHOR_NAME = env.GIT_AUTHOR_NAME ?? 'tiny-oss loop'; - env.GIT_AUTHOR_EMAIL = env.GIT_AUTHOR_EMAIL ?? 'loop@users.noreply.github.com'; + env.GIT_AUTHOR_NAME = env.GIT_AUTHOR_NAME ?? 'github-actions[bot]'; + env.GIT_AUTHOR_EMAIL = env.GIT_AUTHOR_EMAIL + ?? '41898282+github-actions[bot]@users.noreply.github.com'; env.GIT_COMMITTER_NAME = env.GIT_COMMITTER_NAME ?? env.GIT_AUTHOR_NAME; env.GIT_COMMITTER_EMAIL = env.GIT_COMMITTER_EMAIL ?? env.GIT_AUTHOR_EMAIL; }