Skip to content
1 change: 1 addition & 0 deletions .claude/audits/INDEX.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 |
46 changes: 46 additions & 0 deletions .claude/scripts/remind-architecture-review.sh
Original file line number Diff line number Diff line change
@@ -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
9 changes: 9 additions & 0 deletions .claude/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
]
Comment thread
emeraldleaf marked this conversation as resolved.
}
],
"PostToolUse": [
Expand Down
2 changes: 2 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
Loading