Skip to main content

Change Log: SaaS Architecture Policy & Annex A

Tracks what changed as a direct result of each review, and who to credit for it. Only useful, adopted changes are listed — points raised and not adopted are not tracked here. Full technical detail lives in the policy and Annex A themselves; this page is the condensed index.

:::tip Adding a new entry Add a new version block at the top (newest first) with the reviewer's name filled in. If a change came from a document/tool review rather than a named person, credit it as "Source:" instead of "Reviewer:". :::

v1.11 — 2026-10-17​

Source: Direct request — correct errors in the Action Items file

#ChangeWhere
1Corrected: ACT-001 referred to withTenantScope(), renamed to withTenant() to match the shipped API and Annex A.10actions.md (ACT-001)
2Corrected: ACT-004 status "Decided — mirrored" is not a defined status; now Closedactions.md (ACT-004, tracking table)
3Corrected: ACT-004 link to Annex A.10 used the pre-rename heading anchor; updated to the current oneactions.md (ACT-004)

v1.10 — 2026-10-17​

Source: Direct request — record the Search Engine integration decisions (PRs #375 and #376 of rarit-saas-kernel)

#ChangeWhere
1Added: the Search Engine package (@raritllc/search), merged to devrarit-saas-kernel, packages/search
2Decided: platform tenant discovery through SECURITY DEFINER reader functions, one reader role per table (A)ADR platform-enumeration-under-rls
3Decided: the resolver hook supplies row-scope values only; permissions are asked of IAM once per key per request and never cached (B)packages/search/src/v1/access.ts
4Decided: manifest rowScope accepts the string and the object form (C)packages/search/src/core/manifest/schema.ts
5Decided: SearchError extends AppError (D); catalogs in locales/ (E)packages/search
6Decided: search in npm-packages.mjs (F); no reason or sensitive actions for search.* (G)scripts/npm-packages.mjs
7Decided: boolean entitlement search.enabled, never metered; query audit without query textpackages/search
8Added: ACT-012 to ACT-016 (defects found in IAM and platform-core) and ACT-017 (decision record)actions.md (new)

v1.9 — 2026-08-28​

Source: Direct request — formalize code review process as its own policy, separate from the SaaS Architecture Policy (broader scope: covers independent mobile apps too, not just kernel + verticals)

#ChangeWhere
1Added: new sibling policy, "RaR-IT Code Review & Pull Request Policy" v1.0 — PR requirements, Claude Code Review configuration, REVIEW.md governance, merge-enforcement limits under GitHub Freecode-review-policy.md (new)
2Closed: ACT-005, outcome is the new policyactions.md
3Added: ACT-006 — REVIEW.md drift-prevention across repos, no design chosen yetactions.md (new)
4Added: ACT-007 — roll out PR template + branch-naming convention platform-wide, including mobile apps with no prior rollout planactions.md (new)

v1.8 — 2026-08-28​

Source: Direct request — configure Claude Code Review now that a Team plan is active

#ChangeWhere
1Added: ACT-005 — configure Claude Code Review on rarit-saas-kernel and all five vertical repos; trigger set to "Once after PR creation"actions.md (new entry)
2Added: REVIEW.md — repo-facing file translating Policy §4, §5/A.10, A.7, and A.11.3 into explicit Code Review rules, distinct from CLAUDE.md's general project contextREVIEW.md (new, per repo)

v1.7 — 2026-08-27​

Source: Direct request — reproduce and mirror two older ADRs referenced but never previously published in this collection

#ChangeWhere
1Added: ADR "Fastify for Shared Engines (Not NestJS)" reconstructed in full (9 sections, 12-criterion scoring matrix) from the original conversation's actual content and mirrored as a versioned pageadr/fastify-vs-nestjs-decision.md (new, EN + AR)
2Added: "Node.js vs. PHP Laravel" weighted comparison study reconstructed (7 of 9 criteria fully recovered; 2 minor criteria, 10% combined weight, honestly flagged as not recovered rather than guessed)adr/nodejs-vs-laravel-study.md (new, EN only — AR pending)
3Added: versioned PDFs for both, placed in signed-records/adr/ alongside the existing DB-access-pattern ADRsigned-records/adr/
4Restructured: the three ADRs now sit under a nested "Architecture Decision Records" sidebar category, ordered chronologically (Fastify-vs-NestJS → DB access pattern → Node-vs-Laravel)sidebars.ts

v1.6 — 2026-08-27​

Source: Direct request — the external ADR source became unavailable; mirror it locally instead

#ChangeWhere
1Added: db-access-pattern-decision.md reproduced verbatim as a versioned page (v1.0)adr/db-access-pattern-decision.md (new)
2Decided: ACT-004 resolved — mirror, not pin-and-link, given the source became unavailableactions.md
3Fixed: all four references to the external github.com/rarit/rarit-saas-kernel URL replaced with the local mirror linkPolicy §5, §6; Annex A.1, A.10

v1.5 — 2026-08-27​

Source: Direct request — centralize open items into a dedicated tracker

#ChangeWhere
1Added: new Action Items tracker, four open items (ACT-001–ACT-004) with type, status, owner placeholderactions.md (new)
2Added: full withPlatformAdminScope() proposal — separate function, read-only default, per-table opt-in, mandatory audit reason — with the concrete TEN-DASH-01 blocker it resolvesA.11.2 (expanded from open question to proposed design)
3Added: pointer to ACT-003 (event bus durability) — content unchanged, now tracked centrallyA.7
4Added: pointer to ACT-002 (Data Ownership Matrix) — content unchanged, now tracked centrallyPolicy §9
5Added: ACT-004 — recommendation against mirroring rarit-saas-kernel's ADRs into this repo; pin GitHub links to a commit insteadactions.md (new)

v1.4 — 2026-08-27​

Reviewer: _____________ Type: Formal written review (13 findings, High/Medium/Low priority)

#ChangeWhere
1Fixed: "Clinical Templates Engine" → "Clinical Templates Module" (contradicted the five-engine limit in Section 4)A.4, summary table
2Fixed: broken lint:cycles script — legacy ESLint flags incompatible with the flat config specified one step earlierA.9 Step 3
3Added: eslint-plugin-boundaries rule for one-way vertical-to-vertical imports — a real gap no-cycle cannot coverA.9 Step 9 (new)
4Added: RLS stated as the primary, non-bypassable boundary; app-layer filter as secondary, not equivalentA.10
5Added: set_config(..., true) = SET LOCAL, and why that's what makes the pattern connection-pool-safeA.10
6Added: audit rows are stored per-vertical, not centrally — a direct consequence of Section 3.2, now stated explicitlyA.10
7Added: event-bus delivery guarantee flagged as unresolved; transactional-outbox recommended if in-process onlyA.7
8Added: concrete criteria for when a vertical may adopt NestJS, anchored to real examplesPolicy §6.1
9Added: full request lifecycle — Auth → Tenant Resolution → RBAC → Entitlement → RLS → Business logicPolicy §4
10Added: Data Ownership Matrix logged as a standing Architecture Review Board action itemPolicy §9
11Added: proxy-hop specified as the standard remediation over a duplicated guard, if this pattern is ever approvedA.6
12Added: commission-rate configuration ownership clarified (lives in Payment/Entitlements, not Transport)A.2
—Removed: A.11.5 (PostgREST-role note) — judged low value, not a real risk any RaR-IT code was at risk ofRequested directly, not part of this review

v1.3 — 2026-08-27​

Source: Prior-art evaluation — sascdb.sql (an old, unrelated PostgreSQL system's RLS setup, submitted for review)

#ChangeWhere
1Added: RLS pattern for a child table with no direct tenant_id column (subquery-scoped policy)A.11.1 (new)
2Added: cross-tenant admin-read shape flagged as an open proposal for platform-core — not yet an accepted patternA.11.2 (new)
3Added: real anti-example — ENABLE ROW LEVEL SECURITY without FORCE, found in the old system as-isA.11.3 (new)
4Added: real anti-example — notification/audit logic embedded in a DB trigger instead of the service layerA.11.4 (new)

v1.2 — 2026-08-27​

Reviewer: _____________ Type: Informal review comment, pre-sign-off

#ChangeWhere
1Fixed: A.1, A.3, A.4, A.5 were calling fastify.db directly inside route handlers, contradicting db-access-pattern-decision.mdA.1, A.3, A.4, A.5
2Added: repository → service → route three-layer split adopted as the reference shape in every exampleA.1, A.3, A.4, A.5
3Added: getDb() / withTenantScope() documented as the mandatory tenant-scoped access patternA.10 (new), Policy §5
4Added: fastify.decorate('db', ...) and an explicit transaction parameter formally rejected, with reasonsPolicy §6
5Added: audit write shown explicitly joining the same transaction as its triggering mutationA.1, A.10

v1.1 — 2026-08-27​

Source: Circular_Dependency_Problems.pdf (npm/Node.js circular-dependency reference, submitted for evaluation)

#ChangeWhere
1Added: engine-to-engine dependencies must be acyclic — real IAM↔Notifications and Entitlements↔Notifications risk identifiedPolicy §4
2Added: the same acyclic rule applied explicitly to vertical-to-vertical importsPolicy §4
3Added: eslint-plugin-import's no-cycle rule mandated in CI across the whole workspacePolicy §5
4Added: worked example — IAM↔Notifications cycle fixed via published events instead of a reverse importA.7 (new)
5Added: worked example — AMAAR↔Tourism vertical cycle fixed by promoting shared logic to a kernel packageA.8 (new)
6Added: full 8-step CI implementation walkthrough (install → configure → CI job → branch protection → pre-commit → safe rollout → verify)A.9 (new)

v1.0 — 2026-08-25​

Initial policy and Annex A. Status: Proposed, pending Architecture Review Board sign-off.