Skip to content

feat(#293): Rex domain-aware code review — handbooks/domain/ Stage 1#294

Merged
atlas-apex merged 4 commits into
devfrom
feature/GH-293-rex-domain-aware-handbooks
May 19, 2026
Merged

feat(#293): Rex domain-aware code review — handbooks/domain/ Stage 1#294
atlas-apex merged 4 commits into
devfrom
feature/GH-293-rex-domain-aware-handbooks

Conversation

@atlas-apex

Copy link
Copy Markdown
Collaborator

Summary

  • Adds a fourth handbook bucket handbooks/domain/<area>/ for domain-specific review knowledge (GitHub EMU, Stripe webhooks, SSO SAML, etc.) — the kind of gotchas a code/ticket/framework-rules review can't catch.
  • Path-scoping via opt-in YAML frontmatter (paths: field of globs). Handbooks without paths: load always (foundational rules); handbooks with paths: load only when the PR diff matches at least one glob.
  • Self-contained Python matcher in Rex's prompt — handles ** (cross-segment), * (within-segment), ?, and {a,b,c} brace alternation. Replaces an earlier broken shell-case approach (shell case patterns silently fail on both ** globstar and {} alternation).
  • Stages 2 (/codify-rule for capturing misses) and 3 (/enrich-domain for mining recent PRs) tracked in [Feature] Rex domain-aware code review — handbooks/domain/<area>/ glob with diff-match loading + lessons-learned capture #293's ACs as follow-up tickets; this MVP is the foundation.

Why

Rex today reviews PRs with code + ticket + framework rules + adopter handbooks. None of those sources carry domain knowledge of the specific component. Concrete miss flagged 2026-05-19: Rex reviewed a GitHub EMU migration script and missed gotchas Copilot caught (EMU enterprises can't access private forks of public repos). Same shape recurs across Stripe webhooks, SSO claim handling, tenant-isolation rules.

Adding a generic handbook in architecture/ or general/ for every domain rule pollutes those buckets and burns review tokens on every PR. The paths: frontmatter lets domain rules stay narrow without operator config drift — the trigger lives next to the rule.

Design rationale and option comparison: docs/agdr/AgDR-0037-rex-domain-handbooks.md.

Testing

The Python matcher was validated against 8 test cases before commit:

  1. paths matches, diff includes a file under the glob → load
  2. paths declared, diff has no matching file → skip
  3. frontmatter present but no paths: key → always load
  4. no frontmatter at all → always load
  5. brace alternation {ts,js,py} matches .ts
  6. brace alternation matches .py
  7. brace alternation does NOT match .rb
  8. paths: [] empty list → always load

To verify locally:

git fetch origin feature/GH-293-rex-domain-aware-handbooks
git checkout feature/GH-293-rex-domain-aware-handbooks
# Read .claude/agents/code-reviewer.md § "Domain handbook frontmatter — `paths:` field"
# Copy the Python script, save to /tmp/match_handbook.py, run the 8 cases above

Adopter-side smoke (after merge):

mkdir -p handbooks/domain/example
cat > handbooks/domain/example/test.md <<'HB'
---
paths:
  - "src/example/**"
---
# Handbook: example
HB
# Open a PR touching src/example/foo.ts — Rex loads the handbook
# Open a PR touching unrelated/path.ts — Rex skips it

Out of scope (deferred)

Glossary

Term Definition
Rex The Code Reviewer sub-agent (.claude/agents/code-reviewer.md) — automated first-pass review on every PR
Domain handbook A markdown file under handbooks/domain/<area>/ capturing review knowledge about a specific problem domain (EMU migration, Stripe webhooks, etc.)
paths: frontmatter A YAML field at the top of a domain handbook listing globs that scope when Rex loads it (paths: ['scripts/github-emu-migration/**']). Absent or empty → always load
Glob-star (**) Pathname pattern that matches across directory boundaries (scripts/**/sync.ts matches scripts/a/b/sync.ts). Distinct from * which matches only within one segment
Brace alternation ({a,b,c}) Pattern syntax expanding to multiple alternatives (*.{ts,js,py} matches .ts, .js, or .py files)
Custom-handbooks layer The <private>/custom-handbooks/<dim>/... tree introduced in framework #243 for split-portfolio adopters to ship private handbooks alongside the public ones — now gets domain/<area>/ discovery transparently
AgDR Agent Decision Record — captures the why behind a non-obvious technical choice. AgDR-0037 documents this design

Closes #293

…tage 1

Adds a fourth handbook bucket so Rex can load domain-specific review
notes (GitHub EMU semantics, Stripe webhook validation, SSO SAML claim
shapes) scoped to PRs that touch the domain's code. Trigger lives in
YAML frontmatter at the top of each handbook:

    ---
    paths:
      - "scripts/github-emu-migration/**"
      - "**/emu-*.{ts,js,py}"
    ---

    ENFORCEMENT: blocking

    # Handbook: GitHub EMU migration safety

If `paths:` is absent or empty, the handbook loads always — same shape
as the existing architecture/general buckets (foundational rule with
no path boundary). If present, Rex loads only when the PR diff matches
at least one glob.

The matcher is a self-contained Python script in Rex's prompt — handles
**, *, ?, and {a,b,c} brace alternation correctly. The earlier shell-
case attempt silently failed on both ** (case patterns don't support
globstar) and on {ts,js} (brace expansion happens at command-parse
time, not pattern-match time); tested 8 cases incl. the originally
broken ones before shipping.

Custom-handbooks layer (split-portfolio adopters) gets the same
convention transparently via portfolio_custom_handbooks_dir — no
separate wiring.

Stages 2 (/codify-rule for capturing Rex-misses) and 3 (/enrich-domain
for mining recent PRs) are tracked in the ticket's ACs as follow-up
scope; this MVP is the path-glob discovery foundation those skills
layer on.

- Add handbooks/domain/README.md — operator authoring convention
- Add docs/agdr/AgDR-0037-rex-domain-handbooks.md — design rationale
- Update .claude/agents/code-reviewer.md § 8 — fourth bucket row +
  frontmatter-parse section + Python matcher reference
- Update handbooks/README.md — document the new bucket + note why no
  framework samples ship under it

Closes #293

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

The first cut invoked python3 once per candidate handbook. In sandboxed
environments where every Bash call surfaces a permission prompt, that's
O(N) prompts in handbook count — and Rex already runs ~6-8 prompts per
review for the surrounding gh/find/printf calls. An adopter with 10
domain handbooks would have seen 10 extra prompts on every review.

Batched shape:
  printf '%s\n' "$DIFF_FILES" | python3 /tmp/match_handbooks.py "${DOMAIN_HBS[@]}"

Reads handbook paths from argv, diff from stdin, prints loadable paths
to stdout. One python3 invocation regardless of N. Same matching logic;
the per-handbook should_load() function moved inside the script's main()
loop.

Tested against 4 scenarios (matched + always-load mix, no-match, brace
alternation, empty argv) before commit.

AgDR-0037 § "Consequences" updated with the matcher-invocation-shape
rationale so future readers see why batched rather than naive.

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 #294

Commit: d25c23330bbc17615dc38737ffe2bbcf4a28829d

Summary

Adds a fourth handbook bucket handbooks/domain/<area>/ with opt-in paths: frontmatter so I (Rex) can load domain-specific review notes scoped to PRs that touch the domain's code. The matcher is a self-contained Python script in my own prompt; this is a meta-PR modifying my agent definition. The amended HEAD batches the matcher to one python3 invocation regardless of handbook count, which keeps per-review permission-prompt surface constant.

I ran the matcher against 18 stress cases locally — 8 from the PR description plus 10 additional edge cases. The core contract is sound. Two soft issues worth flagging (neither blocks merge).

Checklist Results

  • Architecture & Design: Pass — fourth-bucket extension is consistent with the path-mirroring discovery convention already established for architecture / general / language. Custom-handbooks layer reused without separate wiring.
  • Code Quality: Pass with nits — see "Issues Found" #1 and #2 below.
  • Testing: Pass — 8 documented test cases pre-commit; I re-ran them all green + 10 additional edge cases. One edge case (trailing inline YAML comment) under-loads silently, see #1.
  • Security: Pass — no secrets, no auth/crypto/user-data paths. Python matcher reads files passed via argv and stdin; no shell injection surface.
  • Performance: Pass — the batched refactor is the right shape. O(1) python3 invocations per review regardless of N handbooks.
  • PR Description & Glossary: Pass — Glossary has 7 entries covering Rex, domain handbook, paths: frontmatter, glob-star, brace alternation, custom-handbooks layer, AgDR.
  • Technical Decisions (AgDR):Pass — AgDR-0037 linked in PR body, covers the four-option comparison (frontmatter / sidecar / config / hard-code), the chosen-option rationale, and the batched-matcher invocation shape in Consequences.
  • Adopter Handbooks: Pass — public always-load handbooks evaluated; none triggered findings (this is a docs + prompt PR; no domain/application/migration paths touched).

Issues Found

1. nit — Python matcher: trailing inline YAML comment after a quoted glob is captured literally as part of the glob.

The list-item regex at line 305 of .claude/agents/code-reviewer.md is ^\s*-\s*["\']?(.*?)["\']?\s*$. When the YAML line is:

- "src/foo/**"  # foundational rule

.*? matches up to the last optional-quote alternative which falls through to the trailing \s*$, capturing src/foo/**" # foundational rule as the glob. The handbook then silently fails to load for any real PR (the bogus regex never matches), which is precisely the failure mode the AgDR's stated principle warns against: "under-loading silently is worse than over-loading visibly."

Verified locally: printf 'src/foo/x.ts\n' | python3 match_handbooks.py edgeG.md returns nothing, where edgeG.md has the line above.

Fix sketch (one-line):

m = re.match(r'^\s*-\s*["\']?([^"\'\s#]+(?:[^"\'#]*[^"\'\s#])?)["\']?\s*(?:#.*)?$', line)

Or simpler — strip # ... from line before the regex match. This is the only documented-YAML-feature gap; if you'd rather defer, file a follow-up.

2. nit — **/foo.ts over-matches notfoo.ts at the root because ** translates to .* without a path-boundary anchor.

glob_to_regex('**/important.ts') emits ^.*important\.ts$, which matches both important.ts (correct) and notimportant.ts (incorrect — ** should consume directories + a separator, not arbitrary characters). The prompt documents the semantics as "shell pathname expansion semantics" (line 232), and bash globbing with globstar would not match notimportant.ts.

This is over-loading visibly rather than under-loading silently, which is the right failure direction per the AgDR. The downside: an over-broad load may cite a handbook on PRs where it's irrelevant, which trains operators to ignore the handbook section. Probably fine to ship and refine later if it bites.

Handbook Findings

No public always-load handbook flags fire on this diff:

  • handbooks/architecture/migration-safety.md (blocking) — N/A (no migration paths in diff).
  • handbooks/architecture/clean-architecture-layers.md (advisory) — N/A (no domain/, application/, infrastructure/ code touched).
  • handbooks/general/commit-message-quality.md (advisory) — both commits have substantive WHY bodies, ticket refs, and Co-Authored-By: lines. Subject lines under 70 chars (the d97fa1b headline gets truncated in gh output but the actual subject is fine). Pass.

No language handbooks load — no .ts/.py/.go/.rs files changed.

No domain handbooks exist yet (this PR creates the bucket itself).

Suggestions

  • AgDR-0037 § Artifacts placeholder. Line 83 still says PR me2resh/apexyard#XXX — implementation. Fill in #294 before merge so the AgDR is self-contained for future archaeology.
  • AgDR-0037 § Negative bullet 3 wording. Says "Rex silently skips the handbook" on malformed globs. The actual contract (Decision step 4 in the prompt) is to default to always-load with a visible warning. The AgDR wording is slightly more pessimistic than the implementation. Either tighten the AgDR ("Rex over-loads visibly with a warning") or accept it as describing the worst-case in pathological inputs.
  • Future-work pointer. The matcher script is embedded inline in the prompt at .claude/agents/code-reviewer.md lines 238-322. Next time it changes, a sibling test file at .claude/agents/tests/test_match_handbooks.py would let CI catch regressions like #1 above. Out of scope for this PR but worth a follow-up ticket.
  • /tmp/match_handbooks.py lifecycle. The discovery loop assumes the script lives at /tmp/match_handbooks.py. The prompt doesn't say how it gets there — the implicit contract is "I write it via Bash heredoc on first use per review." Worth noting once in the prompt so future-me doesn't forget.

Verdict

APPROVED

The refactor to batched matching is the right shape and addresses the per-handbook permission-prompt cost concern as described. The Python matcher is robust across 18 stress cases. The two nits above don't block merge — #1 is a real silent-failure mode but only triggers on a YAML pattern most operators won't write, and #2 is the preferred error direction per the AgDR's own stated principle. Both deserve follow-up tickets if you want to harden the matcher.

I checked the bash discovery loop, the ${PRIV:+...} empty-PRIV expansion, the README.md skip filter, the array assembly, and the empty-array guard. All bash-correct (zsh would choke on the unmatched glob handbooks/domain/*/, but the rest of the prompt's discovery shape already assumes bash semantics).

The AgDR conforms to the framework template, the design-rationale narrative is honest about the parser-surface tradeoff, and the four-option table covers the obvious alternatives.


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

Rex reviewed PR #294 (APPROVED) and flagged two real bugs in the prompt
I shipped, plus two AgDR doc nits. Fixing all four before merge so the
"Rex caught a bug in Rex's own prompt and we shipped it anyway" story
doesn't end up in the merge log.

1. Inline-comment regex bug — handbooks with `- "src/foo/**" # comment`
   captured the comment text as part of the glob and silently failed to
   match anything. The exact under-loads-silently failure mode AgDR-0037
   warns against. Fix: pre-strip ` # ...` tails (YAML's comment rule
   for whitespace-prefixed `#`) before the list-item regex.

2. `**/foo.ts` over-matched `notfoo.ts` — `**` was translating to `.*`
   without a path-boundary anchor, so the regex `^.*foo\.ts$` matched
   any string ending in `foo.ts`. Fix: translate `**/` to `(?:.*/)?`
   which is bash globstar semantics (zero or more path segments
   ending in `/`, OR empty for root). `notfoo.ts` no longer matches
   `**/foo.ts`; `foo.ts` and `src/sub/foo.ts` still do.

3. AgDR-0037 § Artifacts: `#XXX` → `#294` so the AgDR is self-
   contained for archaeology.

4. AgDR-0037 § Negative bullet 3 wording: previous text said "silently
   skips" then immediately contradicted itself in the mitigation
   ("degrades to always-load"). Rewrote to be consistent with the
   implementation's actual contract — always-load + visible warning,
   never silent skip.

Tested all 8 original cases + 6 new ones (2 for finding 1, 2 for
finding 2, plus the original `**/notfoo` regression). All 14 pass.

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 (re-review): PR #294 @ 1ed1930e815e1ca30bf1513a4f2cfe9ccf1737e9

Re-reviewing the four fixes from the prior nits at d25c233.

Verification of the four fixes

1. _STRIP_COMMENT inline-comment regex — ✓ verified.
Tested 10 edge cases — whitespace-prefixed #, # without preceding whitespace (literal hash in path), comment-only line, blank line, indented comment, paths: with inline comment, etc. All behave as the doc comment promises. The doc comment correctly calls out the "a literal # in a (rare) quoted glob survives" semantics — accurate for # without preceding whitespace. (Edge edge case: a quoted glob with literal <space>#<space> inside would still get the tail stripped — but that's a path with a space and a hash, which is bizarre enough that YAML quoting issues dominate anyway; not worth handling.)

2. **/ over-match — ✓ verified, regression fixed.
The new (?:.*/)? translation correctly handles all 24 cases I threw at it:

  • **/foo.ts matches foo.ts, a/foo.ts, a/b/foo.ts, a/b/c/foo.ts
  • **/foo.ts does NOT match notfoo.ts, foo.tsx, a/notfoo.ts ✓ ← the regression case
  • src/**/foo.ts matches src/foo.ts (zero segments between), src/a/foo.ts, src/a/b/foo.ts
  • src/**/foo.ts does NOT match srcfoo.ts
  • Trailing ** (e.g. scripts/github-emu-migration/**) matches one or many segments ✓
  • ** alone matches anything ✓
  • **.ts (without /) stays permissive via the fallback branch — matches both foo.ts and a/b/c.ts, rejects foo.txt
  • Brace alternation in **/ context (**/emu-*.{ts,js,py}) correctly excludes notemu-foo.ts and foo.rb

3. AgDR § Artifacts #XXX#294. ✓ fixed.

4. AgDR § Negative bullet 3 rewrite — mostly accurate, one sub-claim is still off (nit, below).

Issues Found

None blocking.

Suggestions (nit, optional)

nit: The AgDR § Negative bullet 3 says:

"...The matcher's parse path applies this default at every branch — missing paths:, empty paths: [], unterminated frontmatter, and exceptions during file read all fall through to return True."

…but the actual OSError branch does the opposite:

try:
    with open(hb) as f: lines = f.readlines()
except OSError:
    return False  # silent skip — contradicts the bullet's claim

I verified all six branches:

Branch Returns Matches AgDR claim?
OSError on file open False (silent skip) No — AgDR claims True
Empty file (no lines) True
No frontmatter (line 1 ≠ ---) True
Unterminated frontmatter (no closing ---) True
Missing paths: key True
Empty paths: [] True

In practice, OSError is rare — the discovery loop already gates on [ -f "$hb" ], so this only fires on a race (file deleted between find + open, permission flipped mid-review). But the AgDR's "asymmetry is deliberate" paragraph explicitly argues that under-loading silently is the failure mode to avoid, and this one branch contradicts it.

Two ways to fix in a follow-up (your call):

  • Code change: change except OSError: return False to except OSError: return True (with an optional print(f"⚠ handbook unreadable, defaulting to always-load: {hb}", file=sys.stderr) so it doesn't degrade silently).
  • Doc change: scope the AgDR bullet to "exceptions during frontmatter parsing (unterminated, unparseable YAML) — file-read errors are treated as missing handbooks and silently skipped". This is also defensible (a missing file genuinely IS a different case from a malformed file).

Either is fine. The other six branches are correct, the matcher is sound, and the four operator-flagged fixes all work.

nit (related): The contract in step 4 of the prompt says "emit a one-line warning to your review output: ⚠ handbook frontmatter unparseable, defaulting to always-load: <path>" — but the matcher itself prints nothing to stderr. The warning is left implicitly as the agent's responsibility (Rex's prompt has the contract; Rex should surface ⚠ when it sees a degraded-path handbook). Could be tightened by either adding print(f"⚠ ...", file=sys.stderr) inside the matcher, or by surfacing the contract more explicitly in § "Handbook Findings" so the agent knows to emit it. Not load-bearing for v1.

Checklist Results

  • ✅ Architecture & Design: Pass
  • ✅ Code Quality: Pass — Python matcher is clean, well-commented; doc comment for _STRIP_COMMENT accurately describes the rule
  • ✅ Testing: Pass — operator ran 14 tests locally (8 original + 6 new); I ran 24 glob translations + 10 comment-strip cases + 8 should_load branches and reproduced the operator's results
  • ✅ Security: N/A (markdown + docs only)
  • ✅ Performance: Pass — batched matcher invocation keeps Bash call count constant; per-review cost is one python3 call regardless of handbook count
  • ✅ PR Description & Glossary: Pass — glossary covers Rex / domain handbook / paths: frontmatter / glob-star / brace alternation / custom-handbooks / AgDR
  • ✅ Technical Decisions (AgDR): Pass — AgDR-0037 covers Options A-D with explicit trade-offs
  • ✅ Adopter Handbooks: N/A (no handbooks/ or custom-handbooks/ triggered by this diff)

Verdict

APPROVED — the four fixes from the prior review all land correctly. The remaining nit on AgDR Negative bullet 3 + the matcher-emits-no-warning detail are small enough to ship-then-fix; neither blocks merge. Re-review marker updated.


🤖 Reviewed by Rex (Code Reviewer Agent)
📌 Reviewed commit: 1ed1930e815e1ca30bf1513a4f2cfe9ccf1737e9

Rex's re-review on 1ed1930 flagged that the AgDR's "always-load on
degraded paths" claim included exceptions-during-file-read, but the
code returns False on OSError (race condition between bash existence
check and Python open).

The code behavior is correct — a vanished candidate file is more
honestly "absent" than "always-load". Tightening the AgDR to call
that out explicitly, with the rationale.

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 #294 (re-review at new HEAD)

Commit: 27052da3b7f1087a210f34812f3b4b99085e7798
Previous approved commit: 1ed1930

Summary

One-line doc clarification in AgDR-0037 § Negative bullet 3 addressing the inconsistency flagged in the prior re-review. The AgDR text now accurately describes the matcher's behavior at every degraded branch.

Diff scope verified

The only change between 1ed1930 (previously APPROVED) and 27052da is a single-bullet rewrite in docs/agdr/AgDR-0037-rex-domain-handbooks.md. No code changes, no surprises.

Bullet wording vs code behavior

Cross-checking the new AgDR text against should_load() in .claude/agents/code-reviewer.md:

Branch AgDR says Code does Match
OSError on open() return False (rationale: vanished file is honestly "absent") except OSError: return False
No frontmatter (line 1 ≠ ---) "no frontmatter at all" → return True if not lines or lines[0].rstrip() != '---': return True
Unterminated frontmatter "unterminated frontmatter" → return True if fm_end is None: return True
Missing paths: key "missing paths: key" → return True if not globs: return True
Empty paths: [] "empty paths: []" → return True empty inner → empty globsif not globs: return True

The rationale for the OSError carve-out is defensible: a file that vanished between the bash existence check and the Python read is genuinely absent from the candidate list; treating it as "always-load" would be silently fabricating a handbook-load. Returning False here is the right call.

Checklist Results

  • Architecture & Design: Pass
  • Code Quality: Pass (no code change)
  • Testing: Pass (no code change; behavior verified by inspection)
  • Security: Pass
  • Performance: Pass
  • PR Description & Glossary: Pass
  • Technical Decisions (AgDR): Pass — AgDR-0037 is the artifact being clarified
  • Adopter Handbooks: N/A

Issues Found

None.

Suggestions

None — the clarification is surgical and the rationale is sound.

Verdict

APPROVED (posted as comment because GitHub blocks self-approval; approval marker written for the merge gate)


Reviewed by Rex (Code Reviewer Agent)
Reviewed commit: 27052da3b7f1087a210f34812f3b4b99085e7798

@atlas-apex atlas-apex merged commit 28b3b76 into dev May 19, 2026
3 checks passed
@atlas-apex atlas-apex deleted the feature/GH-293-rex-domain-aware-handbooks branch May 19, 2026 17:34
me2resh added a commit that referenced this pull request Jun 5, 2026
…294)

* feat(#293): Rex domain-aware code review — handbooks/domain/<area>/ Stage 1

Adds a fourth handbook bucket so Rex can load domain-specific review
notes (GitHub EMU semantics, Stripe webhook validation, SSO SAML claim
shapes) scoped to PRs that touch the domain's code. Trigger lives in
YAML frontmatter at the top of each handbook:

    ---
    paths:
      - "scripts/github-emu-migration/**"
      - "**/emu-*.{ts,js,py}"
    ---

    ENFORCEMENT: blocking

    # Handbook: GitHub EMU migration safety

If `paths:` is absent or empty, the handbook loads always — same shape
as the existing architecture/general buckets (foundational rule with
no path boundary). If present, Rex loads only when the PR diff matches
at least one glob.

The matcher is a self-contained Python script in Rex's prompt — handles
**, *, ?, and {a,b,c} brace alternation correctly. The earlier shell-
case attempt silently failed on both ** (case patterns don't support
globstar) and on {ts,js} (brace expansion happens at command-parse
time, not pattern-match time); tested 8 cases incl. the originally
broken ones before shipping.

Custom-handbooks layer (split-portfolio adopters) gets the same
convention transparently via portfolio_custom_handbooks_dir — no
separate wiring.

Stages 2 (/codify-rule for capturing Rex-misses) and 3 (/enrich-domain
for mining recent PRs) are tracked in the ticket's ACs as follow-up
scope; this MVP is the path-glob discovery foundation those skills
layer on.

- Add handbooks/domain/README.md — operator authoring convention
- Add docs/agdr/AgDR-0037-rex-domain-handbooks.md — design rationale
- Update .claude/agents/code-reviewer.md § 8 — fourth bucket row +
  frontmatter-parse section + Python matcher reference
- Update handbooks/README.md — document the new bucket + note why no
  framework samples ship under it

Closes #293

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

* refactor(#293): batch the domain-handbook matcher — one python3 call per review

The first cut invoked python3 once per candidate handbook. In sandboxed
environments where every Bash call surfaces a permission prompt, that's
O(N) prompts in handbook count — and Rex already runs ~6-8 prompts per
review for the surrounding gh/find/printf calls. An adopter with 10
domain handbooks would have seen 10 extra prompts on every review.

Batched shape:
  printf '%s\n' "$DIFF_FILES" | python3 /tmp/match_handbooks.py "${DOMAIN_HBS[@]}"

Reads handbook paths from argv, diff from stdin, prints loadable paths
to stdout. One python3 invocation regardless of N. Same matching logic;
the per-handbook should_load() function moved inside the script's main()
loop.

Tested against 4 scenarios (matched + always-load mix, no-match, brace
alternation, empty argv) before commit.

AgDR-0037 § "Consequences" updated with the matcher-invocation-shape
rationale so future readers see why batched rather than naive.

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

* fix(#293): address Rex's findings on the domain-handbook matcher

Rex reviewed PR #294 (APPROVED) and flagged two real bugs in the prompt
I shipped, plus two AgDR doc nits. Fixing all four before merge so the
"Rex caught a bug in Rex's own prompt and we shipped it anyway" story
doesn't end up in the merge log.

1. Inline-comment regex bug — handbooks with `- "src/foo/**" # comment`
   captured the comment text as part of the glob and silently failed to
   match anything. The exact under-loads-silently failure mode AgDR-0037
   warns against. Fix: pre-strip ` # ...` tails (YAML's comment rule
   for whitespace-prefixed `#`) before the list-item regex.

2. `**/foo.ts` over-matched `notfoo.ts` — `**` was translating to `.*`
   without a path-boundary anchor, so the regex `^.*foo\.ts$` matched
   any string ending in `foo.ts`. Fix: translate `**/` to `(?:.*/)?`
   which is bash globstar semantics (zero or more path segments
   ending in `/`, OR empty for root). `notfoo.ts` no longer matches
   `**/foo.ts`; `foo.ts` and `src/sub/foo.ts` still do.

3. AgDR-0037 § Artifacts: `#XXX` → `#294` so the AgDR is self-
   contained for archaeology.

4. AgDR-0037 § Negative bullet 3 wording: previous text said "silently
   skips" then immediately contradicted itself in the mitigation
   ("degrades to always-load"). Rewrote to be consistent with the
   implementation's actual contract — always-load + visible warning,
   never silent skip.

Tested all 8 original cases + 6 new ones (2 for finding 1, 2 for
finding 2, plus the original `**/notfoo` regression). All 14 pass.

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

* docs(#293): clarify OSError contract in AgDR-0037 (Rex re-review nit)

Rex's re-review on dbffb81 flagged that the AgDR's "always-load on
degraded paths" claim included exceptions-during-file-read, but the
code returns False on OSError (race condition between bash existence
check and Python open).

The code behavior is correct — a vanished candidate file is more
honestly "absent" than "always-load". Tightening the AgDR to call
that out explicitly, with the rationale.

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

---------

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants