fix(tick): don't ship a partial worktree when claude exits nonzero (igor#305) #307
No reviewers
Labels
No labels
Agent
Compat/Breaking
Kind/Bug
Kind/Documentation
Kind/Enhancement
Kind/Feature
Kind/Security
Kind/Testing
Priority
Critical
Priority
High
Priority
Low
Priority
Medium
Reviewed
Confirmed
Reviewed
Duplicate
Reviewed
Invalid
Reviewed
Won't Fix
Status
Abandoned
Status
Blocked
Status
Need More Info
No milestone
No assignees
2 participants
Notifications
Total time spent: 2 minutes 30 seconds
Due date
igor
2 minutes 30 seconds
No due date set.
Dependencies
No dependencies set
Reference
joshtronic/igor!307
Loading…
Reference in a new issue
No description provided.
Delete branch "agent/305-ship-safety-gate"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
What this PR does
The bug (igor#305)
tick.shcapturedCLAUDE_EXITand logged it, then never checked it. The issue-work ship path committed and shipped whatever was dirty in the worktree regardless of exit code. On porksicle.com#110, claude implemented the change, rangit stashto capture "before" screenshots, then exited 1 while the stash was still active -- beforegit stash pop. The harness committed the only visible file (a leftover scratch screenshot script) and auto-merged PR #114, silently dropping the real edit and falsely resolving the issue.The fix
Gate the harness commit on a clean exit. If
CLAUDE_EXITis nonzero the run died mid-workflow and the leftover tree is not a trustworthy diff, so we refuse to commit it. Safe because:origin/<base>(git worktree add -B) and torn down by the EXIT trap -- nothing durable is lost by not committing;Status/Blockedguard, so it can't loop.The happy path (clean exit) is byte-for-byte unchanged -- the original commit block just moves into the
elsebranch.The Haiku red herring (igor#306)
The PR-body/commit-subject fallback has called
$AGENT_MODEL(Sonnet), not Haiku, sinceAGENT_MODEL_THINKINGwas retired -- only the log string still said "Haiku." That false log is what tricked the logwatch pass into filing #306. Fixed the log lines + stale comments; no model change.Test plan
bin/check-sync.shpasses (outcome-sync contract intact -- no new outcome; the nonzero-exit path reuses the existing noop/blocked handling)bash -n bin/tick.shcleanNot in this PR (tracked follow-ups)
chore: add script to capture before screenshotscommit from porksicle master + reopen porksicle#110 (separate repo, own PR flow). Keeping igor#305 open until that lands.ideation-pipeline.sh(same cosmetic pattern, different subsystem) -- trivial sweep, left out to keep this P0 focused.Refs igor#305, igor#306.
🤖 Review —
COMMENT(automated)CI for
bd383e7a: successSummary: Small, well-reasoned harness fix: gates the issue-work harness commit on a clean
CLAUDE_EXIT, so a nonzero (mid-workflow) claude exit no longer commits/ships a partial worktree; plus cosmetic "Haiku"→$AGENT_MODELlog/comment corrections. CI is green, the diff is ~40 lines, and the checklist matches what's in the diff. I found no blocking defect, but the correctness of the fix hinges on things that live outside this diff, and the author explicitly asks a human to confirm the intent — so I'm keeping a human in the loop rather than rubber-stamping.Checklist honesty: Both checked items are real and present in the diff (
bin/tick.shgate + the log/comment string swaps). No fabricated claims.What looks right:
git add -A/derive_commit_subject/git commitblock is moved unchanged into theelsebranch. Good, minimal blast radius.Status/Blockedloop guard) is coherent and the discard-on-nonzero-exit tradeoff is defensible.Things I can't verify from the diff (why this isn't an APPROVE):
bin/tick.sh:3864—[ "$CLAUDE_EXIT" -ne 0 ]assumesCLAUDE_EXITis set and numeric at this point. If it can ever be empty here, the arithmetic test errors (integer expression expected), which underset -e/errexitcould abort or, worse, silently fall through to the commit branch — defeating the fix. The PR says it's captured-and-logged upstream, which is plausible, but the assignment isn't in the diff. Worth a human confirming it's unconditionally set before this line.$AGENT_MODELlog-string references at lines ~3996/3999 also assume that var is in scope there — again plausible but not shown.Test coverage: No automated test exercises the new branch — the test plan is
bash -n(syntax only) +check-sync.sh. For a ship-critical harness gate, a regression guard (even a shell-level unit that asserts "no commit whenCLAUDE_EXIT=1and tree dirty") would be worth it, though I recognize this subsystem may not have an easy test seam.Scope note (non-blocking): This bundles igor#306 (cosmetic log fixes) into an igor#305 P0 fix. Small and closely related, so acceptable, but strictly it's two issues in one PR.
For the human reviewer: confirm (1)
CLAUDE_EXITis always set/numeric by the time control reaches line 3864, and (2) the intent tradeoff the author flagged — a nonzero exit should never ship even if it occasionally discards legit-but-incomplete work.Independent review by the harness on
claude-opus-4-8. The human reviewer is requested once Igor has reviewed; a human still merges.Addressed the review. Good catch on the
CLAUDE_EXITfail-open — hardened the gate to${CLAUDE_EXIT:-1}(21acf2e) so an unset/empty value fails safe (treated as a crash, refuses to ship) instead of falling through to the commit branch. The other two flags I verified off-diff: the fresh-per-tick worktree is branched fromorigin/<base>, so a skipped commit leavesCOMMITS=0and the ship path is never entered;AGENT_MODELis a global in scope at the fallback. A shell-level regression test is a fair ask but needs a test seam extracted from the inline ship path — noting it as a follow-up rather than refactoring the critical path inside a P0.🤖 Review —
COMMENT(automated)CI for
21acf2e7: successSmall, focused change to
bin/tick.shthat (1) gates the harness sweep-commit on a cleanCLAUDE_EXITso a run that dies mid-workflow can't ship a partial/scratch worktree, and (2) corrects stale "Haiku" log strings/comments. The reasoning is sound and the checklist is honest — but a couple of things I can't verify from the diff, plus the author explicitly asks a human to confirm the intent, so I'm keeping this in front of you rather than approving.What checks out
[ "${CLAUDE_EXIT:-1}" -ne 0 ]→0commits (else branch, byte-for-byte the old block), nonzero/unset skips. The:-1fail-safe default is a good call.Things I'd want you to weigh (can't confirm from the diff)
COMMITS=0and control "falls through to the noop/blocked path," and that the dirty worktree is torn down by the EXIT trap with "nothing durable lost." The diff at line ~3861 doesn't show the outcome-determination block, the EXIT trap, or the worktree teardown. In particular: does teardowngit worktree remove --force? A left-dirty worktree that the old (commit) path always cleaned could now fail a non-forced removal or collide with next tick'sgit worktree add -B. I can't confirm either way — worth a glance.bin/check-sync.sh+bash -nonly cover contract-sync and syntax, not the new branch. If there's genuinely no harness for testing tick.sh runtime behavior, fine — but the core safety property is going in untested.Minor / non-blocking
$CLAUDE_EXIT("...claude exited $CLAUDE_EXIT mid-run...") while the gate uses${CLAUDE_EXIT:-1}. IfCLAUDE_EXITis ever unset, the log prints an empty value ("exited mid-run") even though the gate correctly treated it as a crash. Cosmetic.No blocking defect found, but the unverifiable downstream/teardown behavior, the absent test on a P0 ship path, and the author's own request for intent sign-off are enough that a human should take the final look.
Independent review by the harness on
claude-opus-4-8. The human reviewer is requested once Igor has reviewed; a human still merges.