Architecture Guidance Review — September 2026¶
Status: guidance ratified — sections 3–4 still open for comment
A read-only audit, following the pattern of
docs-gap-audit.md and the
UX / security assessments:
audit first, remediation second. The guidance it produced,
decision-principles.md, was ratified through PR #1146 (September
2026) and is kept separate so it can be read on its own. The impact estimate (§3) and the backlog
restructuring proposals (§4) are still proposals — @Wim / @Taeke please comment there.
Method. Read every file under docs/architecture/, the subdirectory CLAUDE.md files, the
docs/process/ DoR pipeline docs, and docs/risk-scoring/. Read the full body + comment trail of
all 22 open state:awaiting-design issues and all 14 open state:decompose issues. Searched closed
issues/PRs for precedent on five specific leads (business-role-as-matrix-row, the context plugin
catalogue, the risk-scoring plugin tier, decompose-threshold precedent, and buried architectural
rulings in comment threads). No files were changed as part of the audit itself.
Headline finding, stated up front because it reframes everything below: most of the
awaiting-design backlog is not stuck on missing guidance. 17 of the 22 awaiting-design
issues (#749, #750, #752, #757, #762, #764, #768, #769, #772, #776, #779, #783, #784, #788, #789,
790, #796) were auto-routed there by the DoR spec-agent on 2026-07-28, 12:44–12:50 UTC, each¶
with a templated comment that already lists 2–3 concrete options. None have a follow-up reply of any kind — not even a rejection. This is an unanswered ratification queue, not an open architecture question. The single highest-leverage fix available is a process one — a batch "design triage" pass, or a "ratify by silence after N days unless objected" rule — and it is not something a sharper architecture document can fix on its own. See Impact estimate for how this changes the numbers.
That said, the review also found genuine content gaps, one real doc-vs-doc contradiction, and one doc-vs-precedent recurrence worth fixing regardless of the process question. Those are below.
1. Contradictions found¶
1.1 Doc-vs-doc: two incompatible risk-scoring plugin architectures for the same slot¶
docs/architecture/risk-scoring-plugins.md proposes an in-tree Node.js plugin tier (JS modules
registered like the context-plugin registry, reusing its reconcile()), explicitly unbuilt — issue
672 is its tracking issue, and its own status header says "Proposal for review — not yet¶
implemented." docs/risk-scoring/plugin-architecture.md describes a different, incompatible
architecture for what reads as the same conceptual slot — Python ScoringPlugin/DiscoveryPlugin
ABCs, YAML "classifier packs," a plugins/community/ filesystem layout, an idrisk CLI — aimed at a
third-party/open-source contributor ecosystem. It carries no status header distinguishing it as
speculative, unlike every other design doc in the repo (context-redesign.md,
assignment-model-redesign.md, effective-access-engine.md, and risk-scoring-plugins.md itself
all have one). It also directly contradicts context-redesign.md §9's stated non-goal — "A
user-facing plugin SDK — plugins remain in-tree code modules until there's a clear case for
third-party plugins" — since its entire premise is third-party plugins.
Investigation confirmed neither document is shipped, and #672's comment trail never references
plugin-architecture.md at all — the two were developed independently and never reconciled. This
is a live hazard for the A5 "architecture fit" gate: an AI probe asking "does a new risk-scoring
capability fit the existing architecture?" would get two contradictory answers depending which doc it
happened to read. Recommend: add a status header to plugin-architecture.md disambiguating it as a
long-horizon product-vision doc, not a near-term implementation target, and cross-link both documents
to each other so the conflict is visible rather than latent. (Done as part of this review — see
§2.5 below.)
1.2 Doc-vs-precedent: "fix at the source" is written down but has no structural check¶
CLAUDE.md's own cautionary example for "fix at the source, not the surface" is the matrix's old
client-side "owner as its own row" simulation, fixed in PR #446 by giving ownership a real
GroupOwnership resource. That fix is exactly six weeks old (by commit history) when the same
defect shape recurred: issue #937 shows BusinessRole rows leaking into the matrix's resource axis
for the identical reason — a resource-listing query with no resourceType exclusion. The DoR bot's
own certification comment on #937 draws the parallel explicitly, citing resources.js:82-88's
existing BusinessRole exclusion as "an exact precedent for the fix." (That code has since moved
to app/api/src/routes/resources/list.js in #1042; the citation was correct on the day.)
The principle exists in prose (CLAUDE.md) but nothing turns it into a repeatable check — there's no
"every new resource-axis query must state which resourceTypes it excludes" item on any checklist.
Recommend promoting it to a named checklist item (decision-principles.md §A1 does this) rather than
leaving it as a narrative example a future author has to remember to apply.
1.3 Doc-vs-precedent, but resolved on inspection: #749 does not actually contradict the Style Guide¶
The security/maintenance audit's own H1 finding describes #749 as fixing UI that contradicts "the
documented 'blue is the single interactive accent' rule." On inspection, docs/contributing/
style-guide.md §1 does not say that — it defines two intentional accent roles (green = brand,
blue = interactive) and forbids only introducing a third (no indigo/emerald/teal). TaekeK's
comment on #749 ("the Dashboard's lime accent is intentional brand ... isn't the right direction
without a product/brand decision") is the Style Guide reasserting itself correctly, not a new
architectural ruling that contradicts it. The audit finding's paraphrase drifted from what the
Style Guide actually says, and issue #749 inherited the drift — it's currently scoped as "demote
lime/indigo," when the buildable slice (per the Style Guide as written) is "demote indigo only."
Recommend re-scoping #749's title/body before it's picked up again; no doc change needed, the Style
Guide is already correct.
1.4 Doc-vs-doc drift, already caught and fixed — cited for completeness¶
context-redesign.md §4.2's plugin catalogue undercounted (documented 5, two of which never
shipped, one misnamed) against the actual 10 registered plugins. This was flagged by
docs/docs-gap-audit.md (A4) and fixed by PR #805. No outstanding action — included here because it
is the clearest evidence in this repo that plugin catalogues drift the moment a new plugin ships
without a doc-update habit, which is relevant to how much weight to put on any catalogue-style doc
going forward (treat counts in context-redesign.md as illustrative, not authoritative — the source
of truth is app/api/src/contexts/plugins/registry.js).
1.5 Cross-doc pattern fragmentation (not a contradiction, but adjacent)¶
Three different design docs each solve "which rows came from an automated process vs. a human, and
does a re-run clobber the human's edits" with three different, non-interoperating vocabularies:
Contexts' synced/generated/manual variant axis (shipped), risk-scoring-plugins.md's
finding/override model (proposed), and rule-mining-discussion.md's
proposed/approved/implemented lifecycle (exploratory). None reference each other. This isn't wrong in
any one document, but it means the next author solving this exact problem (candidates: #672, #676,
and a hypothetical future BloodHound-derived-finding feature) has no single pattern to reach for. See
decision-principles.md §A4.
2. Proposed architecture document diff¶
There is no single canonical "architecture principles" document today — that absence is itself a
finding. The only place architecture-fit criteria are written down at all is one table cell in
docs/process/definition-of-ready.md (four words: "registry vs engine, additive vs
mutate-shared-contract, matview vs query-time, migrations"), plus CLAUDE.md's four process-level
"Coding Principles" (reuse-before-creating, fix-at-source, coverage-never-down, keep-files-small),
which govern how to build, not what shape to build. The 30+ files under docs/architecture/ are
each a single-feature design spec — rich in reasoning, but none of them is a cross-cutting reference,
and nothing points from the DoR gate to any of them.
The diff below is against the two places that currently function as "the architecture document," plus one new file that becomes the real one.
2.1 New file: docs/architecture/decision-principles.md¶
The sharpened guidance itself — 13 principles, each phrased as a testable question with a 🟢 high-confidence / 🟡 needs-human-review label and 1–2 cited example issues. See the file directly; summarized by section:
- A. Data model & type shape — governance-row exclusion (A1, 🟢), extendedAttributes vs. column promotion (A2, 🟢), closed enum vs. free-form type (A3, 🟢), reuse the variant+reconciliation pattern (A4, 🟡).
- B. Compute strategy — materialize vs. compute-on-demand (B1, 🟡), never silently truncate (B2, 🟢), deterministic vs. LLM for anything persisted (B3, 🟢).
- C. Extensibility — default to the registry/plugin pattern (C1, 🟢), with the risk-scoring contradiction flagged inline.
- D. UI/design-system boundary — this is a routing correction, not new architecture content: it says which of these questions belong to the Designer/Style-Guide track instead of the Architect track (D1–D3, mixed 🟢/🟡).
- E. Sizing & decomposition — a first-draft rubric for the single largest gap found (all 🟡, expect revision after a few uses).
- F. Explicitly out of scope — names the security/hardening cluster and says why it doesn't belong under "architecture fit" at all.
2.2 docs/process/definition-of-ready.md — sharpen the A5 gate criteria¶
- | **Architect / tech lead** | Technical approach; coherence of the codebase | registry vs engine, additive vs mutate-shared-contract, matview vs query-time, migrations |
+ | **Architect / tech lead** | Technical approach; coherence of the codebase | registry vs engine, additive vs mutate-shared-contract, matview vs query-time, migrations, closed-enum vs free-form type axis, deterministic vs LLM for persisted output — see [`decision-principles.md`](../architecture/decision-principles.md) for the full testable checklist and its confidence ratings |
2.3 CLAUDE.md — one pointer, not new prose¶
Added under "Coding Principles," matching the existing bold-lead-in style of the other four principles, kept to a few lines since this file loads into every session:
+ > **Architecture fit is a checklist, not a vibe.** When a feature request raises "does this fit
+ > the existing architecture" — a new resourceType, a new plugin, a materialize-vs-query-time
+ > choice — check it against [`docs/architecture/decision-principles.md`](docs/architecture/decision-principles.md)
+ > before treating it as an open question for a human. Each entry is a testable question with a
+ > confidence rating and cited examples; 🟢 entries are safe to apply directly, 🟡 entries should
+ > still go to the architect the first few times.
2.4 Enforcement table — new row¶
The existing CLAUDE.md table distinguishes hard CI gates from reviewer judgement. Architecture fit is squarely reviewer judgement — no gate should be invented for it — but it deserves a row so that's explicit rather than implied by omission:
+ | **Architecture fit** (registry vs special-case, closed vs free-form types, matview vs query-time, sizing/decomposition) | Reviewer/architect judgement per `docs/architecture/decision-principles.md` — no automated gate. The 🟢/🟡 confidence labels are the only signal of how settled an answer is. |
2.5 docs/risk-scoring/plugin-architecture.md — status header (applied)¶
Added the missing status header every other design doc in the repo carries, and a cross-link resolving §1.1 above:
+ !!! danger "Status: speculative long-horizon vision — not the risk-scoring plugin tier being built"
+ This describes a hypothetical **third-party/open-source** contributor ecosystem (Python ABCs,
+ YAML classifier packs, a CLI). It is unrelated to and inconsistent with
+ [`docs/architecture/risk-scoring-plugins.md`](../architecture/risk-scoring-plugins.md) — the
+ actual (also unbuilt) in-tree Node.js plugin-tier proposal tracked by issue #672 — and
+ conflicts with `context-redesign.md`'s stated non-goal of a third-party plugin SDK. Do not
+ treat this page as architecture guidance until an architect reconciles the two.
2.6 mkdocs.yml — nav entries¶
Both new pages registered under the existing "Quality & audits" and "Design archive" sections,
matching how docs-gap-audit.md and risk-scoring-plugins.md are already listed.
3. Impact estimate¶
Of the 36 open awaiting-design + decompose issues surveyed:
17 are blocked on process, not content (#749, #750, #752, #757, #762, #764, #768, #769, #772,
776, #779, #783, #784, #788, #789, #790, #796) — the spec-agent already proposed answers; nobody¶
ratified or rejected them. A sharper architecture document has near-zero marginal effect on this group by itself. It has a secondary effect on a few of them (see below) where the proposed answer is now also a written 🟢 principle, meaning the next probe could self-certify instead of waiting for a human reply at all — but the 17-item backlog is fundamentally a process gap. Recommend a companion process fix (batch design-triage pass, or a time-boxed "ratify by silence unless objected" rule) as at least as important as anything in this document.
Directly resolvable by the proposed decision-principles.md content (the AI could self-certify
on next probe without waiting on a human), roughly 6–9 of the 36:
| Issue | Principle applied | Confidence |
|---|---|---|
| #676 | C1 — registry/plugin default | 🟢 |
| #754 | D1(Q1) — zero importers, no design gate needed | 🟢 (factual check) |
| #776 | A1 — already answered by Wim in-thread, citing fix-at-source | 🟢 |
| #921 | A3 — closed/free-form axis + AzureRM precedent, matches Wim's own redirect | 🟢 |
| #939 | A3 — closed-enum extension is a known, safe operation | 🟢 |
| #843 | E — its own bundle-vs-boundary litmus test, promoted to a citable doc | 🟢 (definitional part only; sizing still needs a human) |
| #788, #796 | B2 — rules out "cap and lose data," narrows the option space | 🟡 |
| #207 (slice B) | D3 — localStorage default | 🟡 |
Genuinely need a human, and this document doesn't change that — roughly 10–13 of the 36,
spread across three different hats the current state:awaiting-design label conflates into one:
- Designer/Style-Guide, not Architect: #750, #757, #762, #764, #768, #769, #772 — these are form
questions (
definition-of-ready.md's own role table already says so) that need a Style-Guide decision, not an architecture one. Recommend re-routing these to referencedocs/contributing/style-guide.mdexplicitly and, longer-term, considering whetherstate:awaiting-designshould split into two labels matching the two hats it currently merges. - Security/product policy, not Architecture: #779, #780, #782, #783, #784, #785 — see
decision-principles.md§F. - Genuine novel judgment calls with no usable precedent found: #931 (new filter-condition kind),
#837 (buy-vs-build an external workflow engine — a real licensing/dependency tradeoff), #752
(build-vs-adopt a UI dependency, 🟡 default offered but not a confident answer), #934 (already
resolved via requestor answer per the survey — dev-authored reports only, phase 2 deferred),
#837/#839 (blocked on sequencing, not a design question — see the decompose-routing note in
decision-principles.md§E).
Umbrella issues (#787, #777, #723, #699) are decompose-by-design or intentionally parked and aren't "resolved" by any document — they're tracking issues for already-triaged backlogs.
Net honest estimate: roughly a quarter of the current backlog (6–9 issues) becomes directly self-certifiable by the AI once this document is ratified. A further large fraction (17 issues) needs a five-minute human ratification pass that has nothing to do with document content. The remainder needs a human, but this review at least tells you which human (architect vs. designer vs. security owner vs. product board) instead of leaving everything addressed to "the architect."
4. Backlog restructuring proposals¶
4.1 Merges — small related items into one build item¶
- #757 (border-radius scale) + #762 (semantic design-token layer). Radius is a token category;
standing up
index.csstokens covers both in one pass. Building them separately means two functional-test environments clicking through the same visual surfaces twice. - #768 (Apply/Save verb rework) + #769 (rotated-matrix reduced-mode signal) + #776 (unify "governed"
definition in the rotated view). All three are findings against the same component
(
RotatedMatrixView/ the matrix save flow) from the same audit cluster (C-03, H-09, H-10). Currently three separate awaiting-design tickets that would each need their own review pass on the same screen. Recommend one "Rotated matrix UX pass" issue. - #788 + #796. Already explicitly cross-referenced by the requestor ("align with #788 so we don't
ship two pagination idioms") — these should not be built as independent parallel slices. Either
merge, or make #796 formally block-on / consume #788's decision rather than sitting in the same
awaiting-designstate as an unrelated peer. - (Positive precedent, no action needed): #779 already bundles three audit sub-findings (L-1/L-2/L-3, all PowerShell-worker hardening) into one issue — cite this as the model other audit-derived clusters should follow.
4.2 Missing epic-level groupings¶
- Component-consolidation epic — #750 (Button), #752 (Modal), #754 (Section/StatCard), #764
(detail-page family unification) are four facets of one initiative (UX audit H-01/H-02: "no shared
component layer," "two products in one"). Each is currently an independently-stalled ticket
re-asking a slice of the same underlying component-API-shape question. Recommend one epic that
settles the component API shape once (Button variants, Modal API, Section/StatCard API) with #764
as the epic's capstone consumer, rather than four parallel
awaiting-designtickets. - Matrix scale/perf epic — #788 (cursor pagination), #789 (column virtualization), #790 (incremental matview refresh), #796 (endpoint pagination) are four faces of "handle a very large tenant's matrix." Currently four independent tickets, each partially blocked on conventions the others would set. Recommend a shared epic that fixes the pagination convention once (#788) and treats the rest as sequenced consumers.
- BloodHound integration epic — #939 (crawler/ingestion) → #940 (attack-path risk plugin) → #941 (reverse feed/export) are three phases of one integration story, already loosely sequenced by content but not structurally linked. Recommend a parent epic issue with these as sub-issues (the repo's GitHub sub-issue feature is already used elsewhere per the DoR docs), so the dependency is visible on the board instead of implied by title similarity.
- Deployment-hardening epic — #779, #780, #782, #783, #784, #785, and umbrella #787. Each ships as its own small, subsystem-disjoint PR (don't literally merge these — combining PowerShell IEX hardening with Postgres TLS with container limits in one PR would blow past the one-issue-per-branch and file-size/complexity discipline), but grouping them under a visible parent epic gives the Product Board one initiative to prioritize instead of seven scattered low-priority tickets that individually never clear the bar for attention. Note per §F above: this cluster is a security-policy epic, not an architecture one — route it to a policy owner, not "the architect."
4.3 Cross-item contradictions in the backlog¶
- #749 as currently scoped contradicts the Style Guide it's meant to enforce (detailed in §1.3).
Needs re-scoping to "demote indigo only" before it's buildable — filing it as-is and building it
literally would violate
style-guide.md §1's two-role color system. - #843 (Logical Applications) and #937 (Business Roles excluded from matrix rows) are each
individually reasonable but risk drifting out of sync if built as two independent point-fixes.
Both are instances of the same underlying rule (§1.2 /
decision-principles.mdA1: exclude synthetic/governance resourceTypes from general resource-axis queries). Recommend #843's implementation explicitly extend the same exclusion mechanism #937 introduces (or vice versa, whichever ships first), rather than each adding its own ad hoc filter — which is exactly how #937 itself recurred after #446 fixed the same problem forGroupOwnership. - #931 and #680 (new matrix subject-filtering UI) vs. #788/#789 (matrix subject-axis pagination and virtualization) are not yet a live contradiction, but both pairs touch the same subject axis's data-fetch contract concurrently. If #931/#680 ship UI that assumes a fully-loaded, unbounded subject list while #788 simultaneously moves the grid to keyset pagination, whichever ships second risks silently breaking the other's assumption. Flagging now so the two pairs are sequenced or explicitly agree on one fetch contract, rather than discovering the conflict in a later PR.
5. What this review is not confident about¶
Stated explicitly per the task's own instruction to flag uncertainty rather than guess:
- The E. Sizing & decomposition rubric in
decision-principles.mdis the most speculative content in this whole review — synthesized from patterns across 12 issues, with no existing precedent to lean on. Expect it to need real revision after a handful of live uses. - B1 (materialize vs. compute-on-demand) and A4 (reuse the variant/reconciliation pattern) are well-evidenced syntheses across multiple docs, but nobody has ratified them as forward-looking rules — they're inferences from consistent past behavior, not quoted decisions.
- The 17-issue "just needs ratification" count rests on there being no comment after the templated proposal; it's possible some of those 17 have context (a Slack thread, a verbal conversation) this review couldn't see that would change the answer. Worth a quick human sanity check before treating the whole batch as ready to close in one pass.
- This review did not attempt to assess the security/hardening cluster's actual content (§F) — only that it's mis-routed as an architecture question. What the right rotation cadence, rate-limit layer, or vault posture should be is outside this review's scope and needs its own pass by whoever owns that policy.