@lead: Dev-agent runs can break later steps by touching the runtime .agents-workflow scripts checkout #34

Merged
ffaerber merged 1 commits from ai/issue-33 into main 2026-07-04 09:43:14 +02:00

Summary

Dev-agent runs could fail at the post-agent steps (No such file or directory, exit 127) with no PR opened, because the shared step scripts are checked out at .agents-workflow/ inside the same working tree the agent edits. An agent that committed it as a gitlink or rm -rf'd it as "leftover" destroyed the scripts the later steps (build-activity-log, publish) run from $SCRIPTS, stranding pushed work on a branch with no PR (issue #33).

This makes the run immune: the scripts now run from a stable location outside the workspace, the in-tree checkout artifact is scrubbed before publishing, and a failure-safe rescue step guarantees pushed work always gets a PR.

Changes

  • agent.yml: new "Stage shared scripts outside the workspace" step copies the fetched scripts to ${{ runner.temp }}/agents-scripts; repointed $SCRIPTS in every downstream step to that out-of-tree path (was github.workspace/.agents-workflow/...).
  • agent.yml: new if: failure() "Rescue" step opens a PR for the pushed branch when a run fails after commits were pushed, so work is never silently stranded.
  • publish.sh: scrub any in-tree .agents-workflow artifact (untracked dir / tracked files / committed gitlink+submodule) before committing, so it never lands in a PR or breaks the git ops.
  • rescue-pr.sh (new): best-effort, idempotent PR-open-on-failure for the pushed branch, posting the PR link back to the issue.

Resolves #33 · 🤖 @lead

