Skip to content

fix(#56): exempt absolute-form .claude/ / docs/ paths in require-active-ticket.sh#65

Merged
atlas-apex merged 2 commits into
mainfrom
fix/#56-active-ticket-absolute-path
Apr 17, 2026
Merged

fix(#56): exempt absolute-form .claude/ / docs/ paths in require-active-ticket.sh#65
atlas-apex merged 2 commits into
mainfrom
fix/#56-active-ticket-absolute-path

Conversation

@atlas-apex

Copy link
Copy Markdown
Collaborator

Summary

One-hook fix for apexstack#56. When an agent worktree's git-toplevel differs from the outer apexstack tree, require-active-ticket.sh leaves REL_PATH absolute and the case-statement exemptions (relative-only) miss. Legitimate writes to .claude/session/current-ticket and docs/** get blocked.

Fix: extend each path-prefix exemption with its absolute-form twin (*/.claude/*|*/.claude, */docs/*|*/docs, */projects/*/docs/*). The existing *.md pattern already crosses /, so this is the same trick applied to the remaining exemptions.

What changed

  • .claude/hooks/require-active-ticket.sh — three case arms extended + a comment block explaining the pattern for future maintainers
  • Added a 7-case smoke test (/tmp/smoke-56.sh — not committed, stored locally); all pass

Testing

PASS  relative .claude/
PASS  absolute .claude/
PASS  nested-worktree absolute .claude/
PASS  relative docs/
PASS  absolute docs/
PASS  non-exempt code, no ticket       (blocks as before)
PASS  non-exempt code, with ticket     (allows as before)

Scope note

The issue literally asked only about .claude/. I extended docs/ and projects/*/docs/* too — they would fail the exact same way in the exact same conditions. Filing three follow-up bugs would be busywork. Documented the pattern in the comment block so future maintainers see the reasoning.

Glossary

Term Definition
REPO_ROOT Output of git rev-parse --show-toplevel. Used to normalise FILE_PATH to a repo-relative form.
absolute-form fallthrough When FILE_PATH doesn't start with REPO_ROOT (worktree mismatch), the strip on lines 29-31 is a no-op and REL_PATH stays absolute
agent worktree Claude Code creates isolated git worktrees for parallel agents; their git-toplevel is the worktree path, not the outer tree
hook Shell script invoked by Claude Code on PreToolUse; exit 2 blocks the tool call

Related

…ve-ticket.sh

The hook's case statements matched only repo-relative forms. When
FILE_PATH points outside REPO_ROOT (agent worktree whose git-toplevel
differs from the outer apexstack tree), the strip on lines 29-31 is a
no-op and REL_PATH stays absolute. The case match fails, the exemption
is missed, the hook blocks legitimate session-marker / docs writes.

Extends each path-prefix exemption with its absolute-form twin:

  .claude/*|.claude|*/.claude/*|*/.claude
  docs/*|docs|*/docs/*|*/docs
  projects/*/docs/*|*/projects/*/docs/*

The existing `*.md` pattern already crosses `/`, so absolute-match via
a `*/…` prefix is a known-good shape — this just extends the same
trick to the remaining exemptions.

Added 7-case smoke test (relative + absolute form of each exemption
plus the non-exempt block/unblock controls). All pass.

Scope note: #56 literally asked only about `.claude/`. I extended
`docs/` and `projects/*/docs/*` too since they would fail the exact
same way in the exact same conditions — filing follow-up bugs for
each would be busywork. Documented the whole pattern in a comment
block so future maintainers see the reasoning.

Closes #56

@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 #65

Commit: dbb09090cd76620c47d6059f639f86c598094510

Summary

One-hook fix for #56. Extends the REL_PATH case statement in .claude/hooks/require-active-ticket.sh to match absolute-form paths (*/.claude/*, */docs/*, */projects/*/docs/*) in addition to the existing repo-relative forms. Fixes the agent-worktree fallthrough where REPO_ROOT strip is a no-op and REL_PATH stays absolute.

Checklist Results

  • Architecture & Design: Pass — same shape as the existing *.md glob; consistent with how the hook already handles its only other cross-/ pattern
  • Code Quality: Pass — 9-line comment block documents the reasoning; no dead code
  • Testing: Pass — 7-case transcript in PR body covers relative, absolute, nested-worktree, non-exempt-with/without-ticket
  • Security: Pass — exemption only broadens the "no ticket required" path; cannot bypass any other hook
  • Performance: N/A (case-statement eval)
  • PR Description & Glossary: Pass — 4 terms, all load-bearing (REPO_ROOT, absolute-form fallthrough, agent worktree, hook)
  • Technical Decisions (AgDR): N/A — applying an existing pattern, no new approach

Logic trace against the 7 smoke cases

  • REPO_ROOT=/x, FILE_PATH=/x/.claude/session/current-ticketREL_PATH=.claude/session/current-ticket → matches .claude/* → exit 0 ✓
  • REPO_ROOT="" + FILE_PATH=/x/.claude/session/... → strip is no-op → REL_PATH=/x/.claude/session/... → matches */.claude/* → exit 0 ✓
  • REPO_ROOT=/worktree, FILE_PATH=/outer/.claude/session/... → strip mismatches → REL_PATH stays absolute → matches */.claude/* → exit 0 ✓
  • FILE_PATH=/x/docs/foo.mdREL_PATH=docs/foo.md → matches docs/* (or *.md fallthrough) → exit 0 ✓
  • Absolute /y/docs/foo.md with mismatched root → matches */docs/* → exit 0 ✓
  • Non-exempt code, no ticket → falls through to marker check → exit 2 ✓
  • Non-exempt code, with ticket marker → exit 0 ✓

All 7 pass.

Scope

Extending docs/ and projects/*/docs/* alongside .claude/ is justified — same root cause, same file, same patch hunk. Filing two follow-ups would be busywork and would leave the hook half-fixed between merges.

Potential false-positive

*/docs/* will match a legitimate source file at /x/src/myapp/docs/config.ts in a downstream project and exempt it from the ticket-first rule. In practice: apexstack hooks operate on the framework's own exemption list, downstream projects that vendor the hook tend to keep apexstack's layout (docs/ is docs, not source), and the prior docs/* pattern already had this property at the repo-relative level — only difference is it now crosses /. Not a blocker. Worth a follow-up only if a downstream project reports ghost-exempt source files.

Suggestions

None blocking.

Verdict

APPROVED


Reviewed by Rex (Code Reviewer Agent)
Reviewed commit: dbb09090cd76620c47d6059f639f86c598094510

The earlier `*/docs/*` pattern already matches `projects/<name>/docs/<anything>`
(shell case `*` crosses `/`), so the explicit `projects/*/docs/*` arm is
a dead branch. Shellcheck flags it as SC2221/SC2222 and the CI job fails.

Remove the redundant arm; added an inline comment so the intent is
preserved for future maintainers (per-project apexstack docs are still
exempt, just via the generic `*/docs/*` pattern).

Smoke test (all 7 cases) still passes.

@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.

Re-review at HEAD e52928207c1ffe1b6a33c4721dae488e9c9c0a18: incremental diff is purely the shellcheck lint fix — drops the dead projects/*/docs/* arm (subsumed by */docs/*) and adds an inline comment explaining the subsumption for future maintainers. Remaining case arms still cover the intent: per-project apexstack docs exempted via */docs/*. CI green: shellcheck .claude/hooks passes, Verify Ticket ID passes. SC2221/SC2222 warnings resolved. LGTM.

@atlas-apex atlas-apex merged commit 1e666a0 into main Apr 17, 2026
2 checks passed
@atlas-apex atlas-apex deleted the fix/#56-active-ticket-absolute-path branch April 17, 2026 08:57
osama-abu-baker pushed a commit to osama-abu-baker/apexyard that referenced this pull request Jun 3, 2026
…re-active-ticket.sh (me2resh#65)

* fix(me2resh#56): exempt absolute-form .claude/ / docs/ paths in require-active-ticket.sh

The hook's case statements matched only repo-relative forms. When
FILE_PATH points outside REPO_ROOT (agent worktree whose git-toplevel
differs from the outer apexstack tree), the strip on lines 29-31 is a
no-op and REL_PATH stays absolute. The case match fails, the exemption
is missed, the hook blocks legitimate session-marker / docs writes.

Extends each path-prefix exemption with its absolute-form twin:

  .claude/*|.claude|*/.claude/*|*/.claude
  docs/*|docs|*/docs/*|*/docs
  projects/*/docs/*|*/projects/*/docs/*

The existing `*.md` pattern already crosses `/`, so absolute-match via
a `*/…` prefix is a known-good shape — this just extends the same
trick to the remaining exemptions.

Added 7-case smoke test (relative + absolute form of each exemption
plus the non-exempt block/unblock controls). All pass.

Scope note: me2resh#56 literally asked only about `.claude/`. I extended
`docs/` and `projects/*/docs/*` too since they would fail the exact
same way in the exact same conditions — filing follow-up bugs for
each would be busywork. Documented the whole pattern in a comment
block so future maintainers see the reasoning.

Closes me2resh#56

* fix: drop redundant projects/*/docs/* arm (shellcheck SC2221/SC2222)

The earlier `*/docs/*` pattern already matches `projects/<name>/docs/<anything>`
(shell case `*` crosses `/`), so the explicit `projects/*/docs/*` arm is
a dead branch. Shellcheck flags it as SC2221/SC2222 and the CI job fails.

Remove the redundant arm; added an inline comment so the intent is
preserved for future maintainers (per-project apexstack docs are still
exempt, just via the generic `*/docs/*` pattern).

Smoke test (all 7 cases) still passes.

---------

Co-authored-by: me2resh <ahmed.abdelaliem@gmail.com>
mosta7il pushed a commit to mosta7il/apexyard that referenced this pull request Jun 8, 2026
…re-active-ticket.sh (me2resh#65)

* fix(me2resh#56): exempt absolute-form .claude/ / docs/ paths in require-active-ticket.sh

The hook's case statements matched only repo-relative forms. When
FILE_PATH points outside REPO_ROOT (agent worktree whose git-toplevel
differs from the outer apexstack tree), the strip on lines 29-31 is a
no-op and REL_PATH stays absolute. The case match fails, the exemption
is missed, the hook blocks legitimate session-marker / docs writes.

Extends each path-prefix exemption with its absolute-form twin:

  .claude/*|.claude|*/.claude/*|*/.claude
  docs/*|docs|*/docs/*|*/docs
  projects/*/docs/*|*/projects/*/docs/*

The existing `*.md` pattern already crosses `/`, so absolute-match via
a `*/…` prefix is a known-good shape — this just extends the same
trick to the remaining exemptions.

Added 7-case smoke test (relative + absolute form of each exemption
plus the non-exempt block/unblock controls). All pass.

Scope note: me2resh#56 literally asked only about `.claude/`. I extended
`docs/` and `projects/*/docs/*` too since they would fail the exact
same way in the exact same conditions — filing follow-up bugs for
each would be busywork. Documented the whole pattern in a comment
block so future maintainers see the reasoning.

Closes me2resh#56

* fix: drop redundant projects/*/docs/* arm (shellcheck SC2221/SC2222)

The earlier `*/docs/*` pattern already matches `projects/<name>/docs/<anything>`
(shell case `*` crosses `/`), so the explicit `projects/*/docs/*` arm is
a dead branch. Shellcheck flags it as SC2221/SC2222 and the CI job fails.

Remove the redundant arm; added an inline comment so the intent is
preserved for future maintainers (per-project apexstack docs are still
exempt, just via the generic `*/docs/*` pattern).

Smoke test (all 7 cases) still passes.

---------

Co-authored-by: me2resh <ahmed.abdelaliem@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] require-active-ticket.sh fails when REPO_ROOT is a nested worktree — strips wrong prefix from FILE_PATH

2 participants