Harden check-note-template.sh: verify mermaid fence + facet keyword for architecture notes #239
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#239
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
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?
Type
Feature
Lineage
Standalone — discovered during 2026-04-10 architecture-template gate review. Current hook enforces headings only; a note with
### Diagram\n(TODO: add diagram)would pass. Confirmed live: the newarch-domain-pal-e-docsnote was accepted by the hook without any fence validation. This ticket tightens the gate to catch empty diagrams and facet-keyword mismatches.Scope review round 1: NEEDS_REFINEMENT (review-945-2026-04-10). Fixed in this revision: (1) fence detection regex expanded to include the dominant
<pre><code class="language-mermaid">form the pal-e-docs markdown→HTML pipeline produces, (2) regression fixture list expanded from 3 to 13 (7 full triplets + 6 non-triplet arch notes), (3) new AC added for server-rendered HTML round-trip, (4)arch:hooksrelabeled toarch:claude-custom(maps to existing component), (5)story:agent-writehome clarified (the reviewer searchedproject-claude-customwhich does not exist — the story lives inproject-pal-e-docsuser-stories table).Repo
forgejo_admin/claude-customUser Story
As an Agent (Ava creating architecture notes via MCP)
I want the template hook to fail when the Diagram section lacks a valid mermaid fence or contains the wrong diagram type for the facet
So that zero malformed architecture notes land in production (matching the
story:agent-writesuccess metric: "Zero malformed notes in production")Traces to:
story:agent-writeonproject-pal-e-docs(confirmed viaget_section(slug="project-pal-e-docs", anchor_id="user-stories")).Architecture
arch:claude-custom— theclaude-customrow in the Shared Infrastructure table ofarch-domain-pal-e-agency(Note:
arch-domain-pal-e-agencypredates the strict Components-section template; its Shared Infrastructure table functions as the de facto Components surface. Template-conformance migration of that note is separate scope — do not touch in this ticket.)Context
hooks/check-note-template.shroutes onnote_type=architectureand currently enforces that four heading strings are present:Diagram,Components,Key Decisions,Related(lines 75–81). It does NOT verify:The template prescribes a fixed mermaid type per facet (see
template-architecture):arch-domain-{project}erDiagramarch-dataflow-{project}sequenceDiagramarch-deployment-{project}graph TB(acceptgraph TDandflowchart TB/flowchart TDas aliases)The triplet-per-project axiom is the value of the template. Enforcing the mermaid type at the gate makes the three-facet discipline self-enforcing.
File Targets
Files to modify:
hooks/check-note-template.sh— extend thearchitecture)case to verify (a) a mermaid fence is present, (b) the fence keyword matches the expected facet derived from the slug prefix.Files the agent should NOT touch:
template-architecturein pal-e-docs — template prescribes the rule, hook enforces it; no template change neededarch-domain-pal-e-agency— template-conformance migration is separate scopeFence Detection — Container Forms to Accept
The hook reads
tool_input.content, which may arrive in three container forms depending on who's callingcreate_note/update_note:```mermaid...```<pre class="mermaid">...</pre><pre><code class="language-mermaid">...</code></pre>All three must be accepted. The server-rendered form (#3) is the dominant stored form — confirmed by reading
arch-domain-pal-e-docs,arch-domain-westside-basketball, etc. If only forms #1 and #2 are detected, agent round-trips (get_note→ modify →update_note) will be denied because the content coming back from the server is in form #3.Suggested detection pattern (
grep -E):(The
\x60escape avoids triple-backtick collision inside a bash heredoc.)Keyword Extraction
After detecting a fence, extract the first non-comment, non-blank line inside the fence and match against the expected facet keyword. Keyword check must tolerate:
%%comment lines (Mermaid comment syntax)erDiagramorsequenceDiagram autonumber— match on the keyword prefix, not exact line equality)Acceptance Criteria
arch-domain-foocontainingsequenceDiagram) → hook denies with message naming the expected keywordtool_input.contentis server-rendered HTML (form #3,<pre><code class="language-mermaid">) → hook allowsarch-domain-,arch-dataflow-,arch-deployment-arch-auth-westside-basketball,arch-sitemap-westside-basketball,arch-domain-sponsorship,arch-generic-checkout,arch-secrets-pipeline) → fence required, keyword check skippedTest Expectations
mcp__pal-e-docs__create_notewith stub content:bash -n hooks/check-note-template.shto parse-check, then the fixture testsRegression Fixture List (must all still pass)
Full triplets (7):
arch-domain-westside-basketball,arch-dataflow-westside-basketball,arch-deployment-westside-basketballarch-domain-mcd-tracker,arch-dataflow-mcd-tracker,arch-deployment-mcd-trackerarch-domain-pal-e-pac,arch-dataflow-pal-e-pac,arch-deployment-pal-e-pacarch-domain-pal-e-app,arch-dataflow-pal-e-app,arch-deployment-pal-e-apparch-domain-twitch-2k-wager,arch-dataflow-twitch-2k-wager,arch-deployment-twitch-2k-wagerarch-domain-westside-ops,arch-dataflow-westside-ops,arch-deployment-westside-opsarch-domain-westside-mcp,arch-dataflow-westside-mcp,arch-deployment-westside-mcpNew (1):
arch-domain-pal-e-docs(created 2026-04-10 as backing note for ticket pal-e-api#252; dataflow + deployment siblings TODO)Non-triplet arch notes (6) — fence required, facet keyword NOT checked:
arch-auth-westside-basketballarch-sitemap-westside-basketballarch-domain-sponsorshiparch-generic-checkoutarch-secrets-pipelinearch-domain-pal-e-agency(legacy org chart — predates template; usesgraph TDinstead oferDiagramdespite thearch-domain-prefix. This is a known inconsistency that should be accepted as pre-existing, not a regression target for this ticket. Consider adding a grandfather list if clean enforcement would break it.)Heads-up on the legacy note:
arch-domain-pal-e-agencywill FAIL a strictarch-domain-→erDiagramcheck because it usesgraph TD. The hook should either (a) grandfather this note by slug, or (b) accept the failure and flag it as a separate migration ticket. Recommendation: grandfather by slug in a clearly-commented exclusion list, and file a follow-up ticket to migrate the org chart to a template-conformant note. Do not block this ticket on the migration.Constraints
Checklist
bash -n)Related
project-pal-e-docs— knowledge base this protects; home ofstory:agent-writetemplate-architecture— the rule being enforcedarch-domain-pal-e-docs— example of a template-conformant arch note (created 2026-04-10)convention-architecture-ids— arch label derivation depends on valid diagramsScope Review: NEEDS_REFINEMENT
Review note:
review-945-2026-04-10Scope is solid — file targets and line references verify cleanly. Two content fixes and two traceability gaps need to be resolved before dispatch.
[BODY] fixes required in issue body:
html_contentfor existing arch notes uses<pre><code class="language-mermaid">(markdown→HTML conversion), not<pre class="mermaid">. Agents round-tripping viaget_note → update_notewill send this form. Extend the detection regex to a third alternation (e.g.,<code class="[^"]*mermaid[^"]*">) and make keyword extraction work across all three container forms. Without this, the hook will deny legitimate updates to every existing architecture note.<pre><code class="language-mermaid">…) must be accepted, not denied.[SCOPE] gaps (Ava decision):
story:agent-writelabel has no backing story note. Noproject-claude-customproject page exists. Either create one with a user-stories section or add the agent-write story underproject-pal-e-docs.arch:hookslabel has no backing arch note.search_notes("arch-hooks")returns zero. Either create an arch note that lists hooks as a component or relabel to an existing component.Decomposition: not needed. 1 file, 1 function, 7 AC, string-matching. ~15-30 min agent work.
Scope Review: APPROVED
Review note:
review-945-2026-04-10-r2Round 2 review verdict: APPROVED. All five round-1 blockers are resolved or correctly refuted.
[BODY]fence regex — RESOLVED (all three container forms enumerated with concrete grep pattern; server-rendered form #3 called out as dominant)[BODY]regression fixture list — RESOLVED (28 slugs enumerated: 21 triplet + 1 new + 6 non-triplet)[BODY]round-trip AC — RESOLVED (new AC4 covers server-rendered HTML update path)[SCOPE]story:agent-write — REFUTED correctly; verified inproject-pal-e-docsuser-stories table row 4 (round 1 searched nonexistentproject-claude-custom)[SCOPE]arch:hooks — RESOLVED via relabel toarch:claude-custom; verified inarch-domain-pal-e-agency#shared-infrastructure(claude-custom row: "Hooks, skills, agent configs, commands")Traceability triangle fully verified end-to-end. File target
hooks/check-note-template.shconfirmed at 251 lines, architecture case at lines 75–81. Grandfather strategy for legacyarch-domain-pal-e-agency(usesgraph TDnoterDiagram) is reasonable and correctly defers migration to a follow-up ticket.Minor non-blocking nit: the Checklist says "all 22" and the Lineage says "expanded from 3 to 13" but the Regression Fixture List itself enumerates 28 explicit slugs. The enumerated list is authoritative — dev should iterate it directly. Worth tidying eventually, not a review blocker.
Ticket can advance backlog → todo.