Skip to main content

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

LevelNameInterpretation
1UnawareThe issue is not yet recognized or evidenced
2AwareThe company understands the issue but has limited preparation
3PreparingWork is underway with an emerging plan/evidence
4ValidatedUK-specific evidence validates the approach
5EstablishedThe 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

IDDimensionWhat it evaluates
market_customer_fitMarket & Customer FitUK demand, buyer behaviour, ICP
regulatory_complianceRegulatory & Compliance ExposureVAT, PAYE, GDPR, licensing and sector rules
go_to_market_strategyGo-to-Market StrategyUK messaging, pricing, channels and sales motion
uk_credibilityUK Credibility & Social ProofLocal references, customers, events and advisers
talent_hiringUK Talent & Hiring StrategyCritical hires and local-team readiness
operational_infrastructureOperational InfrastructureEntity, workspace, payroll, legal and visa readiness
competitive_intensityCompetitive IntensityLandscape knowledge and differentiated positioning

Ring 2: Enabling Conditions

IDDimensionWhat it evaluates
capital_runwayCapital Adequacy & RunwayResources for 18–24 months of learning
banking_infrastructureBanking & Financial InfrastructureAccounts, payments and inter-company transfers
timeline_realismTimeline Realism & PatienceSales-cycle and stakeholder expectation calibration
leadership_attentionLeadership Attention & OwnershipExecutive commitment and accountable ownership

Ring 3: Risk Modifiers

IDDimensionWhat it evaluates
decision_makingDecision-Making Under UncertaintyDecision quality with incomplete information
commercial_adaptabilityCommercial AdaptabilityWillingness to change pricing, message and approach
reversibility_optionalityReversibility & OptionalityStaged, reversible commitments
success_definitionInternal Success DefinitionAligned 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

LocationPurpose
company_profiles.maturity_stateAuthoritative live company map
company_profiles.versionOptimistic concurrency version
sessions.maturity_state_at_startBaseline for this session
sessions.maturity_stateLatest per-session snapshot
messages.maturity_signalsRaw evidence emitted on a turn
maturity_snapshotsHistorical completion snapshots
company_profile_history.maturity_stateVersioned 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,
  • null explicitly 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:

ConfidenceWeight
high1.0
medium0.7
low0.4

Rules:

  1. A first medium/high signal can assess a dimension at its inferred level.
  2. A first low-confidence signal accumulates evidence but does not assess.
  3. Agreement with the current level reinforces confidence.
  4. Conflicting evidence accumulates in pending_evidence.
  5. Upgrades use a lower base threshold (0.3) than downgrades (0.6 plus current confidence).
  6. One signal can change a dimension by at most one level per turn.
  7. An explicit non-assessment adds only slow pending evidence.
  8. Only changed dimensions appear in touchedIds.
  9. 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:

  • chat
  • chat-live
  • chat-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:

  1. claims the session through claim_session_summary(),
  2. loads transcript and start/current states,
  3. generates narrative, constraints, role-based steps and warnings,
  4. computes dimensions advanced,
  5. generates/deduplicates next tasks,
  6. writes maturity_snapshots,
  7. finalizes the guarded session columns,
  8. 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/maturity full-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

ConcernLocation
Catalogue and browser typesfrontend/src/types/maturity.ts
Frontend algorithm mirrorfrontend/src/utils/maturityAlgorithm.ts
Ring mapfrontend/src/pages/app/maturity/MaturityRingMap.tsx
Dimension detailfrontend/src/pages/app/maturity/MaturityDimensionDetail.tsx
Radarfrontend/src/pages/app/maturity/MaturityRadar.tsx
Edge parser/algorithmsupabase/functions/_shared/maturity-signals.ts
Optimistic persistencesupabase/functions/_shared/maturity-persist.ts
Prompt compositionsupabase/functions/_shared/v2-prompt.ts
Task generationsupabase/functions/_shared/tasks.ts
Text turnssupabase/functions/chat/index.ts
Single voicesupabase/functions/chat-live/index.ts
Group textsupabase/functions/chat-multi/index.ts
Completionsupabase/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:

  1. update canonical browser catalogue/types,
  2. update Edge runtime catalogue,
  3. define all five level descriptors and localization,
  4. migrate stored JSON or provide backward aliases,
  5. update prompts, Aida/MCP tools, charts and seeded demo data,
  6. add parity and migration tests,
  7. 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.