Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
660 commits
Select commit Hold shift + click to select a range
9a938b4
feat(chat-1): CreateChannel feature + ChatMappers + wire Mediator mar…
iammukeshm May 12, 2026
4946f42
feat(chat-1): channel management endpoints — Update, Archive, Restore…
iammukeshm May 12, 2026
f3c392f
feat(chat): channel/message queries + edit/delete/read-marker endpoints
iammukeshm May 12, 2026
d666c9a
test(arch): include Chat module in contracts purity + host boundary c…
iammukeshm May 12, 2026
389cd36
test(chat): unit tests for ChatChannel + Message aggregate invariants
iammukeshm May 12, 2026
e218f3b
test(chat): integration tests for channels + messages (23 tests)
iammukeshm May 12, 2026
b071013
feat(chat-2): SignalR AppHub + Redis backplane + handler broadcasts
iammukeshm May 12, 2026
261b907
test(chat-2): SignalR realtime integration tests + auth fix
iammukeshm May 12, 2026
bdbf60a
feat(chat-3): scaffold Notifications module + InitialNotifications mi…
iammukeshm May 12, 2026
7a63b9b
feat(chat-3): Notifications REST endpoints (list/count/mark/mark-all)
iammukeshm May 12, 2026
adba177
feat(chat-3): MessageMention domain + @username parsing in SendMessage
iammukeshm May 12, 2026
b555dcc
feat(chat-3): MentionedInChannel handler + integration tests (3)
iammukeshm May 12, 2026
d8841c7
feat(chat-4): thread replies endpoint + message reactions
iammukeshm May 12, 2026
c3d1614
feat(chat-4): full-text message search via Postgres tsvector
iammukeshm May 12, 2026
c7a1f72
test(chat-4): typing-indicator integration tests (3)
iammukeshm May 12, 2026
1fe8499
feat(chat-4): ChatChannelFileAccessPolicy for message attachments
iammukeshm May 12, 2026
16734df
feat(chat-4): dashboard /chat page + NotificationBell + RealtimeProvider
iammukeshm May 12, 2026
33d2ace
feat(chat-4): UI polish pass — unread divider, jump pill, live status…
iammukeshm May 13, 2026
84d65e0
fix(chat-4): composer sent empty body when optimistic clear raced the…
iammukeshm May 13, 2026
688bc18
feat(chat-4): resolve user IDs to real names in messages, DM titles, …
iammukeshm May 13, 2026
141e6f9
fix(cors): allow credentialed requests in dev so SignalR negotiate works
iammukeshm May 13, 2026
2ea0dbe
feat(chat-4): topbar chat-unread badge + global toast on incoming mes…
iammukeshm May 13, 2026
152d280
feat(chat-4): @mention autocomplete in composer
iammukeshm May 13, 2026
a16a066
feat(chat-4): redesign chat toast — opt out of .fsh-toast tone-rail
iammukeshm May 13, 2026
39f8e71
fix(chat-4): chat toasts vanished after the previous redesign — kept …
iammukeshm May 13, 2026
fe80223
feat(chat-4): MessageList lands at the latest message on channel open
iammukeshm May 13, 2026
d09a171
feat(realtime): rebuild SignalR hub on token rotation + warn on missi…
iammukeshm May 13, 2026
919bb50
feat(files): paginate ListTrashedFiles via PagedResponse
iammukeshm May 13, 2026
6a43f8f
test(chat): integration tests for POST /messages mirroring dashboard …
iammukeshm May 13, 2026
d334d57
docs(chat-4): spec for deferred UI items — ThreadPanel, mention profi…
iammukeshm May 13, 2026
814b9c4
docs(chat-4): implementation plan for deferred UI items
iammukeshm May 13, 2026
2b13ce2
feat(chat-4): partner avatar on 1-on-1 DM rail rows
iammukeshm May 13, 2026
8aa4b63
feat(chat-4): mention pill opens profile peek with Open DM action
iammukeshm May 13, 2026
2c6d52d
feat(chat-4): ThreadPanel overlay — wire onReply through to a reply s…
iammukeshm May 13, 2026
dbb1115
feat(chat-4): Teams-style inline replies — quote in composer + inline…
iammukeshm May 13, 2026
93abc2f
feat(chat-4): Teams-style chat — right-aligned own bubbles, flat feed…
iammukeshm May 13, 2026
9273d13
feat(chat-4): hover-only merged-row time + jump-to-parent on reply pr…
iammukeshm May 13, 2026
5ad9a5b
feat(chat-4): inline search overlay + smooth scroll on jump-to-parent
iammukeshm May 13, 2026
7c0577a
feat(chat-5): optimistic send — message lands in feed before the netw…
iammukeshm May 13, 2026
53e1156
feat(chat-5): channel settings dialog — rename, members, archive/leave
iammukeshm May 13, 2026
be1d22d
feat(chat-5): message pagination — load older on scroll-to-top
iammukeshm May 13, 2026
1957eef
feat(ui): modernize sonner toasts — frosted gradient-border + tone-ti…
iammukeshm May 13, 2026
3700111
feat(chat-5): file attachments in composer + message bubbles
iammukeshm May 13, 2026
077fb20
feat(chat-5): read receipts under the caller's latest top-level message
iammukeshm May 13, 2026
9137cee
feat(chat-5): mobile responsive sweep — rail / message column alterna…
iammukeshm May 13, 2026
d8ea0d6
fix(chat-5): increase merged-row spacing — bubbles no longer overlap
iammukeshm May 13, 2026
c7402a5
feat(chat-5): pin messages — domain + endpoints + realtime + UI
iammukeshm May 13, 2026
ff5543a
feat(presence): online dots driven by an in-memory PresenceTracker
iammukeshm May 13, 2026
69f2a64
test(chat-5): integration tests for pin messages + presence (18 new) …
iammukeshm May 13, 2026
b828875
fix(chat-5): live read receipts + scope presence broadcasts to tenant…
iammukeshm May 13, 2026
38560bf
feat(infra): FSH.Starter.DbMigrator — dedicated console for applying …
iammukeshm May 13, 2026
0831d10
fix(chat-5): dedupe own-send temp against the SignalR echo to kill th…
iammukeshm May 13, 2026
d130f3f
feat(infra): wire DbMigrator into Aspire + advisory lock + wait-for-DB
iammukeshm May 13, 2026
b5d3818
fix(infra): DbMigrator end-to-end smoke clean — DI + logging + IHoste…
iammukeshm May 13, 2026
011a6e9
fix(chat-5): more breathing room between consecutive bubbles
iammukeshm May 13, 2026
f691c9d
fix(chat-5): bubble halo + much more breathing room between merged rows
iammukeshm May 13, 2026
d78f7d8
fix(chat-5): make merged-row spacing uniform — drop conditional row p…
iammukeshm May 13, 2026
0f4d1f8
fix(chat-5): presign chat attachment URLs at upload so SendMessage do…
iammukeshm May 13, 2026
9edaafe
fix(chat-5): allow attachment-only messages + skip markRead for optim…
iammukeshm May 13, 2026
7fd285a
test(catalog): integration tests for the product CRUD endpoints
iammukeshm May 13, 2026
76fec24
fix(persistence): soft-delete interceptor leaves owned-type reference…
iammukeshm May 13, 2026
e3fac31
fix(migrator): clear CI -warnaserror analyzer errors
iammukeshm May 13, 2026
122a44d
build: treat warnings as errors locally to match CI
iammukeshm May 13, 2026
eda868b
build(aspire): bump Aspire to 13.3.2
iammukeshm May 14, 2026
be2afee
refactor(multitenancy): remove API auto-migration; DbMigrator is the …
iammukeshm May 14, 2026
22cf457
fix(chat): align MessageTests with attachment-only-message contract
iammukeshm May 14, 2026
bdfee3b
build: bump runtime packages to 10.0.8 / 10.6.0; adapt Testcontainers…
iammukeshm May 14, 2026
1f7f4d8
docs: backend health audit — 2026-05-14 (Grade A, 97/100)
iammukeshm May 14, 2026
a432f4c
docs: add CONTRIBUTING.md and SECURITY.md
iammukeshm May 14, 2026
f85d266
fix(docs/deps): patch 8 Dependabot alerts via npm audit fix
iammukeshm May 14, 2026
8483312
test: add Catalog/Notifications/Billing endpoint tests; fix README do…
iammukeshm May 14, 2026
7e0a764
build: finish Testcontainers 4.11 API adaptation in 3 remaining files
iammukeshm May 14, 2026
6e9e22d
fix(security): resolve 5 CodeQL alerts (1 high, 4 medium)
iammukeshm May 15, 2026
dabecf4
fix(ci): use ContainerImageTags (plural) in DbMigrator smoke publish
iammukeshm May 15, 2026
bb370a7
fix(ci): drop legacy PublishProfile=DefaultContainer, use PublishCont…
iammukeshm May 15, 2026
e08f3ff
feat: multitenancy hardening, impersonation, admin app rebuild, demo …
iammukeshm May 18, 2026
169bb4e
fix(security): bump devalue 5.6.4 → 5.8.1 (GHSA-77vg-94rm-hx3p)
iammukeshm May 18, 2026
861381e
fix(migrator): resolve JwtOptions validation failure in Aspire orches…
cesarcastrocuba May 18, 2026
4b307cd
feat(v1-ui): admin + dashboard gap closure with Playwright coverage
iammukeshm May 18, 2026
59aaaa0
feat(v1-backend): v1 P0 hardening pass (security + correctness)
iammukeshm May 18, 2026
e2a740c
fix(migrator): inject JwtOptions placeholder when SigningKey is empty
iammukeshm May 18, 2026
ab38a51
build(aspire): bump Aspire.Hosting.* + AppHost SDK 13.3.2 → 13.3.3
iammukeshm May 18, 2026
40352a4
fix(ui): audit-detail crash, banner redesign, cursor affordance
iammukeshm May 18, 2026
ed6e699
refactor(admin/audits): redesign /audits/:id for the forensic-record …
iammukeshm May 18, 2026
68e5884
feat(admin/impersonation): re-open session button for closed-browser …
iammukeshm May 18, 2026
59265c3
docs(spec): production docker-compose deploy design
iammukeshm May 18, 2026
5c00d18
docs(spec): docs site rebuild — Astro + Tailwind 4, blog visual twin
iammukeshm May 18, 2026
95ba548
docs(plan): implementation plan for production docker-compose deploy
iammukeshm May 18, 2026
b8ffa2a
docs(plan): docs site rebuild — 25-task implementation plan
iammukeshm May 18, 2026
5f99319
feat(docs): scaffold project files (package.json, tsconfig, gitignore)
iammukeshm May 18, 2026
53788bc
feat(docs): install astro 6 + tailwind 4 + integrations
iammukeshm May 18, 2026
dbfdda6
feat(docs): import design tokens verbatim from codewithmukesh/blog
iammukeshm May 18, 2026
d5f0d85
feat(docs): swap primary token to green (#15803d / soft #16a34a)
iammukeshm May 18, 2026
5f359cf
feat(docs): rebrand gradient to green; drop unused tailwind plugins
iammukeshm May 18, 2026
846101d
feat(docs): wire astro.config with green-tinted EC overrides
iammukeshm May 18, 2026
6477a63
feat(docs): site config, content schema, modified-time remark plugin
iammukeshm May 18, 2026
957f4d9
feat(docs): BaseLayout with SEO meta, theme init, skip-link
iammukeshm May 18, 2026
ce5fbb4
feat(docs): ThemeToggle (Astro component, mirrors blog) with View Tra…
iammukeshm May 18, 2026
d81fd80
feat(docs): Header — visual twin of blog with docs nav
iammukeshm May 18, 2026
2755287
feat(docs): Footer — visual twin with project links (no newsletter)
iammukeshm May 18, 2026
04cb11a
feat(docs): MarketingLayout + 404 page
iammukeshm May 18, 2026
345a5d0
feat(docs): landing page with placeholder hero
iammukeshm May 18, 2026
eb38481
fix(docs): adapt config for astro 6 — static output, drop formkit import
iammukeshm May 18, 2026
2ba613b
feat(docs): seed initial MDX content (overview + per-section stubs)
iammukeshm May 18, 2026
b926c40
build(docker): tighten root .dockerignore for frontend + test artifacts
iammukeshm May 18, 2026
9eb560b
feat(docs): sidebar tree builder derived from content collection
iammukeshm May 18, 2026
abcbf49
feat(docs): Sidebar + TOC + PageHeader + Breadcrumbs + PrevNext
iammukeshm May 18, 2026
873161d
feat(docs): MDX component map (Callout, CodeGroup)
iammukeshm May 18, 2026
b7fdf7a
feat(docs): DocsLayout + dynamic [...slug] route
iammukeshm May 18, 2026
a010aa1
refactor(admin): load /config.json at boot for runtime-configurable A…
iammukeshm May 18, 2026
0380fd2
feat(docs): pagefind-powered search (Orama swapped due to Astro 6 inc…
iammukeshm May 18, 2026
47aa12f
feat(docs): full landing page (Hero, FeatureGrid, InstallSnippet, Fin…
iammukeshm May 18, 2026
79adfe6
feat(docs): favicon + Cloudflare Pages config (wrangler.toml)
iammukeshm May 18, 2026
da5c3ec
refactor(dashboard): load /config.json at boot for runtime-configurab…
iammukeshm May 18, 2026
83857a5
feat(docs): README + final purple-hex comment cleanup
iammukeshm May 18, 2026
e1308b1
fix(docs): zod 4 strict default — provide explicit sidebar object
iammukeshm May 18, 2026
d2a1d96
build(admin): Dockerfile with nginx + runtime /config.json envsubst
iammukeshm May 18, 2026
535e100
fix(docs): wire Astro 6 <Font> components — auto-injection removed in…
iammukeshm May 18, 2026
727d74a
build(dashboard): Dockerfile with nginx + runtime /config.json envsubst
iammukeshm May 18, 2026
59b513e
build(api): chiseled runtime + APP_UID; bump bases off preview
iammukeshm May 18, 2026
07e08f6
build(migrator): chiseled Dockerfile + APP_UID + csproj container har…
iammukeshm May 18, 2026
be1b89d
build(deploy): docker-compose .env.example with all knobs documented
iammukeshm May 18, 2026
7cea2e9
feat(docs): redesign landing — conversion-focused with modern code wi…
iammukeshm May 18, 2026
c897e42
build(deploy): postgres init SQL — required extensions
iammukeshm May 18, 2026
7ec33c3
build(deploy): production docker-compose.yml — full stack on one host
iammukeshm May 18, 2026
a13ee83
docs(deploy): five-minute docker-compose deploy guide
iammukeshm May 18, 2026
3f2d202
fix(docs): theme toggle, nav active, alignment, code panel polish
iammukeshm May 18, 2026
896aa69
fix(docs): use blog's exact MDX codeblock style (drop custom code-win…
iammukeshm May 18, 2026
90a6fa3
fix(deploy): resolve 3 issues found during e2e smoke test
iammukeshm May 18, 2026
4130e15
test(deploy): end-to-end docker-compose smoke verified locally
iammukeshm May 18, 2026
9846e0c
docs: point README at deploy/docker for the production deploy story
iammukeshm May 18, 2026
4246fa3
feat(docs): redesign CodeFirst as VS Code-style editor with file tree
iammukeshm May 18, 2026
af9be09
docs(spec): editorial alignment of docs site with codewithmukesh/blog
iammukeshm May 19, 2026
fbe7fe5
fix(container): use numeric UID 1654 in <ContainerUser>
iammukeshm May 19, 2026
daa580c
feat(docs): editorial alignment with codewithmukesh/blog
iammukeshm May 19, 2026
2b6fd54
feat(docs): redesign Hero with animated terminal + stat cards
iammukeshm May 19, 2026
8c4fe34
feat(docs): header nav — add Home, drop Reference
iammukeshm May 19, 2026
345fa30
feat(docs): wire FullStackHero logo + full favicon set
iammukeshm May 19, 2026
da50240
feat(docs): drop "/docs" suffix from header brand link
iammukeshm May 19, 2026
d82a342
feat(docs): unify all landing sections under Hero's design language
iammukeshm May 19, 2026
34a3271
feat(docs): SEO/GEO foundation + fact-checked accuracy pass
iammukeshm May 19, 2026
82de790
feat(docs): copy rewrite — developer-centric + conversion-focused
iammukeshm May 19, 2026
6a5a461
feat(docs): redesign sidebar + TOC as numbered chapter cards
iammukeshm May 19, 2026
1cb49fe
feat(docs): restructure sections + add full Getting Started flow
iammukeshm May 19, 2026
4e08d42
feat(docs): tighten typography to docs-standard scale
iammukeshm May 19, 2026
4dc33a5
fix(docs): switch wrangler to Workers Static Assets
iammukeshm May 19, 2026
9e3aa31
feat(docs): lowercase fullstackhero brand + GitHub stars on landing
iammukeshm May 19, 2026
e09248a
feat(docs): mobile docs nav with slide-up sheet
iammukeshm May 19, 2026
b2ef106
fix(docs): drop placeholder favicon.svg — use brand PNGs/ICO
iammukeshm May 19, 2026
3489952
feat(docs): tier-1 SEO + GEO code wins
iammukeshm May 19, 2026
d512da5
feat(docs): nav Docs link points straight to introduction
iammukeshm May 19, 2026
e035f93
feat(docs): section index pages with auto-generated card grids
iammukeshm May 19, 2026
de5b05d
feat(docs): track-1 content — comparison pages + getting-started SEO
iammukeshm May 19, 2026
b5d9c0c
feat(docs): /docs/ overview hub with chapter-plate category cards
iammukeshm May 19, 2026
1f8e5ef
fix(docs): wire .not-prose utility so card grids escape prose styles
iammukeshm May 19, 2026
45c3739
docs(modules): 10 module deep-dive pages — full first drafts
iammukeshm May 19, 2026
48c461b
docs(building-blocks): 12 building-block reference pages
iammukeshm May 19, 2026
faca251
docs(architecture): 4 architecture pillar pages
iammukeshm May 19, 2026
b3555ca
feat(docs): section cards match chapter-plate aesthetic + fix not-prose
iammukeshm May 19, 2026
86c6203
docs(sections): testing + security + cross-cutting-concerns full pages
iammukeshm May 19, 2026
e1a04d2
docs(cross-cutting): split into 11 per-topic pages
iammukeshm May 19, 2026
9f84d41
docs(security): split into 8 per-topic pages
iammukeshm May 19, 2026
ea79270
docs(testing): split into 6 per-topic pages
iammukeshm May 19, 2026
6f08047
docs(frontend + guides): impersonation walkthrough + frontend split +…
iammukeshm May 19, 2026
e5ac51b
fix(docs): hide screenshot placeholders in production builds
iammukeshm May 19, 2026
519d664
feat(dashboard+backend): v1 UI polish, file visibility/sharing, permi…
iammukeshm May 21, 2026
1184865
fix(tests): update role helpers to deserialize PagedResponse<RoleDto>
iammukeshm May 21, 2026
aa13231
feat(dashboard): A+ rollup — deps + a11y + perf + tokens + UX
iammukeshm May 21, 2026
3941ccc
feat(docs): page view counter (D1) + frontmatter dates + hero copy
iammukeshm May 23, 2026
a47155f
refactor(identity,multitenancy): propagate CancellationToken through …
iammukeshm May 23, 2026
64370ca
docs(test): master test plan — risk-tiered coverage matrix + gap backlog
iammukeshm May 23, 2026
ae6ba21
test(architecture): add validators for the 3 handlers flagged by Hand…
iammukeshm May 23, 2026
c76b229
test(identity): forgot/reset + change-password integration tests
iammukeshm May 23, 2026
0d56d86
test(files): cross-tenant isolation + visibility/sharing
iammukeshm May 23, 2026
4d50af5
test(chat): cross-tenant channel/message isolation
iammukeshm May 23, 2026
58fea62
test(billing): cross-tenant fetch-by-id isolation
iammukeshm May 23, 2026
191ee04
test(chat): merge cross-tenant channel/message isolation tests
iammukeshm May 23, 2026
da1afce
test(files): merge cross-tenant isolation + visibility/sharing tests
iammukeshm May 23, 2026
da2c47a
test(billing): cross-tenant isolation tests + fix 2 P1 fetch-by-id leaks
iammukeshm May 23, 2026
b21cf8f
test(webhooks): HMAC signature correctness
iammukeshm May 23, 2026
2f5111f
test(catalog/tickets/groups): cross-tenant isolation
iammukeshm May 23, 2026
49187f2
test(multitenancy): provisioning failure path + activation block
iammukeshm May 23, 2026
16c7ef7
test(identity): permission-cache invalidation + role tenant isolation
iammukeshm May 23, 2026
3207ab9
test(identity): merge permission-cache invalidation + role isolation
iammukeshm May 23, 2026
7885e1b
test(webhooks): merge HMAC signature tests
iammukeshm May 23, 2026
9fa01b7
test(multitenancy): merge provisioning-failure tests
iammukeshm May 23, 2026
0182b95
test(catalog/tickets/groups): merge cross-tenant isolation tests
iammukeshm May 23, 2026
ea4d733
docs(test): mark wave 1+2 gap-fill complete; note Billing P1 leak fix
iammukeshm May 23, 2026
43971a2
test(webhooks): deliveries/dispatch/fanout/test-send coverage
iammukeshm May 23, 2026
9e7c2e2
test(multitenancy): theme/upgrade/retry/migrations coverage
iammukeshm May 23, 2026
ae8c9a0
test(auditing): by-id/correlation/trace/exception query coverage
iammukeshm May 23, 2026
c38579a
test(identity): sessions/users/forgot-password coverage
iammukeshm May 23, 2026
f8526f7
test(multitenancy): theme/upgrade/retry/migrations coverage + wire Ge…
iammukeshm May 23, 2026
fef20b0
test(identity): sessions/users/forgot-password coverage + fix profile…
iammukeshm May 23, 2026
eb2e734
test(auditing): by-id/correlation/trace/exception query coverage
iammukeshm May 23, 2026
debecbd
test(webhooks): deliveries/dispatch/fanout/test-send coverage
iammukeshm May 23, 2026
8e86c4b
fix(identity): close forgot-password user-enumeration leak
iammukeshm May 23, 2026
4424714
test(catalog): product-image remove/reorder + access-policy + update …
iammukeshm May 23, 2026
67a1765
test(tickets): comments + mappings + event-handler + search coverage
iammukeshm May 23, 2026
8cfe7e5
test(files): purge jobs + access-policy + storage flow coverage
iammukeshm May 23, 2026
ce616a2
fix(auditing): cast jsonb PayloadJson to text for ILIKE filters + tests
iammukeshm May 23, 2026
75904ea
test(billing): usage-snapshots query + monthly-invoice job + domain c…
iammukeshm May 23, 2026
53ce5a2
fix(auditing): cast jsonb PayloadJson to text for ILIKE filters + pro…
iammukeshm May 23, 2026
bbb897a
test(billing): usage-snapshots + monthly-invoice job + domain coverage
iammukeshm May 23, 2026
b0d7233
test(catalog): product-image remove/reorder + access-policy + update …
iammukeshm May 23, 2026
09c693f
test(files): purge jobs + access-policy + storage flow coverage
iammukeshm May 23, 2026
7a039cc
test(tickets): comments/search coverage + fix TicketComment.Id ValueG…
iammukeshm May 23, 2026
b5151e3
feat(dashboard,admin): dentalOS-style login + demo picker, FSH logo l…
iammukeshm May 23, 2026
4222323
test(e2e): Playwright suites for dashboard + admin (214 tests)
iammukeshm May 23, 2026
d428d69
test(auditing): un-skip the 6 jsonb-filter tests now that the cast fi…
iammukeshm May 23, 2026
3e310d0
test(catalog): unit tests for product/money/category domain
iammukeshm May 23, 2026
eaa88ee
test(billing): unit tests for domain transitions + services
iammukeshm May 23, 2026
20890a2
test(webhooks): unit tests for subscription matching + delivery + signer
iammukeshm May 23, 2026
d2bfeb2
test(billing): unit tests for domain transitions + services (80)
iammukeshm May 23, 2026
acc5335
test(catalog): unit tests for product/money/category domain (75)
iammukeshm May 23, 2026
578c689
test(webhooks): unit tests for subscription matching + delivery + sig…
iammukeshm May 23, 2026
8963e76
build(tests): wire Billing/Catalog/Webhooks unit-test projects into s…
iammukeshm May 23, 2026
d599b13
build(coverage): exclude *HostedService background loops from coverag…
iammukeshm May 23, 2026
bf572f2
test(identity): unit tests for device/current-user/password services
iammukeshm May 23, 2026
f815ccb
test(framework): unit tests for storage/core/web/persistence/eventing…
iammukeshm May 23, 2026
886fd55
test(framework): unit tests for storage/core/web/persistence/eventing…
iammukeshm May 23, 2026
29d172b
test(identity): unit tests for device/request-context/token services …
iammukeshm May 23, 2026
8312cf8
build(tests): wire Framework.Tests into solution
iammukeshm May 23, 2026
3c60640
ci: add Billing/Catalog/Chat/Files/Framework/Webhooks to test matrix …
iammukeshm May 23, 2026
679e6e3
ci: raise coverage floor to 80% (meaningful coverage now 83.3%)
iammukeshm May 23, 2026
d409883
test(middleware): real-wiring tests for global exception handler + ra…
iammukeshm May 23, 2026
3d9955a
test(middleware): real-wiring tests for global exception handler + ra…
iammukeshm May 23, 2026
4933583
Revert "test(middleware): real-wiring tests for global exception hand…
iammukeshm May 23, 2026
422a108
test(middleware): real-wiring tests in isolated assembly (global exce…
iammukeshm May 23, 2026
9718099
test(middleware): real-wiring tests in isolated assembly (Integration…
iammukeshm May 23, 2026
6644d2a
build(ci): wire Integration.Middleware.Tests into solution + integrat…
iammukeshm May 23, 2026
2af2f8c
chore: gitignore generated coverage-report/ and TestResults/ dirs
iammukeshm May 23, 2026
fca7c14
refactor: harden concurrency hotspots + cleanup from Roslyn-navigator…
iammukeshm May 23, 2026
96c264a
docs(agents): AGENTS.md guide + .agents/ rules, skills, and workflows…
iammukeshm May 23, 2026
f189474
chore: remove NSwag OpenAPI client-gen tooling + obsolete scripts/docs
iammukeshm May 23, 2026
ba1bcd3
docs(site): AI-Driven Development section, sponsorship surfaces, home…
iammukeshm May 23, 2026
a89da01
docs(home): testing section, View Transitions theme toggle, mobile po…
iammukeshm May 23, 2026
178f382
feat(infra): modernize Terraform — TF 1.15.4 / AWS 6.46, collapse roo…
iammukeshm May 23, 2026
107e0a7
chore: sweep in-flight distribution-template WIP + gitignore local .c…
iammukeshm May 23, 2026
db913c7
fix(docs): render section Overview first, dedup breadcrumbs, name ind…
iammukeshm May 23, 2026
fa89dc3
docs(deployment): add Aspire, DbMigrator, AWS Terraform, CI/CD guides…
iammukeshm May 23, 2026
4ef190a
docs: remove Blazor references (the kit no longer ships Blazor)
iammukeshm May 23, 2026
0d1c705
docs(architecture): expand tenant isolation deep-dive + fix cross-links
iammukeshm May 23, 2026
4b7a641
fix: http(s) scheme validation for webhook URLs + cross-platform Buil…
iammukeshm May 24, 2026
c120c23
docs: add Google Analytics (gtag.js) to BaseLayout
iammukeshm May 24, 2026
344cebb
@
iammukeshm May 24, 2026
999bfc4
@
iammukeshm May 24, 2026
69f3da0
feat(infra): switch Redis to Valkey + add RedisInsight cache browser
iammukeshm May 24, 2026
bbc6a7c
test(integration): run the HybridCache distributed-cache test against…
iammukeshm May 24, 2026
45bca06
docs(agents): docs repo + changelog must travel with each change
iammukeshm May 24, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
66 changes: 66 additions & 0 deletions .agents/rules/api-conventions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# API conventions

Read before adding endpoints, commands/queries, validators, or error handling.

## Endpoints

Static extension methods on `IEndpointRouteBuilder`, returning `RouteHandlerBuilder`. The handler delegates to Mediator. Gate with `.RequirePermission(...)`.

```csharp
public static class RegisterUserEndpoint
{
internal static RouteHandlerBuilder MapRegisterUserEndpoint(this IEndpointRouteBuilder endpoints) =>
endpoints.MapPost("/register", (RegisterUserCommand command,
IMediator mediator, CancellationToken cancellationToken) =>
mediator.Send(command, cancellationToken))
.WithName("RegisterUser")
.WithSummary("Register user")
.RequirePermission(IdentityPermissionConstants.Users.Create);
}
```

- **Always accept and forward `CancellationToken`** to `mediator.Send`. ASP.NET injects it.
- Wire each endpoint in the module's `MapEndpoints()`. Endpoints group under `api/v{version:apiVersion}/{module}`.
- Use `TypedResults` / `.Produces<T>(...)` for accurate OpenAPI. Add `.WithIdempotency()` on POSTs that must be replay-safe.

## CQRS

- **Commands/Queries** live in `Modules.{Name}.Contracts` — implement `ICommand<TResponse>` / `IQuery<TResponse>`. Records preferred.
- **Handlers** live in `Modules.{Name}/Features/` — `public sealed`, implement `ICommandHandler<T,TResponse>` / `IQueryHandler<T,TResponse>`, return `ValueTask<T>`, `.ConfigureAwait(false)` on awaits.
- Paginated queries implement `IPagedQuery` (`PageNumber`, `PageSize`, `Sort`) and return `PagedResponse<T>`.

## Validation

FluentValidation, auto-registered by `ModuleLoader`. Name `{Command}Validator`. Live in the same feature folder.

- **Every command handler needs a validator; every paginated query handler needs one too.** Enforced by `Architecture.Tests` (`HandlerValidatorPairingTests`). A handler legitimately without rules can be added to that test's known-missing allowlist, but prefer writing the validator.
- Validators run via the `ValidationBehavior<,>` Mediator pipeline before the handler.

## Exceptions → ProblemDetails

Throw framework exception types; the global handler converts to RFC 9457 `ProblemDetails`:

| Throw | HTTP |
|---|---|
| `NotFoundException` | 404 |
| `ForbiddenException` | 403 |
| `UnauthorizedException` | 401 |
| `CustomException(msg, errors?, HttpStatusCode)` | as specified (default 400) |

Don't catch broadly to swallow. Background loops may `catch (Exception)` to stay alive, but must **log with context** and exclude `OperationCanceledException` (filtered catch or a preceding `catch (OperationCanceledException)`).

## Permissions

Constants in `Shared/Identity/*Permissions.cs` (e.g. `IdentityPermissionConstants`). Apply with `.RequirePermission(...)` on the endpoint. `RequiredPermissionAttribute` implements `IRequiredPermissionMetadata` — never let a duplicate of that interface appear; it silently disables **all** `.RequirePermission()` gates.

## Specifications

Use `Specification<T>` (`src/BuildingBlocks/Persistence/Specifications/`) for query composition. Default `AsNoTracking = true` — see `database.md` for when tracking is required instead.

## Adding a feature (checklist)

1. Command/query + response in `Modules.{Name}.Contracts/v1/{Area}/{Feature}/`.
2. Handler in `Modules.{Name}/Features/v1/{Area}/{Feature}/`.
3. Validator in the same folder.
4. Endpoint in the same folder; wire in module `MapEndpoints()`.
5. Tests in `Tests/{Name}.Tests/` (+ integration test if it touches DB/IO).
99 changes: 99 additions & 0 deletions .agents/rules/architecture.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# Architecture rules

Modular Monolith + Vertical Slice Architecture (VSA). Read this before adding/moving modules or touching wiring.

## Layers & dependency direction

```
Host (composition root) → Modules.{Name} (runtime) → Modules.{Name}.Contracts (public API)
→ BuildingBlocks (shared framework)
```

- **BuildingBlocks** (`src/BuildingBlocks/`) — Core, Persistence, Web, Caching, Eventing, Storage, Quota, Jobs, Mailing, Shared. Consumed by all modules. **Do not modify without explicit approval.**
- **Modules** (`src/Modules/{Name}/`) — bounded contexts. Each = a runtime project (internal) + a `.Contracts` project (public API: commands, queries, events, DTOs, service interfaces).
- A module **MUST NOT** reference another module's runtime project — only its `.Contracts`. Enforced by `Architecture.Tests` (NetArchTest).

## Module = runtime + Contracts

```
Modules.Identity/ ← runtime (internal): handlers, services, domain, data
Modules.Identity.Contracts/ ← public: ICommand/IQuery types, DTOs, events, service interfaces
```

Cross-module communication: through Contracts service interfaces or integration events only.

## Feature folder layout (VSA)

Each feature is a vertical slice in `Features/v{version}/{Area}/{Feature}/`:

```
Features/v1/Users/RegisterUser/
├── RegisterUserEndpoint.cs # minimal API endpoint
├── RegisterUserCommandHandler.cs # CQRS handler (public sealed)
└── RegisterUserCommandValidator.cs # FluentValidation
```

Module support folders: `Domain/`, `Data/`, `Services/`, `Events/`, `Authorization/`.

## IModule registration

Each module implements `IModule`, declared via an **assembly-level** `[FshModule]` attribute (positional `(Type moduleType, int order = 0)`) — **not** a class-level `[FshModule(Order = n)]`:

```csharp
[assembly: FshModule(typeof(FSH.Modules.Identity.IdentityModule), 1)] // above the namespace

namespace FSH.Modules.Identity;

public sealed class IdentityModule : IModule
{
public void ConfigureServices(IHostApplicationBuilder builder) { ... }
public void ConfigureMiddleware(IApplicationBuilder app) { ... } // optional, runs AFTER UseAuthentication
public void MapEndpoints(IEndpointRouteBuilder endpoints) { ... }
}
```

`ModuleLoader.AddModules` (`src/BuildingBlocks/Web/Modules/ModuleLoader.cs`) discovers `[FshModule]` attributes, orders by `Order` then name, instantiates each, and calls `ConfigureServices`. Endpoints map under `api/v{version:apiVersion}/{module}`.

## ⚠️ The four-place registration footgun

Adding a module requires editing **four** lists. Miss one and it fails *silently*:

| Place | File | Symptom if missed |
|---|---|---|
| Mediator `o.Assemblies` (two markers: Contracts type **and** module type) | `src/Host/FSH.Starter.Api/Program.cs` | Handlers silently undiscovered |
| `moduleAssemblies` array | `src/Host/FSH.Starter.Api/Program.cs` | Module never loaded |
| Mediator assemblies (same pair) | `src/Host/FSH.Starter.DbMigrator/Program.cs` | Migrate/seed misses the module |
| module assemblies array | `src/Host/FSH.Starter.DbMigrator/Program.cs` | Migrate/seed misses the module |

After wiring, the fastest sanity check is: build, hit the endpoint, confirm the handler runs.

## DI & handler conventions

- Mediator handlers: `public sealed`, implement `ICommandHandler<T,TResponse>` / `IQueryHandler<T,TResponse>`, return `ValueTask<T>`, `.ConfigureAwait(false)` on every await. `ServiceLifetime.Scoped`.
- Validators auto-register via `ModuleLoader` (`AddValidatorsFromAssemblies`). Name them `{Command}Validator`.
- Prefer constructor injection / primary constructors. Watch DI lifetimes: stateful singletons must be thread-safe (use `ConcurrentDictionary` / immutable snapshots).

## Middleware ordering (critical)

In `src/BuildingBlocks/Web/Extensions.cs` (`UseHeroPlatform`):

1. ExceptionHandler → ResponseCompression
2. **CORS before HTTPS redirect** (so OPTIONS preflight isn't 307-redirected)
3. HttpsRedirection → SecurityHeaders → static files → Routing
4. **`UseAuthentication`**
5. **`UseModuleMiddlewares`** — each module's `ConfigureMiddleware`, runs **after** auth
6. RateLimiting → Quotas → `UseAuthorization` → `MapModules`

`app.UseHeroMultiTenantDatabases()` (Finbuckle `UseMultiTenant()`) runs in `Program.cs` **before** `UseHeroPlatform`, i.e. **before `UseAuthentication`** — so tenant resolution is header-driven, not claim-driven. See `modules/multitenancy.md`.

## Static/global state

No global mutable static collections enumerated under concurrency. `Audit` (Auditing module) swaps an immutable `IAuditEnricher[]` atomically; `ModuleLoader` guards with a lock. Follow that pattern if you must hold process-global state.

## Configuration & options

- `appsettings.json` (+ `.Development`/`.Production`) live in `src/Host/FSH.Starter.Api/`. DbMigrator links the same files.
- Bind config with the Options pattern: `AddOptions<T>().BindConfiguration(nameof(T))`, section name == type name (e.g. `JwtOptions`, `DatabaseOptions`, `CachingOptions`, `CorsOptions`, `QuotaOptions`, `RateLimitingOptions`; **storage section is `Storage`**, not `StorageOptions`). Add `.ValidateDataAnnotations().ValidateOnStart()` for fail-fast.
- Validate critical options via `IValidatableObject` — `JwtOptions` requires `SigningKey` ≥32 chars and **rejects placeholder strings containing `"replace-with"`**; `DatabaseOptions` rejects empty connection strings.
- **Production fail-fast** (`Program.cs`, before service registration): missing `DatabaseOptions:ConnectionString`, `CachingOptions:Redis`, or `JwtOptions:SigningKey` throws. Dev secrets via `dotnet user-secrets` (AppHost has a `UserSecretsId`); MinIO creds are Aspire secret parameters.
- Platform composition is one call each: `builder.AddHeroPlatform(o => { o.Enable... })` (DI) and `app.UseHeroPlatform(...)` (middleware). Feature flags toggle Caching/Jobs/Mailing/Quotas/Sse/Realtime/OpenTelemetry/CORS/Idempotency.
36 changes: 36 additions & 0 deletions .agents/rules/buildingblocks-protection.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
---
paths:
- "src/BuildingBlocks/**/*"
---

# ⚠️ BuildingBlocks Protection

**STOP. You are modifying BuildingBlocks.**

Changes to BuildingBlocks affect ALL modules across the entire framework. These are core abstractions that many projects depend on.

## Before Proceeding

1. **Confirm explicit approval** - Has the user specifically approved this change?
2. **Consider alternatives** - Can this be done in the module instead?
3. **Assess impact** - What modules will this affect?

## If Approved

- Make minimal, focused changes
- Ensure backward compatibility
- Update all affected modules
- Run full test suite: `dotnet test src/FSH.Starter.slnx`
- Document the change

## Alternatives to Consider

| Instead of... | Consider... |
|---------------|-------------|
| Modifying Core | Extension method in module |
| Changing Persistence | Custom repository in module |
| Updating Web | Module-specific middleware |

## If Not Approved

Do not proceed. Suggest alternatives that don't require BuildingBlocks modifications.
30 changes: 30 additions & 0 deletions .agents/rules/caching.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Caching

`src/BuildingBlocks/Caching/`. Read before adding cached reads or invalidation.

## What's registered

`AddHeroCaching(config)` always registers **`HybridCache`** (L1 in-memory + optional L2 Redis). Inject `HybridCache`, not `IDistributedCache`.

- `CachingOptions.Redis` empty → in-memory only (dev fallback). Set → a **single shared `ConnectionMultiplexer`** (singleton `IConnectionMultiplexer`) backs both the L2 cache and the DataProtection key ring.
- Defaults: total expiration 1h, L1 (local) expiration 2min (`CachingOptions`).
- `ObservableHybridCache` transparently decorates `HybridCache` to emit OpenTelemetry (hits/misses/factory-duration/invalidations). You don't reference it — just inject `HybridCache`.

## Pattern

```csharp
var perms = await cache.GetOrCreateAsync(
CacheKeys.UserPermissions(userId),
async ct => await LoadPermissionsAsync(userId, ct),
tags: [CacheKeys.Tags.Permissions, CacheKeys.Tags.User(userId)],
cancellationToken: ct);
```

- **Keys & tags live in `CacheKeys.cs`** — add new keys/tags there, don't inline strings. Existing: `UserPermissions(userId)`, `TenantTheme(tenantId)`, `IdempotencyEntry(tenantId,key)`, `ImpersonationGrantStatus(jti)`; tags `Permissions`, `Themes`, `Idempotency`, `Tenant(id)`, `User(id)`.
- Invalidate with `RemoveAsync(key)` or `RemoveByTagAsync(tag)` in the relevant mutation handler.
- `GetOrCreateAsync` gives **stampede protection** for free (factory runs once per key).

## Gotchas

- **No L1 backplane.** `RemoveByTagAsync` on one node does **not** evict L1 on peer nodes — cross-node staleness is bounded only by the 2-min local expiration. Don't rely on instant cross-node invalidation; keep local expiration short for hot, mutable data.
- Don't reach for `IDistributedCache` directly except where the framework already does so deliberately (idempotency probe-read) — prefer `HybridCache`.
48 changes: 48 additions & 0 deletions .agents/rules/database.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Database & EF Core conventions

Read before touching entities, DbContexts, migrations, or query filters.

## Entities

- `BaseEntity` — `Id`, `CreatedAt`, `UpdatedAt`, `TenantId`.
- `AggregateRoot` — `BaseEntity` + domain events (`IHasDomainEvents`, `_domainEvents` list).
- Marker interfaces: `IHasTenant`, `IAuditableEntity`, `ISoftDeletable`, `IGlobalEntity`.
- Domain events inherit `DomainEvent` (record: `EventId`, `OccurredOnUtc`, `CorrelationId`, `TenantId`). Integration events implement `IIntegrationEvent`; handlers `IIntegrationEventHandler<T>`.

## Tenant isolation (default-ON)

- `BaseDbContext` auto-applies a tenant query filter to every entity. **Isolation is on by default.**
- Opt out **only** via `IGlobalEntity` (e.g. `BillingPlan`, `ImpersonationGrant`, `Outbox`/`InboxMessage`).
- A subclass DbContext that overrides `OnModelCreating` **must call `base.OnModelCreating(modelBuilder)` LAST**, or the auto-applied filters are lost.
- Cross-tenant reads use `IgnoreQueryFilters()` **plus an explicit re-filter** — never rely on the absence of the filter.
- **Query-filter naming:** SoftDelete filter is *named*; the tenant filter stays *anonymous* (Finbuckle owns it). Don't rename the tenant filter.

## AsNoTracking — and when NOT to

- Read-only queries: add `.AsNoTracking()` (Specifications default to it).
- **Do NOT add `AsNoTracking()` to a read-then-mutate-then-`SaveChanges` query** — the entity must stay tracked or your changes won't persist. The analyzer (AP010) flags these as a smell, but for mutate-and-save flows it is a false positive — leave them tracked.
- `AnyAsync(...)` materializes no entity, so `AsNoTracking()` there is a no-op — skip it.

## Value generation for nav-collection children

A child entity reached **only** through a parent's navigation collection needs `Property(x => x.Id).ValueGeneratedNever()` in its EF config — otherwise EF treats it as `Modified` instead of `Added` and the insert silently misbehaves.

## Migrations

All migrations live in **one** project, `src/Host/FSH.Starter.Migrations.PostgreSQL`, organized **per-module by folder** (`Identity/`, `Catalog/`, `Chat/`, …), each with its own `{Module}DbContextModelSnapshot`.

```bash
dotnet ef migrations add {Name} \
--project src/Host/FSH.Starter.Migrations.PostgreSQL \
--startup-project src/Host/FSH.Starter.Api \
--context {Module}DbContext
```

- **`migrations remove` operates on the snapshot** — run a full build *before* `migrations add` so the snapshot is current, or you can lose the previous migration.
- The DB is **not** migrated at API startup. The `DbMigrator` host is a separate step: `apply` (default), `seed`, `seed-demo` (dev only), `list-pending`; flags `--tenant <id>`, `--catalog-only`, `--seed`. It migrates the tenant catalog first, then each tenant's per-module schema, serialized by a Postgres advisory lock.
- `dotnet-ef` is pinned in `.config/dotnet-tools.json` — run `dotnet tool restore` first.

## Tests + EF

- Integration tests use Testcontainers (real PostgreSQL) — **Docker must be running**.
- In integration tests, set the Finbuckle tenant context **inline in the same method** as the `UserManager`/`DbContext` call; an awaited-helper set is lost (AsyncLocal) and the tenant query filter NREs.
39 changes: 39 additions & 0 deletions .agents/rules/eventing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Eventing — domain events, integration events, Outbox/Inbox

Read before publishing/handling cross-module events. `src/BuildingBlocks/Eventing/`.

## Two tiers

- **Domain events** (in-process, pre-commit) — inherit `DomainEvent` (record: `EventId`, `OccurredOnUtc`, `CorrelationId`, `TenantId`). Raised on aggregates (`IHasDomainEvents`).
- **Integration events** (cross-module, async) — implement `IIntegrationEvent` (`Id`, `OccurredOnUtc`, `TenantId`, `CorrelationId`, `Source`). Handlers implement `IIntegrationEventHandler<T>` (single `HandleAsync(T, ct)`), are `sealed`, live in `Events/` or `IntegrationEventHandlers/`.

## The Outbox is the only way to publish

**Do not call `IEventBus` directly from a handler.** Publish via the outbox so it commits in the same transaction and survives crashes:

```csharp
await _outboxStore.AddAsync(integrationEvent, ct).ConfigureAwait(false);
```

`EfCoreOutboxStore.AddAsync` serializes + `SaveChanges` immediately. `OutboxDispatcherHostedService` polls every `OutboxDispatchIntervalSeconds` (default 10), `OutboxDispatcher` pulls a batch (`OutboxBatchSize`, default 100), publishes via `IEventBus`, and dead-letters after `OutboxMaxRetries` (default 5) → `IsDead`. `OutboxMessage`/`InboxMessage` are `IGlobalEntity` (no tenant filter — the dispatcher has no tenant context; `TenantId` is an explicit column).

## Idempotency is free (in-memory bus)

`InMemoryEventBus` resolves handlers in a fresh DI scope and applies the **Inbox**: skips if `IInboxStore.HasProcessedAsync(eventId, handlerName)`, marks processed after success. Composite key `{Id, HandlerName}`; concurrent-insert race is swallowed. Don't hand-roll dedup.

## Wiring (3 calls in the module's `ConfigureServices`)

```csharp
services.AddEventingCore(builder.Configuration); // serializer + bus + hosted dispatcher
services.AddEventingForDbContext<MyDbContext>(); // outbox/inbox stores (scoped)
services.AddIntegrationEventHandlers(typeof(MyModule).Assembly); // scans IIntegrationEventHandler<>
```

Bus = `EventingOptions.Provider`: `"RabbitMQ"` → `RabbitMqEventBus` (durable topic exchange); else `InMemoryEventBus` (default).

## Gotchas

- **Renaming/moving an integration event type breaks deserialization** — the outbox stores the assembly-qualified type name; `Type.GetType()` returns null → the message dead-letters. Keep event type names/namespaces stable, or migrate dead rows.
- **Background handlers carry no HTTP/tenant context.** An open-generic or background handler that reads a tenant-filtered DbContext must restore Finbuckle context first via `IMultiTenantContextSetter` (see `WebhookFanoutHandler`, `modules/webhooks.md`).
- In-memory bus runs handlers **synchronously in the publisher's scope** — keep handler work minimal; exceptions surface to the originating request (relevant for Notifications consuming Chat events).
- Set `UseHostedServiceDispatcher=false` to drive the outbox via Hangfire instead of the hosted service.
Loading
Loading