Skip to content

fix(#424): hook walker reads session pin for managed project workspaces#425

Merged
atlas-apex merged 1 commit into
me2resh:devfrom
atlas-apex:fix/GH-424-hook-walker-ops-root
May 27, 2026
Merged

fix(#424): hook walker reads session pin for managed project workspaces#425
atlas-apex merged 1 commit into
me2resh:devfrom
atlas-apex:fix/GH-424-hook-walker-ops-root

Conversation

@atlas-apex

Copy link
Copy Markdown
Collaborator

Summary

  • Pin-first resolution — the settings.json inline walker now reads ~/.claude/apexyard/ops-root-<SESSION_ID> (written by pin-ops-root.sh at session start) before walking up the directory tree. This resolves hooks from workspace/<name>/ CWDs where the ops repo is a sibling, not an ancestor.
  • Walk-up hardened — fallback walk-up now also requires .claude/hooks/ dir alongside the marker file, so it skips the portfolio root (has onboarding.yaml but no hooks) even without a pin.
  • 47 walker instances updated — all hook wiring entries in settings.json use the same new pattern.

Testing

  1. From ops repo CWD: hooks fire as before (single-fork mode)
  2. From workspace/yumyum/ CWD: hooks now fire via pin resolution
  3. From portfolio root CWD: hooks now fire via pin resolution
  4. Without a pin (new session before pin-ops-root.sh runs): walk-up fallback works for ops repo CWD, silently skips for portfolio CWD (same as before — no regression)

Glossary

Term Definition
Session pin A file written at session start containing the ops repo path, so hooks can find the ops root from any CWD
Walker The inline bash one-liner in settings.json that resolves the ops root before exec'ing each hook script
Split-portfolio Deployment mode where ops fork and private portfolio are sibling directories, not nested

Refs #424

🤖 Generated with Claude Code

The settings.json inline walker now checks the session pin file
(~/.claude/apexyard/ops-root-<SESSION_ID>) before walking up the
directory tree. This resolves hooks from managed project workspaces
(workspace/<name>/) where the ops repo is a sibling, not an ancestor.

Previously: walker found the portfolio root (has onboarding.yaml but
no .claude/hooks/) and silently exited. All hooks — including
auto-code-review.sh — never fired from workspace CWDs.

Now: pin-first resolution finds the ops root regardless of CWD.
Walk-up fallback also requires .claude/hooks/ dir alongside the
marker file, so even without a pin, it skips the portfolio root.

Single-fork mode: unaffected (ops root has both markers + hooks).

Refs #424

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

@atlas-apex atlas-apex left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review: PR #425

Commit: 1723f2af45eac5eda63b1fef9a23404bc7a7cd87

Summary

This PR fixes the hook walker in .claude/settings.json so that hooks fire correctly when the CWD is a managed project workspace (workspace/<name>/) rather than the ops fork root. All 47 inline walker instances now read the session pin file (~/.claude/apexyard/ops-root-<SESSION_ID>) before falling back to the walk-up. The walk-up itself is hardened to require .claude/hooks/ alongside the marker file, preventing false matches on the portfolio root (which has onboarding.yaml but no hooks directory).

Checklist Results

  • N/A Architecture & Design: Config-only change; no domain/application/infrastructure code
  • PASS Code Quality: Single walker template applied uniformly across all 47 entries; JSON validates cleanly
  • N/A Testing: No unit-testable code changed (the walker is an inline shell one-liner in JSON; the underlying pin-ops-root.sh and _lib-ops-root.sh are pre-existing and unchanged)
  • PASS Security: No secrets, no auth changes, no injection vectors
  • PASS Performance: Pin read is a single file read (fast path); walk-up is the same cost as before (fallback only)
  • PASS PR Description & Glossary: Clear summary with narrative bullets, glossary present with Session pin / Walker / Split-portfolio definitions
  • PASS Summary Bullet Narrative: All three bullets explain what changed and why
  • N/A Technical Decisions (AgDR): Bug fix only — no new libraries, frameworks, patterns, or architecture changes. The pin mechanism (pin-ops-root.sh, _lib-ops-root.sh) already exists on the base branch; this PR wires the inline walker to use it.

Issues Found

None.

Detailed Analysis

Walker logic verification — the new walker template is:

r="";
if [ -n "${CLAUDE_CODE_SESSION_ID:-}" ]; then
  p="${APEXYARD_OPS_PIN_DIR:-$HOME/.claude/apexyard}/ops-root-${CLAUDE_CODE_SESSION_ID}";
  [ -f "$p" ] && IFS= read -r r < "$p" && [ -d "$r/.claude/hooks" ] || r="";
fi;
if [ -z "$r" ]; then
  r=$PWD;
  while [ -n "$r" ] && [ "$r" != / ]; do
    { [ -f "$r/.apexyard-fork" ] || [ -f "$r/onboarding.yaml" ]; } && [ -d "$r/.claude/hooks" ] && break;
    r=${r%/*};
  done;
fi;
[ -d "$r/.claude/hooks" ] || exit 0;
exec "$r/.claude/hooks/<hook>.sh"

Scenarios verified:

Scenario Pin present Result
Single-fork, CWD = ops root Yes Pin resolves, hook fires
Single-fork, CWD = ops root No Walk-up finds markers + .claude/hooks/, hook fires
Single-fork, CWD = subdirectory Either Both paths work
Split-portfolio, CWD = workspace/<name>/ Yes Pin resolves to ops root, hook fires (THE FIX)
Split-portfolio, CWD = workspace/<name>/ No Walk-up climbs above workspace, never finds matching dir, exit 0 (graceful, same as before)
Portfolio root (has onboarding.yaml, no .claude/hooks/) Either Walk-up requires .claude/hooks/ dir, skips portfolio root (THE HARDENING)
Pin file exists but points to stale/invalid path Yes .claude/hooks/ check fails, r resets to "", falls through to walk-up
No markers anywhere No Walk-up exhausts, final guard exit 0

JSON validity: Confirmed via python3 -m json.tool.

Walker uniformity: All 47 bash -c commands use the identical template — only the hook filename at the end differs.

Walker count: 47 on this PR, 47 on upstream/dev (the base branch). No hooks added or removed — pure walker-pattern replacement.

Pin format compatibility: The walker reads IFS= read -r r < "$p", matching pin-ops-root.sh's write format printf '%s\n' "$ops_root". Space-safe.

No circular dependency: pin-ops-root.sh itself is the first hook in the SessionStart chain. The walker finds the hook via pin (on subsequent calls) or walk-up (on first call of the session before pin exists). The hook writes the pin using resolve_ops_root_walk (the pure walk-up variant), avoiding self-referential pin lookup.

Suggestions

None — this is a clean, mechanical bug fix with the right safety properties.

Verdict

APPROVED


Reviewed by Rex (Code Reviewer Agent)
Reviewed commit: 1723f2af45eac5eda63b1fef9a23404bc7a7cd87

@atlas-apex atlas-apex merged commit aa577ac into me2resh:dev May 27, 2026
1 check passed
me2resh added a commit that referenced this pull request Jun 5, 2026
The settings.json inline walker now checks the session pin file
(~/.claude/apexyard/ops-root-<SESSION_ID>) before walking up the
directory tree. This resolves hooks from managed project workspaces
(workspace/<name>/) where the ops repo is a sibling, not an ancestor.

Previously: walker found the portfolio root (has onboarding.yaml but
no .claude/hooks/) and silently exited. All hooks — including
auto-code-review.sh — never fired from workspace CWDs.

Now: pin-first resolution finds the ops root regardless of CWD.
Walk-up fallback also requires .claude/hooks/ dir alongside the
marker file, so even without a pin, it skips the portfolio root.

Single-fork mode: unaffected (ops root has both markers + hooks).

Refs #424

Co-authored-by: me2resh <ahmed.abdelaliem@gmail.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants