Skip to content

Chore/pr 31 followup doc and fix sync#38

Merged
emeraldleaf merged 3 commits into
mainfrom
chore/pr-31-followup-doc-and-fix-sync
May 26, 2026
Merged

Chore/pr 31 followup doc and fix sync#38
emeraldleaf merged 3 commits into
mainfrom
chore/pr-31-followup-doc-and-fix-sync

Conversation

@emeraldleaf

@emeraldleaf emeraldleaf commented May 26, 2026

Copy link
Copy Markdown
Owner

Summary

PR #31 was squash-merged before these doc/tooling updates finished pushing. This PR brings main back in sync with the actual code that landed in PR #31. Zero code logic changes outside of .claude/skills/excalidraw-diagram/ renderer tooling.

What's in scope

1. Walkthroughs aligned with the code now on main

  • docs/code-flows/catalogservice.md — Flow 2 (PUT /products/{id}) Mermaid no longer shows Products.Include(Category).FirstOrDefaultAsync(...) + JOIN against categories. The handler stopped Includeing Category in PR Refactor/catalog vsa collapse #31 (it never read it). Added a Note explaining why the Include was removed. Flow 3 (gRPC ReserveStock) — the xmin-stale branch previously said "DbUpdateConcurrencyException bubbles up as gRPC Internal status." After PR Refactor/catalog vsa collapse #31's try/catch, the handler returns false and the gRPC response is ReserveStockResponse { Success = false } — same shape as insufficient stock. OrderService aborts cleanly without surfacing a 500. Diagram + Note updated.

  • docs/code-flows/orderservice.md — File inventory describes the new IDOR contract: endpoint extracts JWT NameIdentifier and passes it as RequestingBuyerId; handler's EF Where clause filters by Id AND BuyerId (non-owner → null straight from the database → 404 per anti-enumeration).

2. CLAUDE.md IDOR rule rewrite

The previous wording — "Handler loads the entity, then returns null on owner mismatch" — described the post-materialization in-memory check, which the current implementations don't use. Both ShippingService.GetShipmentByOrder and (after PR #31) OrderService.GetOrderById push the ownership predicate into the EF Where clause for tighter defense-in-depth.

The rule now distinguishes:

  • Read handlers: predicate in SQL (preferred — non-owner rows never leave the database, no in-memory comparison step that a buggy refactor could weaken)
  • Write handlers: tracked load + in-memory check (required because they need the tracked entity to mutate)

Reference templates list expanded with explicit file pairings. .claude/agents/architecture-reviewer.md IDOR heuristic synced — a read handler with C#-only ownership check is now Should-consider (still satisfies the null → 404 external contract, but structurally weaker; recommend tightening to the SQL-predicate shape).

