feat: expand check-note-template.sh to enforce 6 new type templates #200
No reviewers
Labels
No labels
domain:backend
domain:devops
domain:frontend
status:approved
status:in-progress
status:needs-fix
status:qa
type:bug
type:devops
type:feature
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
ldraney/claude-custom!200
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "183-expand-check-note-template"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Expands
check-note-template.shto enforce required headings for 6 new note types (review,architecture,user-story,sop,convention,validation) using inline heading lists routed on thenote_typefield. Thedoctype passes through intentionally. Existing tag-based routing forproject-pageandissueis preserved as a legacy fallback.Changes
hooks/check-note-template.sh-- Added type-based routing viacaseonnote_typefield with inline required headings for 6 types. Restructured to checknote_typefirst, then fall back to tag-based routing. No API calls needed for new types (headings are hardcoded from template notes).Test Plan
<h3>and markdown###formats)doctype passes through without enforcementtodo/bughard-block still worksReview Checklist
hooks/check-note-template.shmodified (per issue file targets)check-issue-template.shandcheck-board-item.shnot toucheddoctype explicitly passes throughRelated Notes
Related
Closes #183
QA Review -- PR #200
Checklist
hooks/check-note-template.shmodified (1 file, per issue file targets)check-issue-template.shandcheck-board-item.shuntouchedexit 0in case branchNOTE_TYPE_INPUTtoNOTE_TYPE(cleaner)trap 'exit 0' ERRat top, plus trap on tmpfile cleanupgrep -iFdoes case-insensitive fixed-string match, works for<h3>X</h3>and### Xnote_typefield) checked first, tag-based fallback second -- matches issue specKnown Limitation (not a blocker)
The
grep -iFsubstring matching means "Related" matches lines containing "Related Architecture". In theuser-storytype, a note with only### Related Architecturebut no standalone### Relatedwould incorrectly pass. This is inherited from the original hook's approach and acceptable for a template enforcement hook (not a security boundary). Real notes will have both sections.VERDICT: APPROVE
Clean implementation. Single file change. All 8 acceptance criteria from issue #183 are met. No nits.
PR #200 Review
DOMAIN REVIEW
Tech stack: Bash shell script (Claude Code PreToolUse hook). Single file:
hooks/check-note-template.sh.Architecture: The PR adds a
casestatement routing onnote_typefor 6 new types (review,architecture,user-story,sop,convention,validation) with inline required headings. Thedoctype exits early as a pass-through. Existing tag-based routing forproject-pageandissueis preserved as a legacy fallback. The routing hierarchy is: hard-block (todo/bug) -> type-based enforcement -> tag-based enforcement -> allow.Correctness analysis:
NOTE_TYPE_INPUTtoNOTE_TYPEis clean and avoids collision with the newTAG_TYPEvariable in the tag-based section. Good.CONTENTextraction is moved above the type-based section and shared by both paths. No duplication there.casestatement has no*)default, so unrecognizednote_typevalues (including empty) correctly fall through to tag-based routing. This is the right behavior.doctype doesexit 0inside thecase-- clean early return, no heading enforcement. Correct per spec.if [ -n "$REQUIRED_HEADINGS" ]guard correctly gates the type-based enforcement block and allows fallthrough to tag-based when no type matched.trap 'rm -f "$TMPFILE"; exit 0' ERRaftermktemp, which overwrites the globaltrap 'exit 0' ERRfrom line 18. This is fine -- the type-based path always exits before reaching the tag-based path, and the tag-based path sets its own trap after its ownmktemp. No trap leakage.sedpipeline) is identical between both paths. Consistent.grep -qiFfor heading matching is case-insensitive and literal. This matches the existing pattern. Correct.Potential edge case: If
note_typeis set to something likereviewbutCONTENTis non-empty and contains all headings, the type-based path allows and exits. IfCONTENTis empty, it denies with a helpful message referencing the template slug. Both paths are correct.BLOCKERS
1. No automated tests for 6 new enforcement paths.
The PR adds 6 new
casebranches, each with hardcoded heading lists. The repo has an established test pattern (tests/test_block_groupme_send.sh-- 21 tests for a similar PreToolUse hook). The PR body documents thorough manual testing, but no automated test file was added.This is a BLOCKER per the criteria: "New functionality with zero test coverage." With 6 distinct heading lists hardcoded inline, a test file (
tests/test_check_note_template.sh) should verify:doctype passes throughtodo/bughard-block still worksnote_typefalls through to tag-based routingThe GroupMe test file provides a clear pattern to follow. The heading lists are the most drift-prone part of this hook (if templates change, inline headings could diverge), so automated tests are essential.
NITS
1. DRY opportunity: heading validation loop is duplicated.
The
while IFS= read -r headingloop +MISSINGaccumulation + deny JSON block appears in both the type-based section (~lines 107-130 in the new file) and the tag-based section (~lines 225-248). These could be extracted into avalidate_headings()function. Both paths share the same pattern: write content to tmpfile, decode HTML entities, loop over headings, check withgrep -qiF, build MISSING string, deny if non-empty.This is a nit rather than a BLOCKER because the duplication is between two distinct routing paths (not auth/security logic), and the tag-based path has the additional curl+jq extraction that the type-based path does not.
2. Comment header could reference the matcher.
The script header comment says
# Matcher: mcp__pal-e-docs__create_notewhich is accurate persettings.json. Good -- no issue here, just confirming it is correct.3. Hardcoded headings are a maintenance surface.
The 6 heading lists are hardcoded inline. If template notes in pal-e-docs change, these inline lists must be updated manually. The comment on line ~55 ("These match the template notes in pal-e-docs") is helpful, but there is no automated check that they stay in sync. Consider a future follow-up to either generate these from the templates or add a CI check. Not blocking for this PR.
SOP COMPLIANCE
183-expand-check-note-templatematches issue #183check-issue-template.shandcheck-board-item.shnot touched (per issue constraint)PROCESS OBSERVATIONS
VERDICT: NOT APPROVED
One BLOCKER: missing automated tests for 6 new enforcement paths. The repo has an established test pattern (
tests/test_block_groupme_send.sh) and the same pattern should be applied here. Addtests/test_check_note_template.shcovering the scenarios listed above, then re-request review.QA blocker resolved: added
tests/test_check_note_template.shwith 23 tests (45 assertions).Coverage:
docpass-through (including empty content)todo/bughard-block with alternative suggestionnote_typefallthroughnote_type+ no tags fallthroughhtml_contentfield fallbackAll 45 assertions pass. Existing test suites unaffected.
PR #200 Review
Re-review after fix commit adding
tests/test_check_note_template.shwith 23 tests.DOMAIN REVIEW
Tech stack: Bash hooks (PreToolUse enforcement for Claude Code), shell test harness.
Hook logic (
hooks/check-note-template.sh):casestatement for type-based routing is well-structured. Each type maps to aTEMPLATE_SLUGand inlineREQUIRED_HEADINGS, avoiding unnecessary API calls -- a clear performance improvement over the tag-based path.docpass-through exits before content validation, which is correct and intentional.NOTE_TYPE_INPUTtoNOTE_TYPEand fromNOTE_TYPEtoTAG_TYPEin the tag-based section cleanly separates the two routing paths and avoids variable shadowing.grep -qiFfor case-insensitive fixed-string matching on headings is correct.sedpipeline) in the new type-based section is identical to the existing tag-based section. Consistent.trap 'exit 0' ERRat the top,trap 'rm -f "$TMPFILE"; exit 0' ERRwhen a temp file exists. Both paths handle cleanup correctly.while IFS= read -r headingloop correctly skips empty lines.Test file (
tests/test_check_note_template.sh):html_contentfallback (Test 22), HTML entity decoding (Test 23).tests/test_block_groupme_send.sh: sameassert_contains,assert_deny/assert_emptyhelpers, same summary format. Good consistency.bash "$HOOK", requiring no API calls or network access. Fully self-contained and CI-friendly.BLOCKERS
Previous blocker (no automated tests): RESOLVED. The fix commit adds 23 tests covering all 6 new enforcement paths, doc pass-through, hard-blocks, empty content, unknown types, content field fallback, and HTML entity decoding. The test coverage is comprehensive -- happy paths, error paths, and edge cases are all covered.
No new blockers identified.
NITS
DRY opportunity: The heading-validation logic (create tmpfile, decode entities, loop with
grep -qiF, build MISSING string, cleanup, deny JSON) is duplicated between the type-based path (lines ~85-130 in the new code) and the tag-based path (lines ~215-245). A sharedvalidate_headings()function takingCONTENT,REQUIRED_HEADINGS,NOTE_TYPE, andTEMPLATE_SLUGas arguments would eliminate ~30 lines of duplication. Not a blocker since this is not an auth/security path and both copies are tested, but worth tracking for a future cleanup.Inline heading drift risk: The required headings for 6 types are hardcoded in the hook script. If the template notes in pal-e-docs change, these inline lists must be updated manually. A comment noting this coupling exists (e.g., "Keep in sync with template-{type} notes") would help future maintainers. The header comment does say "These match the template notes in pal-e-docs" but does not call out the manual sync requirement explicitly.
No markdown
###format test: Test 23 covers HTML entity decoding, but no test verifies that plain markdown### Headingformat is matched (the hook comment on line ~93 in the diff says "Support both HTML<h3>and markdown### Headingformats"). Thegrep -qiFon decoded content would match### Headingsince it just searches for the heading text string, so it works implicitly, but an explicit test would document this expectation. Very minor.SOP COMPLIANCE
183-expand-check-note-templatereferences issue #183.envfiles in the diffhooks/check-note-template.sh+tests/test_check_note_template.sh), matching the issue file targetscheck-issue-template.shandcheck-board-item.shnot touched, as specified in the issuePROCESS OBSERVATIONS
test_block_groupme_send.sh). The consistent test harness pattern (assert helpers + summary + exit code) is becoming a convention worth documenting.VERDICT: APPROVED