headless --repo: a secondary-repo push can silently fail while the run closes the issue as done #291

Closed
opened 2026-06-24 17:58:59 +00:00 by coilyco-ops · 2 comments
Member

Symptom

A headless run with a --repo grant can push its primary repo, fail to land the granted secondary repo, and still close the issue reporting both as done - silently losing the secondary repo's committed work.

Evidence (#286)

That run was dispatched ward agent headless coilyco-flight-deck/ward#286 --repo coilyco-bridge/agentic-os-kai. Its closing retro states "I pushed both repos through a GIT_ASKPASS shim." But on origin/main of coilyco-bridge/agentic-os-kai:

  • .agents/skills/tooling-ward-agent/ (SKILL.md + references) is still present - the removal never landed.
  • scripts/mount-skills.sh has no aggregate_authored_skills aggregation - the mount wiring never landed.

The ward-side change landed and the issue closed. So the cross-repo half is lost (container reaped), the skill is now duplicated (ward + agentic-os-kai), and the mount still serves the stale copy. The issue reads "done."

Likely cause

The secondary push rode the same fragile path forced by #288 (the /etc/ward-git-credentials 0600 root:root clobber -> GIT_ASKPASS/FORGEJO_TOKEN shim), and/or hit a non-fast-forward on the busy agentic-os-kai main. Either way the secondary push was rejected/failed while the run treated the successful primary push as the whole done-condition.

Fix

  • A headless run holding --repo grants must verify each granted repo's push actually landed on its target branch (compare local HEAD to the freshly-fetched remote ref) before closing the issue. A failed/rejected secondary push is a hard error -> comment + leave the issue open, never a silent success.
  • The seeded done-condition should name "every granted repo pushed and verified," not just the issue's own repo.
  • Guidance: when a --repo grant exists only to push a second repo, prefer filing that work as a native issue in that repo (single-repo, no cross-repo push) - it sidesteps this failure mode entirely.

Cross-refs

  • #288 - the credential clobber forcing the fragile push path (also recurred on this run).
  • #286 - the half-landed relocation this surfaced from; its agentic-os-kai side still needs to land.
## Symptom A headless run with a `--repo` grant can push its **primary** repo, fail to land the **granted secondary** repo, and still close the issue reporting both as done - silently losing the secondary repo's committed work. ## Evidence (https://forgejo.coilysiren.me/coilyco-flight-deck/ward/issues/286) That run was dispatched `ward agent headless coilyco-flight-deck/ward#286 --repo coilyco-bridge/agentic-os-kai`. Its closing retro states "I pushed both repos through a GIT_ASKPASS shim." But on `origin/main` of `coilyco-bridge/agentic-os-kai`: - `.agents/skills/tooling-ward-agent/` (SKILL.md + references) is **still present** - the removal never landed. - `scripts/mount-skills.sh` has **no** `aggregate_authored_skills` aggregation - the mount wiring never landed. The ward-side change landed and the issue closed. So the cross-repo half is lost (container reaped), the skill is now duplicated (ward + agentic-os-kai), and the mount still serves the stale copy. The issue reads "done." ## Likely cause The secondary push rode the same fragile path forced by https://forgejo.coilysiren.me/coilyco-flight-deck/ward/issues/288 (the `/etc/ward-git-credentials` `0600 root:root` clobber -> `GIT_ASKPASS`/`FORGEJO_TOKEN` shim), and/or hit a non-fast-forward on the busy `agentic-os-kai` main. Either way the secondary push was rejected/failed while the run treated the successful **primary** push as the whole done-condition. ## Fix - A headless run holding `--repo` grants must **verify each granted repo's push actually landed** on its target branch (compare local HEAD to the freshly-fetched remote ref) before closing the issue. A failed/rejected secondary push is a hard error -> comment + leave the issue open, never a silent success. - The seeded done-condition should name "every granted repo pushed and verified," not just the issue's own repo. - Guidance: when a `--repo` grant exists only to push a second repo, prefer filing that work as a **native issue in that repo** (single-repo, no cross-repo push) - it sidesteps this failure mode entirely. ## Cross-refs - https://forgejo.coilysiren.me/coilyco-flight-deck/ward/issues/288 - the credential clobber forcing the fragile push path (also recurred on this run). - https://forgejo.coilysiren.me/coilyco-flight-deck/ward/issues/286 - the half-landed relocation this surfaced from; its agentic-os-kai side still needs to land.
Author
Member

🔒 Reserved by ward agent --driver claude — container ward-ward-issue-291-claude-2fcda570 on host kais-macbook-pro-2.local is carrying this issue (reserved 2026-06-25T07:28:12Z). Concurrent ward agent runs are blocked until it finishes or the reservation goes stale (2h0m0s TTL); --force overrides.

— Claude (she/her), via ward agent

<!-- ward-agent-reservation --> 🔒 Reserved by `ward agent --driver claude` — container `ward-ward-issue-291-claude-2fcda570` on host `kais-macbook-pro-2.local` is carrying this issue (reserved 2026-06-25T07:28:12Z). Concurrent `ward agent` runs are blocked until it finishes or the reservation goes stale (2h0m0s TTL); `--force` overrides. <!-- ward-agent-signature --> — Claude (she/her), via `ward agent`
Owner

Retro from the agent who carried this.

The fix split cleanly in two once I mapped the flow: a prevent half (the seeded done-condition now names "every granted repo pushed AND verified landed" plus the steer toward a native single-repo issue) and a backstop half (the reaper reads WARD_EXTRA_REPOS, fetches each grant, and if a clone's HEAD never reached origin/main it preserves the work on a salvage branch and reopens the issue with a recovery comment). The asymmetry the doctrine called out - reaper backstops only the target - was the right seam to widen: it now verifies grants, it still won't land them for you.

What fought back was mundane: the three docs I needed to touch were already pegged at the 4000-char cap, so every sentence I added had to be paid for by trimming existing prose, several rounds of clawing characters back. And trufflehog was not installed here, so rather than skip the secret scan I fetched the release binary to run the gate for real. Two rounds of merge conflicts as concurrent containers advanced main (agent.go and FEATURES.md), both just "keep both independent additions."

Confidence: high on the seed half (pure, well-tested). Medium-high on the reaper verification - the rendering and env-parse are unit-tested, but the live fetch/push/reopen path is not exercised end-to-end in a real container from here.

One rough edge worth a follow-up: the landed-check is origin/main..HEAD > 0, so an agent that amends or rebases a grant after a successful push could trip a false "did not land". Erring toward surfacing is the safe default, but if it proves noisy a tighter "is HEAD's tree already on main" check would be the fix.

  • Claude (she/her), via ward agent
Retro from the agent who carried this. The fix split cleanly in two once I mapped the flow: a **prevent** half (the seeded done-condition now names "every granted repo pushed AND verified landed" plus the steer toward a native single-repo issue) and a **backstop** half (the reaper reads `WARD_EXTRA_REPOS`, fetches each grant, and if a clone's HEAD never reached `origin/main` it preserves the work on a salvage branch and reopens the issue with a recovery comment). The asymmetry the doctrine called out - reaper backstops only the target - was the right seam to widen: it now *verifies* grants, it still won't *land* them for you. What fought back was mundane: the three docs I needed to touch were already pegged at the 4000-char cap, so every sentence I added had to be paid for by trimming existing prose, several rounds of clawing characters back. And `trufflehog` was not installed here, so rather than skip the secret scan I fetched the release binary to run the gate for real. Two rounds of merge conflicts as concurrent containers advanced `main` (agent.go and FEATURES.md), both just "keep both independent additions." Confidence: high on the seed half (pure, well-tested). Medium-high on the reaper verification - the rendering and env-parse are unit-tested, but the live fetch/push/reopen path is not exercised end-to-end in a real container from here. One rough edge worth a follow-up: the landed-check is `origin/main..HEAD > 0`, so an agent that amends or rebases a grant *after* a successful push could trip a false "did not land". Erring toward surfacing is the safe default, but if it proves noisy a tighter "is HEAD's tree already on main" check would be the fix. - Claude (she/her), via `ward agent`
Sign in to join this conversation.
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
coilyco-flight-deck/ward#291
No description provided.