fix: scope stop-guard to originating repo + force tri-* dispatch - #92
Merged
Conversation
Record repoRoot on devkit_start and approve Stop silently when the current Claude Code session's repo differs. Block remains active in the originating repo — no TTL escape, no branch carve-out, no env override. Empty state.RepoRoot (pre-#91) and missing CLAUDE_PROJECT_DIR fall through to the legacy block so a missing signal never silently approves.
Prepend a DISPATCH-DO-NOT-ANSWER preamble to every tier step in
tri-review / tri-debug / tri-security / tri-dispatch so the calling
model cannot silently roleplay all three tiers when the workflow runs
via MCP. The previous phrasing ("You are the senior reviewer. Review
this code thoroughly:") read as a role assignment to Claude instead of
a dispatch instruction, defeating the multi-model property of tri-*.
Each tier now names a concrete subagent to dispatch to
(codex:codex-rescue for the smart tier, Gemini or a distinct external
for general/fast) and mandates stopping if no external subagent is
available rather than silently answering as the caller.
Fills three gaps from PR review: - TestGuardStopHookRepoScopeWalkUp: exercises the pwd-walk fallback in currentRepoRoot() (existing suite always sets CLAUDE_PROJECT_DIR so the .git-finding loop never ran). Includes the nested-subdir case. - TestGuardStopHookRepoScopeSymlink: proves samePath() actually uses EvalSymlinks — a symlinked path pointing at the originating repo must compare equal. Regression guard for macOS /var → /private/var. - TestGuardStopHookRepoScopeWorktree: pins worktree support where .git is a FILE (gitlink), not a directory. devkit itself runs from worktrees — a future IsDir() refactor would silently bypass the block for every worktree user without this test. go test ./... → 321 passed (was 316).
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.
Closes #91
Summary
Scope the
devkit-stop-guardincomplete-workflow nag to the repo the workflow was started in. Opening a Claude Code session in a different repo now sees an approve verdict instead of blocking on a workflow that doesn't belong to that project. The block remains active when the user returns to the originating repo — scope restriction, not a bypass.Changes
src/lib/state_json.go— addRepoRootfield toSessionState(JSONrepo_root, omitempty).src/mcp/tools.go— records.repoRootinto state ondevkit_start.src/cmd/guard.go— addcurrentRepoRoot()(prefersCLAUDE_PROJECT_DIR, falls back to pwd-walk for.git) andsamePath()(abs + EvalSymlinks + Clean). New scope check inrunStopGuardapproves silently on mismatch; emptystate.RepoRoot(pre-devkit-stop-guard fires across repos — hook is session-scoped, should be repo/branch-scoped #91 sessions) and empty current repo root (missing hook env) fall through to legacy block — no silent approve on missing signal.src/cmd/guard_test.go—TestGuardStopHookRepoScopetable test covering matching/mismatched/trailing-slash/empty-state/empty-env cases.Test plan
go test ./...— 316 passgofmt -lclean on changed filesgo vet ./...cleanCLAUDE_PROJECT_DIR) paths