3. Excalidraw diagram sync

  • docs/hybridcache-flow.excalidraw — Three elements patched: the write-step (repo.UpdateAsync(product)context.SaveChangesAsync()), the cache-miss factory body (product = await repo.GetByIdAsync(id, ct); return product is null ? null : new ProductDto { ... } → inline context.Products.AsNoTracking().Where(...).Select(...).FirstOrDefaultAsync(ct)), and the source-path footer (CatalogService.Application/Handlers/{...}Handler.csCatalogService/Features/{...}.cs).
  • docs/service-request-flow.excalidraw — Three elements patched: the DB step (Repository.UpdateAsync → DbContext.SaveChangesAsynccontext.SaveChangesAsync()), the annotation that referenced "Repository implementation calls context.SaveChangesAsync()..." → handler-direct wording, and the handler-step annotation listing "repositories" as a constructor-injected dependency → DbContext-direct.
  • Both text (rendered) and originalText (Excalidraw's re-wrap source) fields updated for every change, so the diagrams won't revert on next open.

4. Regenerated SVG renders

docs/hybridcache-flow.svg and docs/service-request-flow.svg regenerated via .claude/scripts/rebuild-diagrams.sh. Verified — none of the four stale strings (repo.UpdateAsync, Repository.UpdateAsync, repo.GetByIdAsync, CatalogService.Application/Handlers) remain; the new DbContext-direct text is present.

5. Excalidraw renderer tooling unblock

The rebuild script had been failing with a generic TimeoutError on window.__moduleReady === true. Root cause: the template imported @excalidraw/excalidraw?bundle from esm.sh unpinned, which resolves to 0.18.1 today — and 0.18.1's transitive module init hangs in headless Chromium with no console errors, no failed requests, no diagnostics.

  • render_template.html — pinned to @excalidraw/excalidraw@0.18.0?bundle. 0.18.0 resolves cleanly in <10s.
  • render_excalidraw.py — bumped wait_for_function timeout from 30s to 90s as belt-and-suspenders, AND added three Playwright listeners (console, pageerror, requestfailed) routing to stderr. Next time the renderer hangs, the actual cause (CDN issue, JS error, hung module) will surface in the script output instead of a bare TimeoutError.

6. .gitignore — docs/*.png

The rebuild script generates both .svg (committed, inlined into README + docs/code-flows.md + docs/code-flows/catalogservice.md) and .png (1–2 MB local-only side-effects, not referenced anywhere). Other diagrams have always followed the SVG-only convention; this rule makes it explicit.

What's NOT in scope

  • No application code changes
  • No test changes
  • No CI/CD changes
  • No package/dependency changes (the renderer tooling lives under .claude/skills/ and isn't part of the .NET build)

The 4 code-correctness fixes from PR #31's CodeRabbit review (GetOrderById IDOR predicate in SQL, ReserveStock concurrency catch, SearchProducts empty-term guard, UpdateProduct unused Include removal) are already on main as part of the PR #31 squash.

Test plan

  • CI build green (no .cs files changed; should be a fast green)
  • Visual diff on the two regenerated SVGs in the rendered PR view — confirm the new strings show
  • (Optional, requires uv + Playwright) Re-run .claude/scripts/rebuild-diagrams.sh --force docs/hybridcache-flow.excalidraw locally to confirm the renderer no longer hangs and produces matching SVG output

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Documentation

    • Enhanced authorization pattern guidance for secure entity access control
    • Clarified code flow documentation for order and catalog service operations
    • Updated architecture reference materials with detailed ownership verification patterns
  • Chores

    • Pinned diagram rendering library version for improved stability
    • Enhanced rendering diagnostic output for better troubleshooting
    • Updated configuration to exclude auto-generated diagram outputs

Review Change Stack

emeraldleaf and others added 3 commits May 25, 2026 23:56
Three doc/diagram updates that the PR #31 code changes made stale:

1. docs/code-flows/catalogservice.md — Flow 2 (PUT /products/{id})
   Mermaid showed `Products.Include(Category).FirstOrDefaultAsync(...)`
   with a SELECT * FROM products JOIN categories. After PR #31 removed
   the unused .Include(p => p.Category), the actual query is
   `Products.FirstOrDefaultAsync(...)` with no JOIN. Updated the diagram
   line + added a Note explaining why the Include was removed.

2. docs/code-flows/catalogservice.md — Flow 3 (gRPC ReserveStock)
   The xmin-stale branch said "DbUpdateConcurrencyException bubbles up as
   gRPC Internal status — OrderService sees the call fail and aborts the
   order." That described the pre-fix behavior. After PR #31's try/catch
   around SaveChangesAsync, the handler now returns false and the gRPC
   response is `ReserveStockResponse { Success = false }` — the same shape
   as insufficient stock. OrderService aborts cleanly without surfacing a
   500. Updated the Note + added the explicit return-path lines in the
   diagram.

3. docs/code-flows/orderservice.md — file inventory
   Updated the OrderEndpoints.cs and GetOrderById.cs entries to describe
   the new IDOR contract: endpoint extracts JWT NameIdentifier and passes
   it as RequestingBuyerId; handler's EF Where clause filters by both
   Id AND BuyerId (non-owner → null straight from the database → 404
   per anti-enumeration).

4. CLAUDE.md "Authorization" rule (Security Requirements)
   The previous wording — "Handler loads the entity, then returns null
   on owner mismatch" — described the post-materialization in-memory
   check, which the current implementations don't use. Both ShippingService
   .GetShipmentByOrder and (after PR #31) OrderService.GetOrderById push
   the ownership predicate into the EF Where clause for tighter
   defense-in-depth. The rule now distinguishes:
     - Read handlers: predicate in SQL (preferred)
     - Write handlers: tracked load + in-memory check (required because
       they need the tracked entity to mutate)
   Reference templates list expanded with explicit file pairings + which
   pattern each demonstrates.

5. .claude/agents/architecture-reviewer.md — IDOR heuristic update
   Synced with the CLAUDE.md rule rewrite. A read handler whose ownership
   check is in C# rather than SQL is now flagged as Should-consider
   (structurally weaker), not Must-fix (still satisfies the external
   null → 404 contract). Reference templates updated.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two diagrams had text from the repository-wrapper era that no longer matches
the code:

  docs/hybridcache-flow.excalidraw
    - "repo.UpdateAsync(product) → SaveChangesAsync"
      → "context.SaveChangesAsync() (xmin / RowVersion check fires here)"
    - Factory body "product = await repo.GetByIdAsync(id, ct); return ...
       new ProductDto { ... project ... }"
      → "return context.Products.AsNoTracking().Where(p => p.Id == id)
         .Select(p => new ProductDto { ... }).FirstOrDefaultAsync(ct)"
    - Source path "CatalogService.Application/Handlers/{GetProductById,
       UpdateProduct,ReserveStock}Handler.cs"
      → "CatalogService/Features/{GetProductById,UpdateProduct,ReserveStock}.cs"
      (after the catalog VSA collapse, those files moved out of the
       Application/Handlers/ subdirectory of the deleted CatalogService.
       Application project)

  docs/service-request-flow.excalidraw
    - Step label "Repository.UpdateAsync → DbContext.SaveChangesAsync
       (entity write + outbox row)"
      → "context.SaveChangesAsync() (entity write + outbox row — atomic
        via Wolverine)"
    - Annotation "Repository implementation calls context.SaveChangesAsync()..."
      → "Handler calls context.SaveChangesAsync() directly (no repository
        wrapper). EF generates UPDATE .. WHERE Id=@id AND RowVersion=@orig ..."
    - Handler-step annotation "Constructor-injected dependencies (repositories,
       IMessageBus, ICatalogClient for gRPC, ILogger)"
      → "DbContext directly (no IFooRepository wrapper — DbContext IS
        Unit-of-Work, DbSet<T> IS Repository), IMessageBus, ICatalogClient
        (gRPC port — substituted in tests), ILogger"

Both `text` (rendered, possibly word-wrapped) and `originalText` (the
authoritative source Excalidraw re-wraps from on open) fields were updated,
so the next person to open the .excalidraw file sees the new text rather
than the .excalidraw re-rendering back to the old original.

KNOWN DEFERRED: docs/hybridcache-flow.svg and docs/service-request-flow.svg
are the rendered exports inlined into README.md + docs/code-flows.md + docs/
code-flows/catalogservice.md, and they still contain the four stale strings
above. Excalidraw exports are not trivially scriptable from CLI (need a
browser/jsdom + canvas), so the SVGs trail until manually regenerated:

  1. Open the .excalidraw file in VS Code (pomdtr.excalidraw-editor) or
     excalidraw.com
  2. File → Export → SVG (or the "Export image" button)
  3. Overwrite the existing .svg next to the .excalidraw file
  4. Commit

Not blocking — the rendered docs show slightly stale labels until then,
but the .excalidraw source-of-truth is correct.

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

Two related changes that together unblock SVG regeneration for the
Excalidraw .excalidraw sources updated in c1a9e04:

1. .claude/skills/excalidraw-diagram/references/render_template.html
   Pinned the Excalidraw import from
     https://esm.sh/@excalidraw/excalidraw?bundle (resolves to latest =
     0.18.1 today, which hangs during transitive module init in headless
     Chromium — `window.__moduleReady` never gets set, even at a 90s
     timeout, with no console errors or failed requests)
   to
     https://esm.sh/@excalidraw/excalidraw@0.18.0?bundle (resolves cleanly
     in <10s).

2. .claude/skills/excalidraw-diagram/references/render_excalidraw.py
   - Bumped the `wait_for_function("window.__moduleReady === true")`
     timeout from 30000 to 90000. Belt + suspenders alongside the pin —
     warm-cache resolves fast (~5s), cold-cache CDN fetches under load
     can stretch to ~60s, 90s is a comfortable ceiling.
   - Added page.on("console"), page.on("pageerror"),
     page.on("requestfailed") hooks routing to stderr. When the render
     ever hits a CDN issue again, the actual cause (CORS, 404, JS error,
     hung module init) will surface in the script output instead of the
     uninformative generic TimeoutError.

3. docs/hybridcache-flow.svg + docs/service-request-flow.svg
   Regenerated via .claude/scripts/rebuild-diagrams.sh after the pin
   landed. Verified: all four stale strings (repo.UpdateAsync,
   Repository.UpdateAsync, repo.GetByIdAsync,
   CatalogService.Application/Handlers) are gone; the new
   DbContext-direct shape (context.SaveChangesAsync, "DbContext directly",
   "no IFooRepository wrapper") is present.

4. .gitignore
   Added `docs/*.png` — the rebuild-diagrams.sh script generates both
   .svg (committed, inlined into README + docs/code-flows.md) and .png
   (local-only render side-effect — ~1-2 MB binaries that aren't referenced
   anywhere). The other Excalidraw sources in docs/ have always followed
   this convention; the rule just makes it explicit.

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

codecov Bot commented May 26, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@coderabbitai

coderabbitai Bot commented May 26, 2026

Copy link
Copy Markdown

Walkthrough

This PR refines IDOR ownership-check patterns for read vs. write handlers, cascades the updated patterns through code-flow documentation and service diagrams, and hardens the Excalidraw rendering pipeline with improved timeouts, diagnostics, and dependency pinning.

Changes

IDOR Ownership-Check Pattern and Code-Flow Documentation

Layer / File(s) Summary
IDOR ownership-check pattern documentation
.claude/agents/architecture-reviewer.md, CLAUDE.md
Agent checklist and main documentation now mandate ownership predicates inside EF Where clauses for read handlers (non-owner rows never leave the database), in-memory ownership checks after tracked load for write handlers, and 404 (not 403) translation for null/false results, with expanded endpoint/feature reference mappings.
Service-request flow diagram — DbContext-first unit-of-work
docs/service-request-flow.excalidraw
Handler dependencies now explicitly show direct DbContext injection (no repository wrapper), with DbContext as Unit-of-Work and DbSet<T> as Repository. Handler calls context.SaveChangesAsync() directly, outbox rows flush atomically via Wolverine transaction integration, and EF concurrency-token checks fire during save.
CatalogService code-flow and hybrid-cache diagram updates
docs/code-flows/catalogservice.md, docs/hybridcache-flow.excalidraw
CatalogService PUT handler now uses tracked EF load without unnecessary Include(Category). ReserveStock gRPC concurrency conflicts are caught in-memory, returning Success = false instead of propagating as gRPC Internal status. Hybrid-cache diagram updated to show context.Products LINQ query projecting directly to ProductDto, and source paths now reference Feature files instead of application handlers.
OrderService code-flow documentation — buyer ownership predicate
docs/code-flows/orderservice.md
GetOrderById documentation explicitly documents the EF Where clause buyer-ownership predicate (BuyerId == RequestingBuyerId), preventing enumeration with non-owner queries returning null and mapping to 404, while keeping existing DTO projection via AsNoTracking() + .Select(...).
Excalidraw rendering infrastructure — timeout and logging
.claude/skills/excalidraw-diagram/references/render_excalidraw.py, .claude/skills/excalidraw-diagram/references/render_template.html, .gitignore
Playwright rendering hardens event handling with console/error/network-request listeners routing to stderr. Module-readiness wait timeout increases from 30s to 90s with comments explaining esm.sh CDN/bundling modes. Excalidraw module pinned to version 0.18.0 in HTML template. Gitignore updated to exclude local PNG render outputs under docs/.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related PRs

  • emeraldleaf/NextAurora#27: Updates documentation for the same IDOR ownership-check pattern (read/write split and 404 translation) in agent rules and main CLAUDE docs.
  • emeraldleaf/NextAurora#30: Refines IDOR/ownership handling rules in architecture documentation, tightening the same read-vs-write and 404-vs-403 expectations.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Title check ❓ Inconclusive The title 'Chore/pr 31 followup doc and fix sync' refers to the actual primary changes—documentation updates and tooling synchronization following PR #31—but is somewhat terse and generic. Consider a more descriptive title such as 'docs: sync architecture rules and diagrams with PR #31 implementation changes' to better signal the scope and nature of the documentation refresh.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch chore/pr-31-followup-doc-and-fix-sync

Warning

Review ran into problems

🔥 Problems

Git: Failed to clone repository. Please run the @coderabbitai full review command to re-trigger a full review. If the issue persists, set path_filters to include or exclude specific files.


Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@CLAUDE.md`:
- Around line 41-44: Add the required audit flag to the CLAUDE.md update by
inserting the exact note "Run /check-rules locally to audit paraphrases against
this diff." adjacent to the changed policy text (the paragraph describing Read
handlers, Write handlers, 404 vs 403, and the reference templates like
OrderEndpoints.cs / Features/GetOrderById.cs, ShippingEndpoints.cs /
Features/GetShipmentByOrder.cs, CatalogEndpoints.cs / Features/UpdateProduct.cs)
so reviewers see the prompt to run /check-rules locally when this
paraphrase/content is changed.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: c3d2abe7-3950-4c32-b5c2-c6ac009caac1

📥 Commits

Reviewing files that changed from the base of the PR and between 2892538 and 6028a93.

⛔ Files ignored due to path filters (2)
  • docs/hybridcache-flow.svg is excluded by !**/*.svg
  • docs/service-request-flow.svg is excluded by !**/*.svg
📒 Files selected for processing (9)
  • .claude/agents/architecture-reviewer.md
  • .claude/skills/excalidraw-diagram/references/render_excalidraw.py
  • .claude/skills/excalidraw-diagram/references/render_template.html
  • .gitignore
  • CLAUDE.md
  • docs/code-flows/catalogservice.md
  • docs/code-flows/orderservice.md
  • docs/hybridcache-flow.excalidraw
  • docs/service-request-flow.excalidraw

Comment thread CLAUDE.md
@emeraldleaf
emeraldleaf requested a review from Copilot May 26, 2026 11:46
@emeraldleaf
emeraldleaf merged commit fff2860 into main May 26, 2026
7 checks passed

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

Updates architecture/code-flow documentation and diagram rendering tooling to reflect recent refactors (direct DbContext usage, EF query shapes, and scoped-access security patterns), while making Excalidraw diagram exports more reliable.

Changes:

  • Refreshes service-request and HybridCache diagrams/docs to match new handler/repository patterns and message/outbox semantics.
  • Tightens documented IDOR guidance to prefer ownership predicates inside EF Where clauses for read handlers.
  • Stabilizes Excalidraw rendering by pinning the CDN module version and improving render-time diagnostics/timeouts.

Reviewed changes

Copilot reviewed 8 out of 11 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
docs/service-request-flow.excalidraw Updates diagram text to describe direct DbContext usage and Wolverine outbox atomicity.
docs/hybridcache-flow.svg Regenerated diagram SVG to reflect new CatalogService feature file locations and EF query shape.
docs/hybridcache-flow.excalidraw Updates Excalidraw source for the HybridCache flow (source paths + factory/query/write steps).
docs/code-flows/orderservice.md Clarifies endpoint/handler security contract (buyer-scoped predicate + 404 anti-enumeration behavior).
docs/code-flows/catalogservice.md Updates sequence diagrams to remove unnecessary Include join and document concurrency exception handling behavior.
CLAUDE.md Refines security guidance for scoped reads/writes with concrete EF predicate patterns and references.
.gitignore Ignores locally-generated Excalidraw PNG renders under docs/.
.claude/skills/excalidraw-diagram/references/render_template.html Pins Excalidraw ESM import to a known-good version to avoid headless hangs.
.claude/skills/excalidraw-diagram/references/render_excalidraw.py Adds console/request failure logging and increases module-load wait timeout.
.claude/agents/architecture-reviewer.md Aligns the reviewer checklist with the updated IDOR/ownership-predicate guidance.

Comment on lines +152 to 155
page.on("requestfailed", lambda req: print(f" [request failed] {req.url} — {req.failure}", file=sys.stderr))

# Load the template
page.goto(template_url)
# the browser pulls in many transitive modules afterwards; 30s was
# too tight on slower networks. 90s is a comfortable ceiling — if we
# ever hit that, the CDN is genuinely down rather than slow.
page.wait_for_function("window.__moduleReady === true", timeout=90000)
Comment on lines +1695 to +1696
"text": "Handler calls context.SaveChangesAsync() directly (no repository wrapper). EF generates UPDATE\n.. WHERE Id=@id AND RowVersion=@orig (SQL Server) or AND xmin=@orig (Postgres) — concurrency token in the WHERE clause. Outbox envelopes staged during the handler flush in the SAME transaction via Wolverine UseEntityFrameworkCoreTransactions.",
"originalText": "Handler calls context.SaveChangesAsync() directly (no repository wrapper). EF generates UPDATE\n.. WHERE Id=@id AND RowVersion=@orig (SQL Server) or AND xmin=@orig (Postgres) — concurrency token in the WHERE clause. Outbox envelopes staged during the handler flush in the SAME transaction via Wolverine UseEntityFrameworkCoreTransactions.",
@emeraldleaf
emeraldleaf deleted the chore/pr-31-followup-doc-and-fix-sync branch June 4, 2026 00:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants