Skip to content

Readiness & Open Decisions — for Dave and Rob

Ref: suitability-engine-arch-3 · Capability: N/A — cross-cutting Status: draft Repo: suitability-engine

One page: what’s actually shipped, what’s mid-review, what’s blocked, and every decision that needs a human call before it’s technically ready. Snapshot as of 2026-08-17 — statuses below are live states, not aspirational.

1. What’s actually merged (real, in main, today)

Section titled “1. What’s actually merged (real, in main, today)”
  • HTTP service exposure (format=json export, /healthz) — suitability-engine#2
  • TENANT_CONFIG_STATIC_JSON bootstrap seed path — #5
  • Client-facing report UX spec + working reference implementation, adviser review design spec + wireframe, Origo integration architecture overview — #9
  • Multi-tenant report templates — format=docx now works per-tenant#8, closing #6. The review round found and fixed real security issues before merge: a tenant slug/ID collision that could leak one tenant’s data into another’s template, a symlink path-traversal escape from the template store, missing per-tenant subdirectory enforcement, filesystem paths leaking into error responses, and no size cap on partner-supplied templates. 130/130 tests passing.
  • Partner template fidelity smoke test#15, a golden-record check + CLI a partner can run against their own template before deploying it. 149/149 tests passing.
  • Self-hosted licence-gating — the wtp-hosted bypass is closed#7. DEPLOYMENT_MODE is now a compile-time constant baked into dist/ by which Docker build target produced the image; no runtime input (env var, mounted file, CLI flag) can change it any more. Verified end-to-end (built both variants, confirmed each behaves correctly), 5 regression tests, 197 tests passing.
  • Template validation/lint#18, closing #12. A pre-flight check + CLI for a candidate template’s {{tag}} references against the SuitabilityData contract, with a decompression-size cap on the parsing path.
  • Self-serve template upload API#25, landing #11 onto main for real (hardened against a symlink write-target escape, atomic rename-based writes, an upload-time size ceiling).
  • This readiness hub + subdomain rename + REST/gRPC/GraphQL API definitions#24.
  • Branch protection enabled on main — required status checks (typecheck + test, docker build smoke, governance/frontmatter), linear history, no force-push/deletion, conversation resolution required. This repo had none until 2026-08-17 (auto-flagged as a critical governance finding, issue #26).
PRWhatStatus
suitability-engine#10Astro/Starlight docs-siteThe licence-gating-disclosure issue that blocked this is fixed (via #24, merged into this branch) and the underlying gap itself is now closed (#7 merged) — ready for a fresh review and merge to main.
client-onboarding#2684FINIO shared-hub decommissionBLOCKED. The FINIO hub was destroyed cleanly in Azure, but CO’s Pulumi program (apps/client-onboarding/infra/__main__.py) has an unconditional StackReference.require_output() call to the now-gone stack that runs for every environment, not just Benchmark. pulumi preview fails in wtp-stg right now.

3. Topology — what’s built and how it fits together

Section titled “3. Topology — what’s built and how it fits together”
flowchart TB subgraph ORIGO["Origo's infrastructure (self-hosted, SPEC-152 Option D)"] OA["Origo adviser UI\n(Origo builds — SPEC-005 design only)"] OC["Origo client UI\n(Origo builds from SPEC-004 reference impl)"] SE["suitability-engine container\n- licence-gated at startup (#7, MERGED, bypass closed)\n- per-tenant template store (#8, MERGED)\n- self-serve template upload (#25, MERGED)\n- REST today; gRPC/GraphQL contracts defined, unbuilt"] OA -->|HTTP| SE OC -->|HTTP| SE end subgraph WTP["WTP-operated infrastructure"] COREAD["client-onboarding\ninternal read-API\n(only call that ever crosses back)"] CRM["CRM-bridge (SPEC-153)\nstill embedded in client-onboarding\ninbound-only, Plannr only\ncandidate for future extraction (ADR-0001)"] end subgraph FUTURE["Anticipated internal consumers"] DB["data-backbone"] end SE -->|"HTTPS, shared-secret bearer\n(API-key-with-scopes planned)"| COREAD DB -.->|"wants CRM integration\n(2nd demand signal, not yet built)"| CRM classDef shipped fill:#1a5c3a,stroke:#0d3d24,color:#fff classDef review fill:#8a6d1c,stroke:#5c4a10,color:#fff classDef notbuilt fill:#555,stroke:#333,color:#fff class COREAD shipped class SE shipped class OA,OC review class CRM,DB notbuilt

Green = merged and live. Amber = built, PR open, in review. Grey = designed or requested, not built.

4. Decisions that need Dave and/or Rob — nothing below is silently assumed

Section titled “4. Decisions that need Dave and/or Rob — nothing below is silently assumed”
#DecisionWhy it can’t be made in codeRecommendation if you want one
CO#2684 blockerFix the CO Pulumi program’s unconditional FINIO StackReference, or accept the breakageTouches the live production CO stack — flagged by the reviewer as author-decision-required, not something to fix blindLand a minimal guard (get_output with a None-check instead of require_output) as a fast follow-up PR, merge that first, then this one
#13PDF export approachReal tradeoff between runtime weight (LibreOffice sidecar), a network dependency that contradicts the self-hosted design goal (cloud conversion API), or fidelity risk for a regulated document (pure-JS)LibreOffice headless, accepting the heavier self-hosted container — most consistent with “no external network calls” being the whole point of Option D
#14Graded confidence scoring vs. surfacing the real categorical signalBuilding real per-value confidence is a genuine feature investment; the honest alternative is presenting what already exists (sourceClass) without dressing it upSurface sourceClass directly for now; revisit graded scoring only if an adviser reviewer workflow actually needs the finer signal
#16Where suitabilityReasons narrative content livesCross-repo (client-onboarding owns the real data today); may be a product/commercial call about what Origo is expected to author themselvesDocument that Origo authors this themselves from derived recommendation data — cheapest, and consistent with “branding is not part of the contract”
#17Two-tier tenancy (partner → firm)Confirm with Origo whether this is real near-term scope before redesigning TenantConfigDon’t build ahead of confirmed need — single-tier is fine for a single-firm pilot
#19 / #3Per-tenant auth scoping for the shared bearer token (accepted gap, now live on main via #25 — the upload endpoint has no per-tenant scoping or audit trail)A real fast-follow, not a blocker, but worth a target date now that it’s live rather than hypotheticalScope before Origo pilots with more than one tenant sharing infrastructure
ADR-0001When CRM-bridge actually extractsDepends on SPEC-153 reaching a stable bidirectional design — not a fixed dateCommunicate explicitly to data-backbone’s team that this isn’t imminent, so they don’t wait indefinitely or build a duplicate
API interface strategyWhether/when to actually implement gRPC or GraphQL serversBoth are unbuilt-by-design until a real consumer needs themWait for data-backbone (or another internal consumer) to have a concrete access pattern before implementing either

5. Everything else — smaller, tracked, not blocking

Section titled “5. Everything else — smaller, tracked, not blocking”

#3 (scope the shared server token per-tenant) and #4 (request/audit logging) are pre-existing, not new from this work, and not yet started — #3 is the same underlying gap as #19 above. #20 (synchronous/uncached render needs a concurrency bound) and #21 (no audit-record persistence for docx exports specifically) are tracked, not started.

The Suitability Engine’s core pipeline, multi-tenant template mechanism, licence-gating, self-serve template upload, and full UX design are all built, hardened, and merged to main. Origo could pilot against the JSON contract, generate branded documents, and run self-hosted with a genuinely-enforced licence today. Every review this session surfaced real issues before merge, not after — 5 security fixes in #8, the wtp-hosted bypass fully closed and verified in #7, a symlink write-target escape closed in #25 — and main now has branch protection for the first time, closing a critical governance gap (#26) that predated all of this work.

What’s genuinely not built yet, and needs a scoping decision before it is: PDF export (#13), graded confidence scoring (#14), multi-firm tenancy (#17), and where suitabilityReasons narrative content lives (#16). What’s outstanding and not a decision, just work: the docs-site (#10) needs a final review pass now that its one blocker is fixed, and CO#2684 (FINIO) needs its Pulumi guard fix landed.