The Maturity Engine
Navaid turns conversation evidence into a structured, evolving assessment of a company's UK-market readiness. The active model has 15 dimensions, three rings, and five maturity levels. The older strong/developing/fragile readiness representation remains only for legacy-session compatibility.
The Model
Levels
| Level | Name | Interpretation |
|---|---|---|
| 1 | Unaware | The issue is not yet recognized or evidenced |
| 2 | Aware | The company understands the issue but has limited preparation |
| 3 | Preparing | Work is underway with an emerging plan/evidence |
| 4 | Validated | UK-specific evidence validates the approach |
| 5 | Established | The capability is repeatable, owned, and operating |
An unassessed dimension is initialized at level 1 with assessed:false; its
numeric placeholder is not evidence that the company was assessed as Unaware.
Ring 1: Entry Feasibility
| ID | Dimension | What it evaluates |
|---|---|---|
market_customer_fit | Market & Customer Fit | UK demand, buyer behaviour, ICP |
regulatory_compliance | Regulatory & Compliance Exposure | VAT, PAYE, GDPR, licensing and sector rules |
go_to_market_strategy | Go-to-Market Strategy | UK messaging, pricing, channels and sales motion |
uk_credibility | UK Credibility & Social Proof | Local references, customers, events and advisers |
talent_hiring | UK Talent & Hiring Strategy | Critical hires and local-team readiness |
operational_infrastructure | Operational Infrastructure | Entity, workspace, payroll, legal and visa readiness |
competitive_intensity | Competitive Intensity | Landscape knowledge and differentiated positioning |
Ring 2: Enabling Conditions
| ID | Dimension | What it evaluates |
|---|---|---|
capital_runway | Capital Adequacy & Runway | Resources for 18–24 months of learning |
banking_infrastructure | Banking & Financial Infrastructure | Accounts, payments and inter-company transfers |
timeline_realism | Timeline Realism & Patience | Sales-cycle and stakeholder expectation calibration |
leadership_attention | Leadership Attention & Ownership | Executive commitment and accountable ownership |
Ring 3: Risk Modifiers
| ID | Dimension | What it evaluates |
|---|---|---|
decision_making | Decision-Making Under Uncertainty | Decision quality with incomplete information |
commercial_adaptability | Commercial Adaptability | Willingness to change pricing, message and approach |
reversibility_optionality | Reversibility & Optionality | Staged, reversible commitments |
success_definition | Internal Success Definition | Aligned lead, lag and learning measures |
The canonical catalogue and UI types live in
frontend/src/types/maturity.ts. Edge code has a runtime-safe equivalent in
_shared/maturity-signals.ts; changes must be mirrored and parity tested.
State Shape
Each dimension stores:
{
id: string;
name: string;
description: string;
level: 1 | 2 | 3 | 4 | 5;
assessed: boolean;
confidence: number; // 0..1
rationale: string | null;
pending_evidence: number;
last_updated: string;
}
The full state groups dimensions into rings and has a top-level
last_updated.
Storage locations
| Location | Purpose |
|---|---|
company_profiles.maturity_state | Authoritative live company map |
company_profiles.version | Optimistic concurrency version |
sessions.maturity_state_at_start | Baseline for this session |
sessions.maturity_state | Latest per-session snapshot |
messages.maturity_signals | Raw evidence emitted on a turn |
maturity_snapshots | Historical completion snapshots |
company_profile_history.maturity_state | Versioned provenance/audit |
One company can have several concurrent sessions. The selected browser company does not change the authoritative row.
Structured Model Output
The advisor prompt asks the model for tagged sections:
<response>user-facing answer</response>
<reasoning>optional concise reasoning</reasoning>
<signals>[...]</signals>
<task_completions>[...]</task_completions>
<company_facts>{...}</company_facts>
A maturity signal is:
{
dimension_id: string;
inferred_level: 1 | 2 | 3 | 4 | 5 | null;
confidence: 'low' | 'medium' | 'high';
rationale: string;
}
parseV2Response() is defensive:
- missing sections return safe defaults,
- malformed signal JSON produces no signals rather than corrupting state,
- string levels are normalized,
- unknown dimension IDs are excluded from maturity changes and can be retained as company facts,
nullexplicitly means insufficient evidence,- task completions and facts are parsed separately.
Do not let raw model JSON write directly to Postgres.
Evidence-Update Algorithm
The shared Edge Function algorithm uses:
| Confidence | Weight |
|---|---|
| high | 1.0 |
| medium | 0.7 |
| low | 0.4 |
Rules:
- A first medium/high signal can assess a dimension at its inferred level.
- A first low-confidence signal accumulates evidence but does not assess.
- Agreement with the current level reinforces confidence.
- Conflicting evidence accumulates in
pending_evidence. - Upgrades use a lower base threshold (
0.3) than downgrades (0.6plus current confidence). - One signal can change a dimension by at most one level per turn.
- An explicit non-assessment adds only slow pending evidence.
- Only changed dimensions appear in
touchedIds. - Level changes produce
{dimension_id,from,to}transitions.
The asymmetry avoids a single ambiguous statement collapsing a well-evidenced assessment while allowing new positive validation to progress.
The frontend mirror in frontend/src/utils/maturityAlgorithm.ts supports UI
preview/behaviour. It must not become a divergent authority.
Turn Pipeline
All three advisor paths use the same parser and state transition logic:
chatchat-livechat-multi
load company profile/version
└─ compose v2 prompt
└─ call Gemini
└─ parse tagged output
├─ persist messages/signals
├─ apply transitions
├─ optimistic profile write/retry
├─ update session snapshot
├─ merge facts/history
└─ complete/generate tasks
Prompt context
_shared/v2-prompt.ts combines:
- active v2 system prompt
- canonical output contract
- live maturity state
- company facts/learnings
- current/open tasks
- focus dimension
- recent conversation
- locale directive
- UK-readiness scope
Prompt Lab can test a draft, but production turns load the active stored v2 prompt.
Optimistic Persistence
_shared/maturity-persist.ts exists because parallel sessions can read the same
profile version.
Conceptually:
read version N + state A
apply signal → state B
UPDATE profile
SET state=B, version=N+1
WHERE id=? AND version=N
if no row updated:
read latest version/state
reapply this turn's signals
retry with bounded attempts
The helper then keeps the session snapshot aligned. An unconditional last-write wins update would lose evidence. All new maturity-writing paths must use the same concurrency strategy.
Database triggers also prevent authenticated browser clients from writing the derived fields directly.
Task Generation and Completion
_shared/tasks.ts generates next-level work when dimensions advance or summary
analysis identifies gaps.
Controls:
- use canonical dimension IDs and target levels,
- avoid duplicates against open tasks,
- keep model output within the task schema,
- attach company and originating session,
- allow role and priority assignment,
- complete tasks only from explicit completion signals or user action.
The public tasks Edge Function does not generate. It lists, updates, and
dismisses existing rows.
Session Completion
generate-summary:
- claims the session through
claim_session_summary(), - loads transcript and start/current states,
- generates narrative, constraints, role-based steps and warnings,
- computes dimensions advanced,
- generates/deduplicates next tasks,
- writes
maturity_snapshots, - finalizes the guarded session columns,
- records token usage.
The claim prevents double generation and double charging when two browser tabs complete simultaneously.
Where the Model Surfaces
- in-session right pane
/app/maturityfull-screen map/app/progression/app/tasks- session summary
- multi-company
/app/overview - Aida maturity tools
- MCP maturity resources/tools
- group voice agent instructions
Key Files
| Concern | Location |
|---|---|
| Catalogue and browser types | frontend/src/types/maturity.ts |
| Frontend algorithm mirror | frontend/src/utils/maturityAlgorithm.ts |
| Ring map | frontend/src/pages/app/maturity/MaturityRingMap.tsx |
| Dimension detail | frontend/src/pages/app/maturity/MaturityDimensionDetail.tsx |
| Radar | frontend/src/pages/app/maturity/MaturityRadar.tsx |
| Edge parser/algorithm | supabase/functions/_shared/maturity-signals.ts |
| Optimistic persistence | supabase/functions/_shared/maturity-persist.ts |
| Prompt composition | supabase/functions/_shared/v2-prompt.ts |
| Task generation | supabase/functions/_shared/tasks.ts |
| Text turns | supabase/functions/chat/index.ts |
| Single voice | supabase/functions/chat-live/index.ts |
| Group text | supabase/functions/chat-multi/index.ts |
| Completion | supabase/functions/generate-summary/index.ts |
Testing Invariants
Tests should cover:
- all 15 IDs exist once in the correct ring,
- levels clamp to 1–5,
- malformed/unknown signals cannot corrupt state,
- low-confidence first evidence stays unassessed,
- upgrade/downgrade thresholds are asymmetric,
- one turn moves at most one level,
- agreement reinforces confidence,
- transitions/touched IDs are correct,
- frontend and Edge algorithms stay in parity,
- two concurrent writes merge rather than overwrite,
- summary claim has one winner,
- task generation deduplicates.
Extension Guidance
Add or rename a dimension
This is a schema and product-model change, not a copy edit:
- update canonical browser catalogue/types,
- update Edge runtime catalogue,
- define all five level descriptors and localization,
- migrate stored JSON or provide backward aliases,
- update prompts, Aida/MCP tools, charts and seeded demo data,
- add parity and migration tests,
- consider historic snapshots that retain the old ID.
Change the algorithm
Update both implementations, add examples for upgrades/downgrades and run concurrency tests. Do not tune thresholds only in the UI.
Add another writer
Use:
- verified identity/tenant authorization,
- canonical parser,
- optimistic persistence,
- history provenance,
- token accounting,
- server-side task generation rules.
Troubleshooting
Map appears to reset
Check whether the page loaded a session snapshot instead of
company_profiles.maturity_state, and inspect profile version conflicts.
Parallel sessions lose changes
Find any unconditional profile update that bypasses
maturity-persist.ts.
Dimension never assesses
Inspect raw messages.maturity_signals, confidence, inferred level and pending
evidence. Low-confidence first evidence intentionally does not assess.
Task appears twice
Verify generation checked current open tasks with the same company, dimension/target and semantic purpose before insert.