Skip to content

devkit-stop-guard fires across repos — hook is session-scoped, should be repo/branch-scoped #91

Description

@5uck1ess

Problem

`hooks/devkit-stop-guard.sh` (and by extension `devkit-engine guard --stop`) nags on every turn of every Claude Code conversation whenever any workflow session is active — regardless of which repo the user is currently working in. Once you start a `pr-ready` (or any other workflow) in repo A and the session isn't driven to completion, opening an unrelated conversation in repo B still fires:

```
Workflow pr-ready incomplete — 1 steps remaining. Call devkit_advance to continue.
```

The workflow session ID and state live in `~/.claude/` (plugin state dir), not in the repo that started it. So the hook has no way to tell "this nag applies here" vs "this nag belongs to a different project's stuck workflow."

Repro

  1. Start any devkit workflow in repo A (`/devkit:pr-ready`, `/devkit:feature`, etc.).
  2. Advance partway but don't reach the final step (e.g., close the terminal during CI monitoring).
  3. Open a fresh Claude Code session in an unrelated repo B (or even no repo).
  4. Every Stop event in repo B is blocked with the nag from repo A's workflow.

Why it's wrong

  • Workflows are repo-scoped in intent: they operate on a branch, a PR, a diff.
  • The nag is useful when you're still in the repo/branch that started the workflow — it keeps you from forgetting to finish.
  • The nag is pure noise when you've cd'd out or context-switched — it blocks unrelated work.
  • There's also no stale-session timeout, so a forgotten workflow from last week still fires today.

Proposed fix

Make the guard repo-aware. On session start, the workflow state should record:

  • `cwd` (or resolved git repo root) at start time
  • branch name at start time
  • start timestamp

Then `devkit-engine guard --stop` should:

  1. Read the current `CLAUDE_PROJECT_DIR` (or resolve the active repo root from `pwd`).
  2. Read the active session's recorded repo root.
  3. Approve silently if they don't match — the workflow is not relevant to this session.
  4. Block (current behavior) only when the repo roots match.
  5. Optionally: approve silently for sessions older than a configurable TTL (`DEVKIT_WORKFLOW_STALE_HOURS`, default 24h), logging a stderr warning about the stale session.

Same thinking for branch: if the current branch differs from the one recorded at workflow start, either warn-approve or block — TBD. Repo-scoping alone fixes 95% of the pain.

Behavior to preserve

  • Same nag firing when you ARE in the right repo on the right branch with an active workflow — that's useful.
  • No regression when `CLAUDE_PROJECT_DIR` is unset or the session state is missing — continue to approve (as the hook already does on missing binary).

Touch points

  • `src/mcp/server.go` — record cwd + branch when `devkit_start` is called (likely already tracks cwd somewhere in `src/lib/state.go`).
  • `src/cmd/guard.go` — extend the stop-guard decision with repo match check.
  • `src/lib/state.go` / `src/lib/state_json.go` — add fields if not already present.
  • `hooks/devkit-stop-guard.sh` — no change needed; wrapper stays the same.
  • Tests: `src/cmd/guard_test.go` table — add cases for mismatched cwd → approve.

Context

Surfaced while running `/devkit:pr-ready` for PR #90 (local runner + stealth scraping). The CI monitor step held the session open; the Stop hook fired on every conversational turn of unrelated work in other directories for the duration.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions