Repository navigation
Harness fixes: standing-goal Stop hook, Read-path guard, comment-guard fixups, Windows notifications, fleet status - #1
Merged
Merged
Conversation
…awns DEV-44/DEV-45: zj-claude.sh spawned autonomous-goal workers with no mechanism to enforce the goal to completion, so they parked at the next ordinary turn boundary with the goal unmet. --keep-going installs a session-scoped Stop hook (keep-going-hook.sh) that blocks the session from stopping until it creates .claude/goal-done, bounded by a max continuation count (KEEP_GOING_MAX, default 40) so a broken done-when signal can't loop forever. A human aborts early via .claude/goal-stop. Opt-in per spawn so a one-shot ask still stops normally. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EAvmyrEwiqgQhbaA3w4TsM
zellaude-hook.sh's PermissionRequest notification path had a Darwin branch (terminal-notifier/osascript) and a Linux branch (notify-send) but no Windows branch at all - on Windows a blocking permission request produced only a terminal bell and nothing else surfaced it. Replaced both platform branches with a single OSC9 (ConEmu-style) toast escape sequence written to /dev/tty. zellij passes unrecognised OSC sequences through to the host terminal, and OSC9 is understood by Windows Terminal, iTerm2, and others, so this covers all three platforms with one code path instead of two platform-specific ones that silently degrade on a third. Unfocused mode previously used osascript/xdotool frontmost-window checks with no Windows equivalent, so it silently never fired there. The ticket's proposed fix (read zellij's forwarded host focus state) needs zellij 0.45 (PR 5485), not yet released - installed/available via scoop is still 0.44.3. Rather than ship a focus check that can't work yet, Unfocused now falls back to notifying Always with a once-per-pane stderr warning, so the degradation is visible instead of silent (which was the original bug). Revisit when 0.45 ships. Click-to-focus (the terminal-notifier -execute callback) is dropped: OSC9 carries no callback. Tracked separately as DEV-12. Tested by invoking the hook directly with crafted stdin (Always mode, Unfocused mode incl. one-shot warning, rate-limit window, a non-PermissionRequest event, and malformed JSON for fail-open) rather than relying on live firing in this session. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EAvmyrEwiqgQhbaA3w4TsM
…jects/<key>/ (DEV-39)
The existing CLAUDE.md prose rule ("confirm a path is relative to the
repo root, not ~/.claude/projects/<key>/") did not change behaviour:
the emergent-tools digest shows 78 occurrences across 53 sessions of
Read failing on a path resolved against the transcript-store directory
instead of the actual repo. A classification pass correlating
tool_use/tool_result pairs across all local transcripts (not just the
5 examples in the digest) found this class dominates at 44% of the 78
occurrences, ahead of genuinely-absent paths (35%), POSIX /tmp on
Windows (10%), and tool-artifact-assumed-to-exist (5%) - the latter
two are out of scope here and noted as follow-ups rather than folded
into this fix.
Since prose guidance can't compete with a wrong path already in
context, this adds a PreToolUse hook (Read|Edit|Write|Glob|Grep) that
detects a path routed through .claude/projects/<key>/, computes the
likely intended path relative to the real cwd, and denies with the
suggested correction instead of letting the tool fail with a bare
"File does not exist."
Tested by invoking the hook directly with crafted stdin (misresolved
path denied with correct suggestion, normal path passes, Glob's
`path` field denied, a Bash tool call with no path field passes, and
a real Windows-backslash-style payload denied) - see
fix-project-path-hook.test.sh, 5/5 passing.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EAvmyrEwiqgQhbaA3w4TsM
zellaude-hook.sh already emits a JSON payload on every hook event but only forwards it to the zellaude zellij plugin - nothing aggregates it across workers. Sink each event into a shared JSON map (keyed by pane id) at ~/.config/zellij/plugins/zellaude-fleet-state.json (or the Windows AppData equivalent, matching the existing settings-file lookup pattern), guarded by a short mkdir-based lock since many worker panes write the same file concurrently. zj-fleet-status.sh reads that map and prints pane id, ticket slug (derived from the worktree dirname), last hook event, seconds idle, and PR state via `gh pr list --head wt/<slug>` (cached 30s so a run doesn't hammer the API once per pane). Plain script + on-demand floating pane, per the ticket's own steer away from a third wasm plugin when a script gets most of the value. zj-fleet-status.test.sh drives both scripts directly with crafted hook stdin (never live firing): upsert creates/overwrites per-pane entries without disturbing others, the renderer picks them up, and both a missing and a malformed state file degrade sanely. 9/9 passing. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EAvmyrEwiqgQhbaA3w4TsM
TAJD
added a commit
that referenced
this pull request
Sep 6, 2026
fix(zj-claude): port --keep-going onto the current script, restoring trust pre-accept PR #1 was authored against an older zj-claude.sh. Merging it would have reverted three fixes that were live in the chezmoi target: workspace-trust pre-acceptance, the post-spawn transcript verification, and the @file prompt-mutation fix. Losing trust pre-accept is the worst of these - the trust dialog swallows the prompt auto-submit and the session hangs looking alive. Re-added the target as the source of truth, then reapplied the --keep-going feature on top of it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019dYTG2kvngJ875otpDRVHm @
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Closes out the DEV harness-bug standing goal, in priority order:
zj-claude.sh --keep-goingnow installs a session-scoped Stop hook (keep-going-hook.sh) that blocks stop until.claude/goal-doneexists (bounded byKEEP_GOING_MAX, default 40, with a.claude/goal-stophuman abort).gh pr create). Verified directly against the transcript: 3gh pr createcalls (not 1), the PR link is present right after the 3rd, and the queue was fully drained at the last recorded timestamp — the original "stalled" diagnosis doesn't hold up against the actual transcript. Closed as a false stall diagnosis; no code change.~/.claude/projects/<key>/(the transcript store) instead of the repo root, 78 times across 53 sessions per the emergent-tools digest. A prose CLAUDE.md rule wasn't working;fix-project-path-hook.sh(PreToolUse) now blocks and rewrites the suggestion directly, fails open on malformed input.stop-writing-commentshook (separate repo/PR, see below) had 4 false-positive/negative classes: regex literals misread as//comments, bare HTML placeholders flagged as prose, git-plumbing Bash commands falsely audited, and comment restores/moves permanently denied. Fixed as one cluster with one test suite.zellaude-hook.sh's notification path had Darwin/Linux branches and nothing for Windows. Replaced both with a single OSC9 escape (ESC ] 9 ; <text> ESC \) written to/dev/tty, which zellij passes through to the host terminal.Unfocusedmode's proper fix needs zellij 0.45 (PR 5485, not yet released — confirmed viazellij --version/scoop info zellij, both 0.44.3); it now falls back toAlwayswith a one-time stderr warning instead of silently never firing.zellaude-hook.shalready emitted a JSON payload per hook event but never aggregated it. It now upserts into a shared state file (~/.config/zellij/plugins/zellaude-fleet-state.json, mkdir-lock guarded);zj-fleet-status.shrenders one line per worker (pane, ticket slug, event, idle seconds, PR state via cachedgh pr list).The comment-guard fixes (DEV-28/41/43/49) shipped as a separate PR in the
stop-writing-commentsrepo, now CI-green on all 6 platform/Node combinations: TAJD/stop-writing-comments#1Test plan
keep-going-hook.test.sh— 4/4 (block-until-done, goal-done allows stop, goal-stop aborts, max-tries exhaustion allows stop)fix-project-path-hook.test.sh— 5/5 (misresolved paths corrected, normal paths pass silently, Globpathvariant, Bash-with-no-path passes, Windows-backslash variant), plus a fail-open fix for malformed JSON input verified before/afterzellaude-hook.shOSC9 notification path exercised directly with crafted stdin: Always mode, Unfocused fallback + one-shot warning, 10s rate limit, non-PermissionRequestevents, malformed JSON fail-openzj-fleet-status.test.sh— 9/9 (state upsert, overwrite-in-place, multi-pane render, missing/malformed state file handling)HUMAN CHECK
PermissionRequestin a zellij pane on Windows Terminal (run Claude Code without--dangerously-skip-permissions, let it ask for a tool). Expect: bell + a "Claude Code — Permission requested" toast within ~30s, suppressed on a second request inside 10s. This is the one thing I could not visually verify from this session — I confirmed the OSC9 byte sequence is well-formed and zellij 0.44.3 doesn't error on it, but not that a toast actually renders.--keep-going) — spawn a throwaway worker withbash ~/.claude/scripts/zj-claude.sh --keep-going test-tab <dir> "say hi then try to stop"and confirm it gets re-prompted instead of ending, thentouch .claude/goal-donein its worktree and confirm it's allowed to stop.dc6386d,d8608a3,68c0309,4d5fd70);git revert <sha>on any one of them cleanly undoes just that fix. No settings.json changes beyond the one new PreToolUse hook entry for DEV-39 (fix-project-path-hook.sh), which is additive and itself fails open.🤖 Generated with Claude Code
https://claude.ai/code/session_01EAvmyrEwiqgQhbaA3w4TsM