## Summary Dev-agent runs could fail at the post-agent steps (`No such file or directory`, exit 127) with no PR opened, because the shared step scripts are checked out at `.agents-workflow/` **inside** the same working tree the agent edits. An agent that committed it as a gitlink or `rm -rf`'d it as "leftover" destroyed the scripts the later steps (`build-activity-log`, `publish`) run from `$SCRIPTS`, stranding pushed work on a branch with no PR (issue #33). This makes the run immune: the scripts now run from a stable location **outside** the workspace, the in-tree checkout artifact is scrubbed before publishing, and a failure-safe rescue step guarantees pushed work always gets a PR. ## Changes - **agent.yml**: new "Stage shared scripts outside the workspace" step copies the fetched scripts to `${{ runner.temp }}/agents-scripts`; repointed `$SCRIPTS` in every downstream step to that out-of-tree path (was `github.workspace/.agents-workflow/...`). - **agent.yml**: new `if: failure()` "Rescue" step opens a PR for the pushed branch when a run fails after commits were pushed, so work is never silently stranded. - **publish.sh**: scrub any in-tree `.agents-workflow` artifact (untracked dir / tracked files / committed gitlink+submodule) before committing, so it never lands in a PR or breaks the git ops. - **rescue-pr.sh** (new): best-effort, idempotent PR-open-on-failure for the pushed branch, posting the PR link back to the issue. --- Resolves #33 · 🤖 @lead
gitea-actions bot added 1 commit 2026-07-04 09:35:15 +02:00
Stage the shared scripts into $RUNNER_TEMP and point $SCRIPTS there for every
step, so an agent that commits/deletes the in-tree .agents-workflow checkout no
longer destroys the scripts the post-agent steps run (issue #33). Scrub any
in-tree .agents-workflow artifact before publishing, and add a failure-safe
rescue step that opens a PR for pushed work when a run fails.

🤖 @lead PR ready for review — @ffaerber please review & merge:

🤖 **@lead** — ✅ PR ready for review — @ffaerber please review & merge: - https://git.ffaerber.duckdns.org/ffaerber/agents/pulls/34

🤖 @lead — activity log (105 entries):

tool calls & reasoning

💬 I'll start by reading AGENTS.md and understanding the current workflow structure.
🔧 read: AGENTS.md
🔧 glob: ``
🔧 bash: git branch --show-current && git status && echo "---FILES---" && find .gitea -type f 2>/dev/null
🔧 read: .gitea/workflows/agent.yml
💬 Now I understand the architecture. The core problem: .agents-workflow is checked out inside ${{ github.workspace }} (the caller's working tree the agent edits). Let me look at the key scripts to understand what needs to change.
🔧 read: .gitea/workflows/scripts/run-agent.sh
🔧 read: .gitea/workflows/scripts/publish.sh
💬 Now I fully understand the problem. Let me plan the fix.

The core issue: .agents-workflow is checked out via actions/checkout@v4 with path: .agents-workflow, which lands inside ${{ github.workspace }} — the same tree the agent edits. actions/checkout requires the path to be within the workspace, so I can't just point it to /tmp.

Let me check the route.sh script for how it uses the workspace, and check the caller workflow.
🔧 read: .gitea/workflows/scripts/route.sh
🔧 read: .gitea/workflows/ai-agent.yml
💬 Now I have a complete picture. Let me design the fix.

Root cause analysis:

  • .agents-workflow is checked out at path: .agents-workflow inside ${{ github.workspace }} (the caller's tree the agent edits).
  • $SCRIPTS points into it, so all post-agent steps (build-activity-log, publish) depend on files inside the tree the agent can freely mutate.
  • If the agent commits it as a gitlink, deletes it, or otherwise touches it, later steps break with exit 127.

The fix strategy: Two complementary defenses:

  1. Copy the scripts out of the workspace once, right after fetching them, into a stable location outside the git working tree (${{ runner.temp }}/agents-scripts). Point $SCRIPTS there for all subsequent steps. Then the agent editing/deleting .agents-workflow can't affect the scripts the later steps run.

  2. Don't silently strand pushed work: make route.sh push the branch up-front (already done), and ensure that even if a later step fails, the publish path is resilient. But the stronger guarantee is: make the scripts immune, so publish always runs.

Let me also make the workspace robust — restore/clean .agents-workflow before running the git-sensitive publish. Let me check build-activity-log.sh briefly.
🔧 read: .gitea/workflows/scripts/build-activity-log.sh
💬 The activity-log and publish scripts read from /tmp/* (already outside the workspace), so only the script files themselves at $SCRIPTS are vulnerable. The fix is clean: copy scripts out of the workspace and point $SCRIPTS at that copy.

Let me plan the todos and implement.
🔧 todowrite: 4 todos
💬 Now let me implement. The key change: after fetching .agents-workflow, copy its scripts to ${{ runner.temp }}/agents-scripts (outside the workspace/git tree), and repoint every $SCRIPTS to that path.

First, the fetch step + a new copy step. I'll add the copy into the fetch step's flow by making the fetch step followed by a copy-out step.
🔧 edit: .gitea/workflows/agent.yml
💬 Now repoint every $SCRIPTS in downstream steps from ${{ github.workspace }}/.agents-workflow/.gitea/workflows/scripts to ${{ runner.temp }}/agents-scripts.
🔧 bash: grep -n "github.workspace }}/.agents-workflow/.gitea/workflows/scripts" /workspace/ffaerber/agents/.gitea/workflows/agent.yml
💬 Line 66 is the SRC in my new copy step — keep that. Lines with SCRIPTS: (79, 99, 116, 130, 137, 145, 153, 175, 181) must be repointed. Let me replace all the SCRIPTS: assignments.
🔧 bash: cd /workspace/ffaerber/agents && perl -0pi -e 's{SCRIPTS: \$\{\{ github\.workspace \}\}/\.agents-workflow/\.gitea/workflows/scripts}{SCRIPTS: \$\{\{ runner.temp \}\}/agents-scripts}g' .gitea/workflows/agent.yml && grep -n "SCRIPTS:\|SRC:" .…
💬 All repointed. Now the second defense: ensure a mutated .agents-workflow in the tree can't corrupt the publish step's git operations and strand pushed work.

The publish step runs git status --porcelain, git add -A, git push. If the agent committed .agents-workflow as a gitlink or left it as junk, git add -A could stage the checkout dir into the PR. Let me harden publish.sh to scrub the .agents-workflow artifact from the working tree before committing, so it never leaks into the PR and never confuses the git operations.
🔧 todowrite: 2 todos
💬 Now in publish.sh, before the git-commit-and-push block, scrub any .agents-workflow artifact the agent may have left in the tree (whether staged as a gitlink, tracked, or untracked). This keeps it out of the PR and prevents git ops from breaking.
🔧 edit: .gitea/workflows/scripts/publish.sh
💬 Now the third protection: a failed run should not silently strand pushed work with no PR. If a step between run-agent and publish fails (e.g. build-activity-log), the workflow stops and publish never runs — leaving the pushed branch orphaned. Let me make build-activity-log non-fatal (it's already set -u and best-effort with || true internally but could still exit non-zero) and add a failure-safe fallback that opens a PR when the run fails after the agent pushed work.

Let me check: build-activity-log uses set -u (not -e), and every command has || true or || echo. The final head -3 could exit non-zero if file empty but that won't fail. It's already robust. The bigger gap is: if the Run agent step itself or any step fails, publish never runs.

Let me add a failure-safe step (if: failure()) that opens a PR for the branch when the agent already pushed commits — so work is never stranded.
🔧 read: .gitea/workflows/agent.yml
💬 I'll add a failure-safe step right before the "Mark failed" step. It runs only on failure, and only for dev (pr) agents, opening a PR for the pushed branch so work is never stranded. I'll add a small script for it.
🔧 edit: .gitea/workflows/agent.yml
💬 Now create rescue-pr.sh. It must be self-contained and best-effort: check if the branch has commits ahead of main on the remote, and if so open a PR (idempotently, matching head client-side like publish.sh does).
🔧 write: .gitea/workflows/scripts/rescue-pr.sh
💬 Now let me validate: YAML syntax and shell syntax.
🔧 bash: cd /workspace/ffaerber/agents && python3 -c "import yaml,sys; yaml.safe_load(open('.gitea/workflows/agent.yml')); print('agent.yml YAML OK')" && bash -n .gitea/workflows/scripts/rescue-pr.sh && echo "rescue-pr.sh syntax OK" && bash -n .gite…
🔧 bash: cd /workspace/ffaerber/agents && bash -n .gitea/workflows/scripts/rescue-pr.sh && echo "rescue-pr.sh syntax OK" && bash -n .gitea/workflows/scripts/publish.sh && echo "publish.sh syntax OK"; pip install pyyaml -q 2>/dev/null && python3 -c "…
🔧 bash: cd /workspace/ffaerber/agents && command -v yq >/dev/null && yq '.' .gitea/workflows/agent.yml >/dev/null && echo "yq OK"; command -v ruby >/dev/null && ruby -ryaml -e "YAML.load_file('.gitea/workflows/agent.yml'); puts 'ruby YAML OK'" || e…
💬 YAML parses cleanly with yq. Let me set the executable bit on the new script to match the others, and verify.
🔧 bash: cd /workspace/ffaerber/agents && ls -l .gitea/workflows/scripts/ | head && chmod +x .gitea/workflows/scripts/rescue-pr.sh && ls -l .gitea/workflows/scripts/rescue-pr.sh
💬 Now let me review the full diff to make sure everything is consistent.
🔧 bash: cd /workspace/ffaerber/agents && git --no-pager diff --stat && echo "===AGENT.YML DIFF===" && git --no-pager diff .gitea/workflows/agent.yml
💬 The diff looks complete and correct. Let me do a final review of the publish.sh diff.
🔧 bash: cd /workspace/ffaerber/agents && git --no-pager diff .gitea/workflows/scripts/publish.sh
💬 All three defenses are in place. Now let me consider one edge: if the agent already committed .agents-workflow in a prior commit on the branch (already pushed), the git rm --cached here only removes it from the working index for the new commit — the earlier commit still has it. That's acceptable: the scripts run from runner.temp, so the run won't break, and the PR reviewer sees it. Fully rewriting history is out of scope and risky. The scrub prevents new leakage and prevents git ops from failing. Good.

Let me verify the staging step's cp -a "$SRC"/. "$DST"/ works and there are no hidden shell issues with set -eu in the inline step by testing locally.
🔧 bash: cd /tmp && rm -rf srctest dsttest && mkdir -p srctest && cp /workspace/ffaerber/agents/.gitea/workflows/scripts/*.sh srctest/ && SRC=/tmp/srctest DST=/tmp/dsttest bash -c 'set -eu; rm -rf "$DST"; mkdir -p "$DST"; cp -a "$SRC"/. "$DST"/; chm…
💬 The staging step works. Now commit and push.
🔧 bash: cd /workspace/ffaerber/agents && git add .gitea/workflows/agent.yml .gitea/workflows/scripts/publish.sh .gitea/workflows/scripts/rescue-pr.sh && git status && git --no-pager log --oneline -3
💬 Note .agents-workflow/ is untracked in my own working tree here — I must NOT commit it (that's exactly the bug). Only my three intended files are staged. Committing now.
🔧 bash: `cd /workspace/ffaerber/agents && git commit -m "fix(agent): run step scripts from outside the workspace so agents can't break the run

Stage the shared scripts into $RUNNER_TEMP and point $SCRIPTS there for every
step, so an agent that co…💬 Pushed successfully toai/issue-33. 🔧 **todowrite**: 0 todos💬 Done. Implemented a three-layer fix for issue #33 onai/issue-33`.

Root cause: actions/checkout requires its path to be inside the workspace, so .agents-workflow/ (the runtime scripts checkout) necessarily lands inside the tree the dev agent edits. $SCRIPTS pointed into it, so an agent committing it as a gitlink or rm -rfing it as "leftover" destroyed the scripts the post-agent steps run → exit 127, no PR, work stranded.

The fix (.gitea/workflows/agent.yml, publish.sh, new rescue-pr.sh):

  1. Move scripts out of the agent's reach — new "Stage shared scripts outside the workspace" step copies the fetched scripts to ${{ runner.temp }}/agents-scripts, and every downstream step now runs $SCRIPTS from there. Nothing the agent does to the working tree can touch the run's own execution environment.
  2. Scrub the in-tree artifact before publishingpublish.sh now removes any .agents-workflow the agent left (untracked dir, tracked files, or committed gitlink/submodule) so it never leaks into a PR or breaks the git ops. Safe because the real scripts live outside the workspace.
  3. Never silently strand pushed work — new if: failure() "Rescue" step (rescue-pr.sh) opens a PR for the pushed branch when a run fails after commits were pushed, and comments the PR link on the issue. Idempotent and fully best-effort.

I did not commit the stray .agents-workflow/ in my own tree (that's the bug itself). PRs are opened by automation — I did not open one.

BEGIN_PR_DESCRIPTION

Summary

Dev-agent runs could fail at the post-agent steps (No such file or directory, exit 127) with no PR opened, because the shared step scripts are checked out at .agents-workflow/ inside the same working tree the agent edits. An agent that committed it as a gitlink or rm -rf'd it as "leftover" destroyed the scripts the later steps (build-activity-log, publish) run from $SCRIPTS, stranding pushed work on a branch with no PR (issue #33).

This makes the run immune: the scripts now run from a stable location outside the workspace, the in-tree checkout artifact is scrubbed before publishing, and a failure-safe rescue step guarantees pushed work always gets a PR.

Changes

  • agent.yml: new "Stage shared scripts outside the workspace" step copies the fetched scripts to ${{ runner.temp }}/agents-scripts; repointed $SCRIPTS in every downstream step to that out-of-tree path (was github.workspace/.agents-workflow/...).
  • agent.yml: new if: failure() "Rescue" step opens a PR for the pushed branch when a run fails after commits were pushed, so work is never silently stranded.
  • publish.sh: scrub any in-tree .agents-workflow artifact (untracked dir / tracked files / committed gitlink+submodule) before committing, so it never lands in a PR or breaks the git ops.
  • rescue-pr.sh (new): best-effort, idempotent PR-open-on-failure for the pushed branch, posting the PR link back to the issue.
    END_PR_DESCRIPTION
🤖 **@lead** — activity log (105 entries): <details> <summary>tool calls & reasoning</summary> 💬 I'll start by reading AGENTS.md and understanding the current workflow structure. 🔧 **read**: `AGENTS.md` 🔧 **glob**: `` 🔧 **bash**: `git branch --show-current && git status && echo "---FILES---" && find .gitea -type f 2>/dev/null` 🔧 **read**: `.gitea/workflows/agent.yml` 💬 Now I understand the architecture. The core problem: `.agents-workflow` is checked out **inside** `${{ github.workspace }}` (the caller's working tree the agent edits). Let me look at the key scripts to understand what needs to change. 🔧 **read**: `.gitea/workflows/scripts/run-agent.sh` 🔧 **read**: `.gitea/workflows/scripts/publish.sh` 💬 Now I fully understand the problem. Let me plan the fix. The core issue: `.agents-workflow` is checked out via `actions/checkout@v4` with `path: .agents-workflow`, which lands **inside** `${{ github.workspace }}` — the same tree the agent edits. `actions/checkout` requires the path to be within the workspace, so I can't just point it to `/tmp`. Let me check the `route.sh` script for how it uses the workspace, and check the caller workflow. 🔧 **read**: `.gitea/workflows/scripts/route.sh` 🔧 **read**: `.gitea/workflows/ai-agent.yml` 💬 Now I have a complete picture. Let me design the fix. **Root cause analysis:** - `.agents-workflow` is checked out at `path: .agents-workflow` inside `${{ github.workspace }}` (the caller's tree the agent edits). - `$SCRIPTS` points into it, so all post-agent steps (`build-activity-log`, `publish`) depend on files inside the tree the agent can freely mutate. - If the agent commits it as a gitlink, deletes it, or otherwise touches it, later steps break with exit 127. **The fix strategy:** Two complementary defenses: 1. **Copy the scripts out of the workspace once, right after fetching them**, into a stable location outside the git working tree (`${{ runner.temp }}/agents-scripts`). Point `$SCRIPTS` there for all subsequent steps. Then the agent editing/deleting `.agents-workflow` can't affect the scripts the later steps run. 2. **Don't silently strand pushed work**: make `route.sh` push the branch up-front (already done), and ensure that even if a later step fails, the publish path is resilient. But the stronger guarantee is: make the scripts immune, so publish always runs. Let me also make the workspace robust — restore/clean `.agents-workflow` before running the git-sensitive publish. Let me check `build-activity-log.sh` briefly. 🔧 **read**: `.gitea/workflows/scripts/build-activity-log.sh` 💬 The activity-log and publish scripts read from `/tmp/*` (already outside the workspace), so only the **script files themselves** at `$SCRIPTS` are vulnerable. The fix is clean: copy scripts out of the workspace and point `$SCRIPTS` at that copy. Let me plan the todos and implement. 🔧 **todowrite**: `4 todos` 💬 Now let me implement. The key change: after fetching `.agents-workflow`, copy its scripts to `${{ runner.temp }}/agents-scripts` (outside the workspace/git tree), and repoint every `$SCRIPTS` to that path. First, the fetch step + a new copy step. I'll add the copy into the fetch step's flow by making the fetch step followed by a copy-out step. 🔧 **edit**: `.gitea/workflows/agent.yml` 💬 Now repoint every `$SCRIPTS` in downstream steps from `${{ github.workspace }}/.agents-workflow/.gitea/workflows/scripts` to `${{ runner.temp }}/agents-scripts`. 🔧 **bash**: `grep -n "github.workspace }}/.agents-workflow/.gitea/workflows/scripts" /workspace/ffaerber/agents/.gitea/workflows/agent.yml` 💬 Line 66 is the `SRC` in my new copy step — keep that. Lines with `SCRIPTS:` (79, 99, 116, 130, 137, 145, 153, 175, 181) must be repointed. Let me replace all the `SCRIPTS:` assignments. 🔧 **bash**: `cd /workspace/ffaerber/agents && perl -0pi -e 's{SCRIPTS: \$\{\{ github\.workspace \}\}/\.agents-workflow/\.gitea/workflows/scripts}{SCRIPTS: \$\{\{ runner.temp \}\}/agents-scripts}g' .gitea/workflows/agent.yml && grep -n "SCRIPTS:\|SRC:" .…` 💬 All repointed. Now the second defense: ensure a mutated `.agents-workflow` in the tree can't corrupt the publish step's git operations and strand pushed work. The publish step runs `git status --porcelain`, `git add -A`, `git push`. If the agent committed `.agents-workflow` as a gitlink or left it as junk, `git add -A` could stage the checkout dir into the PR. Let me harden `publish.sh` to scrub the `.agents-workflow` artifact from the working tree before committing, so it never leaks into the PR and never confuses the git operations. 🔧 **todowrite**: `2 todos` 💬 Now in `publish.sh`, before the git-commit-and-push block, scrub any `.agents-workflow` artifact the agent may have left in the tree (whether staged as a gitlink, tracked, or untracked). This keeps it out of the PR and prevents git ops from breaking. 🔧 **edit**: `.gitea/workflows/scripts/publish.sh` 💬 Now the third protection: **a failed run should not silently strand pushed work with no PR.** If a step *between* run-agent and publish fails (e.g. build-activity-log), the workflow stops and publish never runs — leaving the pushed branch orphaned. Let me make `build-activity-log` non-fatal (it's already `set -u` and best-effort with `|| true` internally but could still exit non-zero) and add a failure-safe fallback that opens a PR when the run fails after the agent pushed work. Let me check: build-activity-log uses `set -u` (not `-e`), and every command has `|| true` or `|| echo`. The final `head -3` could exit non-zero if file empty but that won't fail. It's already robust. The bigger gap is: **if the Run agent step itself or any step fails, publish never runs.** Let me add a failure-safe step (`if: failure()`) that opens a PR for the branch when the agent already pushed commits — so work is never stranded. 🔧 **read**: `.gitea/workflows/agent.yml` 💬 I'll add a failure-safe step right before the "Mark failed" step. It runs only on failure, and only for dev (pr) agents, opening a PR for the pushed branch so work is never stranded. I'll add a small script for it. 🔧 **edit**: `.gitea/workflows/agent.yml` 💬 Now create `rescue-pr.sh`. It must be self-contained and best-effort: check if the branch has commits ahead of main on the remote, and if so open a PR (idempotently, matching head client-side like publish.sh does). 🔧 **write**: `.gitea/workflows/scripts/rescue-pr.sh` 💬 Now let me validate: YAML syntax and shell syntax. 🔧 **bash**: `cd /workspace/ffaerber/agents && python3 -c "import yaml,sys; yaml.safe_load(open('.gitea/workflows/agent.yml')); print('agent.yml YAML OK')" && bash -n .gitea/workflows/scripts/rescue-pr.sh && echo "rescue-pr.sh syntax OK" && bash -n .gite…` 🔧 **bash**: `cd /workspace/ffaerber/agents && bash -n .gitea/workflows/scripts/rescue-pr.sh && echo "rescue-pr.sh syntax OK" && bash -n .gitea/workflows/scripts/publish.sh && echo "publish.sh syntax OK"; pip install pyyaml -q 2>/dev/null && python3 -c "…` 🔧 **bash**: `cd /workspace/ffaerber/agents && command -v yq >/dev/null && yq '.' .gitea/workflows/agent.yml >/dev/null && echo "yq OK"; command -v ruby >/dev/null && ruby -ryaml -e "YAML.load_file('.gitea/workflows/agent.yml'); puts 'ruby YAML OK'" || e…` 💬 YAML parses cleanly with `yq`. Let me set the executable bit on the new script to match the others, and verify. 🔧 **bash**: `cd /workspace/ffaerber/agents && ls -l .gitea/workflows/scripts/ | head && chmod +x .gitea/workflows/scripts/rescue-pr.sh && ls -l .gitea/workflows/scripts/rescue-pr.sh` 💬 Now let me review the full diff to make sure everything is consistent. 🔧 **bash**: `cd /workspace/ffaerber/agents && git --no-pager diff --stat && echo "===AGENT.YML DIFF===" && git --no-pager diff .gitea/workflows/agent.yml` 💬 The diff looks complete and correct. Let me do a final review of the publish.sh diff. 🔧 **bash**: `cd /workspace/ffaerber/agents && git --no-pager diff .gitea/workflows/scripts/publish.sh` 💬 All three defenses are in place. Now let me consider one edge: if the agent already **committed** `.agents-workflow` in a *prior* commit on the branch (already pushed), the `git rm --cached` here only removes it from the working index for the *new* commit — the earlier commit still has it. That's acceptable: the scripts run from `runner.temp`, so the run won't break, and the PR reviewer sees it. Fully rewriting history is out of scope and risky. The scrub prevents *new* leakage and prevents git ops from failing. Good. Let me verify the staging step's `cp -a "$SRC"/. "$DST"/` works and there are no hidden shell issues with `set -eu` in the inline step by testing locally. 🔧 **bash**: `cd /tmp && rm -rf srctest dsttest && mkdir -p srctest && cp /workspace/ffaerber/agents/.gitea/workflows/scripts/*.sh srctest/ && SRC=/tmp/srctest DST=/tmp/dsttest bash -c 'set -eu; rm -rf "$DST"; mkdir -p "$DST"; cp -a "$SRC"/. "$DST"/; chm…` 💬 The staging step works. Now commit and push. 🔧 **bash**: `cd /workspace/ffaerber/agents && git add .gitea/workflows/agent.yml .gitea/workflows/scripts/publish.sh .gitea/workflows/scripts/rescue-pr.sh && git status && git --no-pager log --oneline -3` 💬 Note `.agents-workflow/` is untracked in my own working tree here — I must NOT commit it (that's exactly the bug). Only my three intended files are staged. Committing now. 🔧 **bash**: `cd /workspace/ffaerber/agents && git commit -m "fix(agent): run step scripts from outside the workspace so agents can't break the run Stage the shared scripts into \$RUNNER_TEMP and point \$SCRIPTS there for every step, so an agent that co…` 💬 Pushed successfully to `ai/issue-33`. 🔧 **todowrite**: `0 todos` 💬 Done. Implemented a three-layer fix for issue #33 on `ai/issue-33`. **Root cause:** `actions/checkout` requires its `path` to be inside the workspace, so `.agents-workflow/` (the runtime scripts checkout) necessarily lands *inside* the tree the dev agent edits. `$SCRIPTS` pointed into it, so an agent committing it as a gitlink or `rm -rf`ing it as "leftover" destroyed the scripts the post-agent steps run → exit 127, no PR, work stranded. **The fix** (`.gitea/workflows/agent.yml`, `publish.sh`, new `rescue-pr.sh`): 1. **Move scripts out of the agent's reach** — new "Stage shared scripts outside the workspace" step copies the fetched scripts to `${{ runner.temp }}/agents-scripts`, and every downstream step now runs `$SCRIPTS` from there. Nothing the agent does to the working tree can touch the run's own execution environment. 2. **Scrub the in-tree artifact before publishing** — `publish.sh` now removes any `.agents-workflow` the agent left (untracked dir, tracked files, or committed gitlink/submodule) so it never leaks into a PR or breaks the git ops. Safe because the real scripts live outside the workspace. 3. **Never silently strand pushed work** — new `if: failure()` "Rescue" step (`rescue-pr.sh`) opens a PR for the pushed branch when a run fails after commits were pushed, and comments the PR link on the issue. Idempotent and fully best-effort. I did not commit the stray `.agents-workflow/` in my own tree (that's the bug itself). PRs are opened by automation — I did not open one. BEGIN_PR_DESCRIPTION ## Summary Dev-agent runs could fail at the post-agent steps (`No such file or directory`, exit 127) with no PR opened, because the shared step scripts are checked out at `.agents-workflow/` **inside** the same working tree the agent edits. An agent that committed it as a gitlink or `rm -rf`'d it as "leftover" destroyed the scripts the later steps (`build-activity-log`, `publish`) run from `$SCRIPTS`, stranding pushed work on a branch with no PR (issue #33). This makes the run immune: the scripts now run from a stable location **outside** the workspace, the in-tree checkout artifact is scrubbed before publishing, and a failure-safe rescue step guarantees pushed work always gets a PR. ## Changes - **agent.yml**: new "Stage shared scripts outside the workspace" step copies the fetched scripts to `${{ runner.temp }}/agents-scripts`; repointed `$SCRIPTS` in every downstream step to that out-of-tree path (was `github.workspace/.agents-workflow/...`). - **agent.yml**: new `if: failure()` "Rescue" step opens a PR for the pushed branch when a run fails after commits were pushed, so work is never silently stranded. - **publish.sh**: scrub any in-tree `.agents-workflow` artifact (untracked dir / tracked files / committed gitlink+submodule) before committing, so it never lands in a PR or breaks the git ops. - **rescue-pr.sh** (new): best-effort, idempotent PR-open-on-failure for the pushed branch, posting the PR link back to the issue. END_PR_DESCRIPTION </details>
ffaerber force-pushed ai/issue-33 from d1b98e7975 to 423a060816 2026-07-04 09:43:04 +02:00 Compare
ffaerber merged commit d0a4de9682 into main 2026-07-04 09:43:14 +02:00
Sign in to join this conversation.
No Reviewers
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: gitea/agents#34