diff --git a/.claude/audits/INDEX.md b/.claude/audits/INDEX.md index 4c0895fd..7a003339 100644 --- a/.claude/audits/INDEX.md +++ b/.claude/audits/INDEX.md @@ -39,3 +39,4 @@ Persistent log of every `/article-audit` run. One row per audit; full reasoning | 2026-06-12 | [.NET Microservices Guide — What To Do and What To Avoid](2026-06-12-dotnet-microservices-do-avoid.md) (Kerim Kara, Real World .NET) | ⚙️ Re-audited after user pasted full body (initial pass paywall-limited). All 10 do/avoid items covered; article's own examples violate the canon 3× (publish-after-save dual-write vs outbox; ValidateAudience=false vs JWT rule; IOrderService layer vs VSA). Recurring implicit stance: internal-gRPC trust + no-gateway undocumented (2nd hit today) | Offered small encoding issue (internal-trust + no-gateway triggers); pending user decision | | 2026-06-12 | [Building Dapr Workflows in .NET With Aspire](2026-06-12-dapr-workflows-aspire.md) (Milan Jovanović, The .NET Weekly) | ✅ Considered + rejected with depth — project-decisions.md §22 ("Dapr — considered, not adopted") + architecture.md:283 (choreography saga, no central orchestrator) + perf doc already names Temporal/Durable Functions/Step Functions as the durable-execution alternatives. 202-Accepted + idempotency + outbox all encoded | No new issue — §22's table maps the classic 5 building blocks but not **Dapr Workflow**; fold a Workflow row + orchestration-vs-choreography trigger into open #86 | | 2026-06-15 | [Wolverine + Azure Service Bus Emulator](2026-06-15-wolverine-asb-emulator.md) (WolverineFx official docs) | ⚙️ Official recipe (`UseAzureServiceBus` + `transport.ManagementConnectionString` on :5300 + AutoProvision) matches in-flight feat-branch fix and **contradicts two `main` rules** — CLAUDE.md:230 + architecture.md:399 claim "emulator has no management API" (false; it does, only subscription admin returns 500). Also confirms the `ListenToAzureServiceBusSubscription(sub, c => c.TopicName=topic)` API vs our wrong `"{topic}/{sub}"` form | No new issue — tracked on #148; doc corrections land with that branch. Confirms feat-branch "Approach B" is officially correct; subscription-admin-500 limitation is ours to document (upstream omits it) | +| 2026-06-17 | [The New SDLC with Vibe Coding](2026-06-17-the-new-sdlc-with-vibe-coding.md) (Addy Osmani, Shubham Saboo, Sokratis Kartakis) | ✅ Already encoded, more rigorously — the project’s AI-assisted loop, verification, and rule-encoding surfaces are already the repo’s operating model | No action | diff --git a/.claude/scripts/remind-architecture-review.sh b/.claude/scripts/remind-architecture-review.sh new file mode 100755 index 00000000..d1f580ee --- /dev/null +++ b/.claude/scripts/remind-architecture-review.sh @@ -0,0 +1,46 @@ +#!/usr/bin/env bash +# PreToolUse hook on Bash. When the command opens a PR (`gh pr create`), surfaces a reminder +# to run the architecture-reviewer agent first for architecturally-significant changes. +# +# Purpose: the "architecture-reviewer before merge" rule (CLAUDE.md "Presence in the loop") +# proved skippable as principle alone — the ASB->RabbitMQ swap (PR #159) shipped to a PR before +# any review; the review, run only after a prompt, then caught a dead OpenTelemetry trace source. +# This is the mechanical catch at the ship moment. Non-blocking by design: significance is a +# judgment call (pattern-conforming changes legitimately skip the agent), and a hard deny on a +# command pattern can't ever let the PR through. The gate is "resolve findings before MERGE", +# and PRs are not auto-merged, so a reminder at open-time is correctly timed. +# +# See CLAUDE.md "Architecturally-significant changes get an architecture-reviewer pass". + +set -uo pipefail + +command=$(jq -r '.tool_input.command // ""' 2>/dev/null) + +# `git commit` messages routinely mention "gh pr create" in prose (this script's own commit did), +# which would false-positive on a plain substring match. A real PR-open is never part of a commit. +case "$command" in + *"git commit"*) exit 0 ;; +esac + +# Only fire when actually opening a PR. +case "$command" in + *"gh pr create"*) ;; + *) exit 0 ;; +esac + +# Feed the reminder text straight into jq as raw stdin (-R -s) to build the hook output. +# (Avoids a heredoc inside $(...), which mis-parses apostrophes on macOS bash 3.2.) +jq -Rs '{hookSpecificOutput: {hookEventName: "PreToolUse", additionalContext: .}}' <<'EOF' +ARCHITECTURE-REVIEW GATE (CLAUDE.md "Presence in the loop") -- about to open a PR. + +If this change is architecturally significant, run the `architecture-reviewer` agent on the +diff and address or explicitly defer its findings in the PR body before merge. Significant = + - adds/removes a dependency or transport, OR + - touches 3+ services, OR + - alters a cross-cutting pattern (DI, middleware, persistence, messaging, auth), OR + - modifies a Domain aggregate, OR + - the /feature-spec Significance Check would flag it. + +Pattern-conforming changes (CRUD matching an existing shape, an audit-log row, a doc-only +edit) may proceed without the agent. This is a reminder, not a block. +EOF diff --git a/.claude/settings.json b/.claude/settings.json index 2610c106..0bb827a3 100644 --- a/.claude/settings.json +++ b/.claude/settings.json @@ -50,6 +50,15 @@ "command": "/Users/joshuadell/NovaCraft/.claude/scripts/block-sync-over-async.sh" } ] + }, + { + "matcher": "Bash", + "hooks": [ + { + "type": "command", + "command": "/Users/joshuadell/NovaCraft/.claude/scripts/remind-architecture-review.sh" + } + ] } ], "PostToolUse": [ diff --git a/CLAUDE.md b/CLAUDE.md index b2349b9d..394867a3 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -200,6 +200,8 @@ Diagrams are always paired: every `docs/*.excalidraw` ships with a sibling `docs **Presence in the loop, not approval at the gate.** Reviewing the AI's finished diff is not presence — by the time you see it, the agent has already filled gaps you didn't notice. For *non-pattern-conforming* features (new bounded context, novel transport, security model change, multi-step refactor), stay present during implementation: check intermediate state, push back on inferred decisions, course-correct before the diff is too large to read honestly. For *pattern-conforming* features (CRUD endpoint following existing shape, new saga handler matching the canonical pattern), the canon + CodeRabbit + tier-3 mechanical catches do the work — gate review is sufficient. **Rule of thumb: if `/feature-spec` flagged the change as architecturally significant in its Significance Check, treat it as non-pattern-conforming and stay present.** Naming the distinction matters because "I'll review when it's done" is how three days of rework happens. +**Architecturally-significant changes get an `architecture-reviewer` pass BEFORE the PR is opened — not after, not "if I remember."** This is the operational form of "presence in the loop": the agent ([.claude/agents/architecture-reviewer.md](.claude/agents/architecture-reviewer.md)) reads the diff against the canon and returns Must-fix / Should-consider / Aligned. "Significant" = the `/feature-spec` Significance Check would flag it, OR the change adds/removes a dependency or transport, touches 3+ services, alters a cross-cutting pattern (DI, middleware, persistence, messaging, auth), or modifies a Domain aggregate. Findings are addressed or explicitly deferred in the PR body before merge. **A `PreToolUse` hook on `gh pr create` surfaces this reminder at the ship moment** (`.claude/scripts/remind-architecture-review.sh`) — the principle alone proved skippable (the ASB→RabbitMQ swap shipped to PR before review; the review then caught a dead OTel trace source), so the hook is the mechanical catch. Pattern-conforming changes (CRUD endpoint matching an existing shape, an audit-log row) skip the agent — the hook is a reminder, not a block. + **Continue is the verb that gets you in trouble. Build is not.** Prototyping is encouraged — cheap building lets you run more experiments, which is genuine leverage. The discipline is the *stop*, not the *start*: the moment a prototype stops being how you find a return and becomes the whole job (build after build, no return you can point to), it's the stop that matters. For experiments, set a token budget and a stop-time up front. When either runs out, the default answer is "we learned what we needed; we don't continue." Per Kapil Viren Ahuja: *"Nobody approves that. Nobody ever approves that. It approves itself, one month at a time."* The mechanical floor: every experimental branch ends at a defined sunset, even if the work was technically interesting. Carry-debt (maintained, secured code that nobody needed) is real. All five rules are for everyone working in this repo (humans, AI assistants, future-you). Don't wait to be asked.