Live vs periodic fields matrix (T1.9)
Status: locked for test (implement agent attestation 2026-08-12)
Purpose: Decide, for every P0 entity.field, whether the value is evented (live), import (periodic), or manual. Unblocks T1.10 freshness targets and tells ingest which path is allowed.
Related: SOURCE-INVENTORY.md (T0.5), SOURCE-OWNERSHIP.md, SOURCE-EVENT-MAPPING.md, PORSLINE-FORMS.md (T1.5), DATA-MODEL.md, SCHEMA.md, RISK-RULES-V1.md, ops/CSV-IMPORT.md (T2.8)
1. Classes (closed set)
Every P0 field uses exactly one primary class. Supplemental paths are notes, not a fourth class. The class value is never unknown.
| Class | Meaning | Write path | Not allowed |
|---|---|---|---|
| evented | Live product / Orbit fact. Collector is the only event write ingress (ADR 0002). Includes Orbit derived projections (SYS-01, T2.4, T2.9, T3.4) rebuilt from canonical events. |
Backend SDK / outbox, form webhook → collector, or Orbit job → collector | CSV as the source of truth for booking, payment, session, pause, completion, or stop |
| import | Periodic batch that is not yet (or not primarily) on the live event path. Job audits rows and, if an event is produced, POSTs the collector — it never writes canonical_event directly (T2.8). |
Orbit import job (apps/import-job) |
Authoritative P0 booking / payment / session facts |
| manual | Human-authored in Orbit (staff UI / API). Not rebuildable from the event log (SCHEMA.md rebuild boundary). | apps/web operational writes (action, insight) |
Using staff notes as a substitute for Simiaroom backend outcomes |
Derived vs evented: client.current_stage, session_count, risk_level, and other T2.9 / T3.4 serving fields are evented. They are projections of canonical events, not a separate class.
CDC (inventory #9, blocked) is reconciliation, not a field class. When unblocked it re-emits the same evented facts with the same source + source_event_id (SOURCE-EVENT-MAPPING.md).
2. Latency expectations per class
Aligned with TECHNICAL-ARCHITECTURE.md §5 (freshness) and VOLUME-AND-RETENTION.md §6 (dashboard p95). Numeric DQ gates (evented accept < 5 s, event lag < 24 h, completeness ≥ 99%) are locked in DATA-QUALITY-BAR.md (T1.10). These rows remain the class contracts.
| Class | Ingest freshness (source → collector / store) | Serving freshness (decision-loop screens) | Dashboard query p95 (not ingest lag) |
|---|---|---|---|
| evented | Same-day required for P0 authoritative outcomes. Collector HTTP accept is seconds; profile/risk recompute follows SYS-01 (minutes after the triggering event in normal ops). |
At-risk / Client 360 / journey / retention read precomputed profile + snapshots, not raw replay. | Timeline ≤ 2 s; at-risk list ≤ 3 s; funnel / retention ≤ 5 s |
| import | Near-daily acceptable (architecture §5). Shipped P0 feed ops_support_ticket is weekly (CSV-IMPORT.md). Form CSV (until webhook) is weekly. One-time historical backfill is off-peak, not a freshness SLO. |
After collector ingest + SYS-01, same serving path as evented. Ops extras that stay audit-only (open tickets) appear on import reports, not as risk facts, until mapped to an event. |
Same screen p95 after the batch has been ingested |
| manual | Immediate on staff save (operational Postgres). | Immediate on /at-risk assign/close and Client 360 actions (T3.8). |
Same as evented screens that join action |
Late events are first-class: preserve occurred_at, sent_at, received_at (DATA-MODEL.md §9). Freshness is measured on received_at vs occurred_at (T2.11), not by silently rewriting history.
3. Import feeds and owners
Every import field below maps to one of these feeds. Owners use T0.5 team slugs (SOURCE-INVENTORY.md). Named Simiaroom contacts remain TBD; escalation: CX Orbit technical lead → Simiaroom engineering lead.
| Feed ID | What it carries | Cadence | Owner (file / vendor) | Owner (Orbit job) | Inventory # | P0 status |
|---|---|---|---|---|---|---|
ops_support_ticket |
Call logs, tickets, complaint status, manual follow-ups that are not yet evented (architecture §4.4). Resolved rows with case fields may plan internal_case_resolved. |
Weekly | Simiaroom ops (CSV export) | cx-orbit-ops / apps/import-job |
14 + §4.4 | Shipped (T2.8). Job granted; live ops CSV requested |
porsline_form_export |
FDB-02 / FDB-04 / FDB-07 responses until Porsline webhook is granted | Weekly (until webhook → evented) | cx-orbit-data + Simiaroom ops | cx-orbit-data (T2.7) | 11 | requested — P0 mitigation; join keys in PORSLINE-FORMS.md; T2.7 importer |
myoutcomes_form_export |
Same FDB types; alternate, not a second primary | Same as Porsline | cx-orbit-data + Simiaroom clinical | cx-orbit-data (T2.7) | 12 | requested — do not run as P0 SoT. T1.5 locked Porsline as primary |
assessment_legacy |
Historical assessment_completed until assessment SDK is live |
One-time / as needed | assessment-team | cx-orbit-data | 2 | Mitigation only. Primary class remains evented (simiaroom_assessment) |
churn_reason_historical |
Historical CHR-02 / CHR-06 ledgers / offboarding forms | One-time / as needed | clinical-team + cx-orbit-data | cx-orbit-data | 7, 8 | Mitigation only. Primary class remains evented (simiaroom_clinical) |
Not import feeds (locked): bookings, payments, sessions, pause, treatment completion, therapist selection. Those stay evented even if CDC/import is used later for backfill — backfill must reuse the producer source + source_event_id (SOURCE-OWNERSHIP.md).
4. P0 entity coverage
DATA-MODEL §1 entities. Physical tables: SCHEMA.md / packages/db. Generic aliases: Customer=client, Provider=therapist, Interaction=session.
| Entity | Table | P0 decision-loop? | Primary class (entity) |
|---|---|---|---|
| Client | client |
Yes (360, journey, retention, at-risk) | evented (derived profile) |
| Therapist | therapist |
Yes (join key; T3.5 depth is P1) | evented |
| Session | session |
Yes | evented |
| Booking | booking |
Yes | evented |
| Payment | payment |
Yes | evented |
| Assessment | assessment |
Yes (funnel assessed) |
evented |
| FeedbackResponse | feedback_response |
Yes when present (SIG-FDB-*; missing ≠ trigger) | import (P0) → evented when webhook granted |
| Insight | insight |
No (P1 T3.7 shipped /insights) |
manual |
| Action | action |
Yes (T3.8 assign/close) | manual |
| Event | canonical_event |
Yes (timeline / rebuild) | evented |
| IdentityLink | identity_link |
Yes (identified funnel; incomplete identity excluded) | evented |
| RawEvent | raw_event |
Infrastructure | evented |
| EventQuarantine | event_quarantine |
Infrastructure | evented |
| RiskSnapshot | risk_snapshot |
Yes (explainability) | evented (derived) |
Import audit tables (import_batch, import_row) are the import class’s own audit, not P0 decision fields.
5. Matrix — `entity.field` → class
Legend: P0 decision = used by journey (T3.2), retention (T3.3), at-risk (T3.4), Client 360 (T2.10), or actions (T3.8). Class is never unknown.
project_id, created_at, and updated_at on event-backed tables are Orbit serving metadata written with the projection (evented). On action / insight they are manual.
5.1 Client (`client`) + derived profile
| Field | Class | P0 decision | Source / notes |
|---|---|---|---|
client_id |
evented | Yes | Identity from authentication_succeeded / identity_link_created (simiaroom_auth, inventory #1) |
current_stage |
evented | Yes | T2.9 from journey events (JOURNEY-STAGE-MACHINE.md) |
current_therapist_id |
evented | Yes | therapist_selected / later session+booking (simiaroom_matching #3) |
session_count |
evented | Yes | Count of session_completed only (T2.4) |
last_session_at |
evented | Yes | Last session_completed |
days_since_last_session |
evented | Yes | Derived at as_of from last_session_at |
next_session_booked |
evented | Yes | Open booking_confirmed with slot ≥ as_of |
established_cadence_days |
evented | Yes | Observed median gap (T2.4) or declared on pause event; not import |
current_pause_flag |
evented | Yes | therapy_pause_declared without later resume/complete (simiaroom_clinical #7) |
expected_return_date |
evented | Yes | Pause-event property (not a client column today; T2.9/T3.4 input). Fallback 14 UTC days if missing is a rule, not an import |
completion_flag |
evented | Yes | treatment_completed (#7) |
therapist_change_count |
evented | Yes | CHG-01/02 + rematch (simiaroom_matching #3) |
last_meaningful_event_at |
evented | Yes | T2.9 from SYS-01 trigger set |
risk_level |
evented | Yes | T3.4 risk_v1 (derived). Storage may be the enum value unknown when unscored — that is a level, not a field class |
risk_reasons |
evented | Yes | T3.4 composition |
risk_rule_version |
evented | Yes | Written by T3.4 (risk_v1) |
Identity refs (user_id / anonymous_id) |
evented | Yes | identity_link + event envelope; not CSV |
Segment attrs (channel, issue_type) |
evented | Yes (T3.2 optional filters) | channel / auth_method on identified events; issue_type on assessment_completed. Null → drop out of that filter (METRICS-AND-RISK.md §3) — still classified |
5.2 Therapist (`therapist`)
| Field | Class | P0 decision | Source / notes |
|---|---|---|---|
therapist_id |
evented | Yes | Matching / session / booking producers (#3, #4, #6) |
specialty_tags |
evented | No (T3.5 P1) | Matching service when available; omit rather than invent. Not import |
5.3 Session (`session`)
| Field | Class | P0 decision | Source / notes |
|---|---|---|---|
session_id |
evented | Yes | simiaroom_sessions #6 |
client_id / therapist_id / booking_id / payment_id |
evented | Yes | Same producers; join keys |
session_number |
evented | Yes | T2.4 continuum (not reset on rematch) |
status |
evented | Yes | session_completed / no-show / attendance issue |
scheduled_start_at |
evented | Yes | Booking/session scheduled slot |
completed_at |
evented | Yes | session_completed |
attendance |
evented | Yes | SES-06 / SES-08 / SES-S01. Browser session_joined is non-authoritative (P1) |
gap_from_previous_days |
evented | Yes | T2.4 derived |
therapist_changed_since_previous |
evented | Yes | T2.4 derived |
feedback_linked |
evented | Yes | Set when a feedback row joins; feedback payload class is import until webhook |
continuation_status |
evented | Yes | Derived from pause/stop/complete/next booking |
5.4 Booking (`booking`)
| Field | Class | P0 decision | Source / notes |
|---|---|---|---|
booking_id / client_id / therapist_id / payment_id / slot_id |
evented | Yes | simiaroom_booking #4 |
status |
evented | Yes | booking_confirmed / booking_cancelled |
scheduled_start_at |
evented | Yes | Slot start (scheduling privacy class) |
cancel_reschedule |
evented | Yes | Cancel actor/reason on BKG-S04 |
Browser BKG-01/05/06 are behavioral only — they do not write these fields.
5.5 Payment (`payment`)
| Field | Class | P0 decision | Source / notes |
|---|---|---|---|
payment_id / client_id / booking_id / session_id |
evented | Yes | simiaroom_payments #5 (gateway #10 feeds the handler, not Orbit) |
amount / currency |
evented | Yes | PAY-06 / PAY-07 |
status |
evented | Yes | Succeeded / failed — never from checkout UI (PAY-04) |
package |
evented | Yes | Payment verification properties |
discount / refund |
evented | Yes | Payment backend; refund is a compensating event, not a silent overwrite |
5.6 Assessment (`assessment`)
| Field | Class | P0 decision | Source / notes |
|---|---|---|---|
assessment_id / client_id |
evented | Yes | simiaroom_assessment #2 |
method |
evented | Yes | assessment_completed |
progress |
evented | Funnel uses completion, not in-progress | Live completion is evented. ASM-02/04 browser progress is P1 behavioral |
completed_at |
evented | Yes | ASM-09. Supplemental import: assessment_legacy until SDK live — does not change primary class |
5.7 FeedbackResponse (`feedback_response`)
| Field | Class | P0 decision | Source / notes |
|---|---|---|---|
feedback_id |
import | Yes when present | Feed porsline_form_export (primary) or myoutcomes_form_export (alternate). Target class evented when inventory #11/#12 webhook is granted |
client_id / session_id / therapist_id |
import | Yes (join) | PORSLINE-FORMS.md join keys; T2.7 importer. Missing join → quarantine, not a silent client |
form_type / form_version |
import | Yes | Form catalog (PORSLINE-FORMS.md) |
scores |
import | Yes (SIG-FDB-LOW / DECLINE) | Restricted. Missing scores do not fire signals |
free_text |
import | No (restricted; not a risk input) | Restricted storage; not a P0 decision field |
imported_category |
import | No (P1 text themes) | Rule/keyword/LLM-light — T3.6 |
submitted_at |
import | Yes | Form occurred_at / submitted timestamp |
P0 rule: do not block at-risk or timeline if feedback is empty (empty.feedback = true on Client 360).
5.8 Insight (`insight`) — P1 entity, class locked
| Field | Class | P0 decision | Source / notes |
|---|---|---|---|
insight_id / title / severity / segment / evidence / suggested_action / status |
manual | No | Semi-manual (T3.7 /insights). Not evented. source=insight actions are T3.10 (P1; not the P0 at-risk loop) |
5.9 Action (`action`)
| Field | Class | P0 decision | Source / notes |
|---|---|---|---|
action_id / title / source / linked_client_id / linked_insight_id / owner / priority / status / result / completed_at |
manual | Yes | T3.8 staff UI/API (client_risk / manual). T3.10 allows source=insight + linked_insight_id from /insights |
| Canonical INT-01 / INT-03 / INT-09 | evented | Yes (SIG-FOLLOWUP) | Orbit Actions → collector (cx_orbit_system #14). Supplemental import: resolved ops_support_ticket rows may plan internal_case_resolved (T2.8). Open/in-progress tickets stay audit-only |
T3.8 today persists operational rows and does not yet emit INT-03/INT-09. The class is still evented for those canonical types (ownership + mapping). Until emit ships, SIG-FOLLOWUP only fires from fixtures or import-planned INT-09 — that is a residual, not an unclassified field.
5.10 Event, identity, ingest, risk snapshot
| Entity.field | Class | P0 decision | Source / notes |
|---|---|---|---|
canonical_event.* |
evented | Yes | Collector mapping from accepted raw |
identity_link.* |
evented | Yes | AUTH-04 + T2.3 job (simiaroom_auth #1) |
raw_event.* / event_ingestion_request.* / event_dedup_* |
evented | Infra | Collector only |
event_quarantine.* |
evented | Infra | Collector reject/quarantine path |
risk_snapshot.level / reasons / rule_version / source_event_id / computed_at |
evented | Yes | T3.4 append-only audit |
import_batch.* / import_row.* |
import | Audit | T2.8; not decision facts |
source_reconciliation_run.* |
evented | DQ (T2.11) | Orbit ingest job; CDC still blocked (#9) |
6. At-risk inputs (`risk_v1`) — every input has a class
Authoritative input list: RISK-RULES-V1.md §2. Test Agent check: no row without a class.
| Input | Class | Producer / feed | Signal(s) |
|---|---|---|---|
current_stage |
evented | T2.9 from journey events | Eligibility / churned queue |
session_count |
evented | session_completed (#6) |
Eligibility (need ≥ 1) |
last_session_at |
evented | session_completed |
Gap math |
days_since_last_session |
evented | Derived at as_of |
SIG-GAP-*, SIG-NO-BOOKING |
established_cadence_days |
evented | T2.4 / pause properties | SIG-GAP-CADENCE / LONG / FALLBACK |
next_session_booked |
evented | Future booking_confirmed (#4) |
SIG-GAP-LONG copy, SIG-NO-BOOKING |
current_pause_flag |
evented | therapy_pause_declared (#7) |
Suppressions / cap |
expected_return_date |
evented | Pause record properties | SIG-GAP-PAUSE-BREACH |
completion_flag |
evented | treatment_completed (#7) |
Hard suppress |
Recent payment_failed / payment_succeeded |
evented | PAY-07 / PAY-06 (#5) | SIG-PAY-FAIL |
Recent booking_cancelled / session_no_show_recorded |
evented | BKG-S04 / SES-08 (#4, #6) | SIG-NOSHOW |
therapist_change_count / therapist_change_* |
evented | CHG-01/02 (#3) | SIG-THERAPIST-CHG |
Feedback scores / session_dissatisfaction_reported |
import | porsline_form_export (primary). Target evented when #11 webhook granted. Missing ≠ trigger |
SIG-FDB-LOW / DECLINE / DISSAT |
client_contact_attempted / internal_case_resolved |
evented | Orbit Actions → collector (#14). Supplemental import ops_support_ticket for resolved extras |
SIG-FOLLOWUP |
therapy_stop_declared |
evented | simiaroom_clinical (#7). Supplemental import churn_reason_historical for backfill only |
SIG-STOP |
unplanned_gap_detected (CON-S01) |
evented | Orbit T2.4 — corroboration only, not a required input | SIG-GAP-* |
as_of is the report clock, not a stored field — not classified.
7. P0 decision fields — none left unclassified
Closed list used by the P0 loop. Every row has class evented, import, or manual.
| Decision use | Fields | Class |
|---|---|---|
| Identity / funnel entry | client_id, identity link, channel |
evented |
| Journey spine | assessment complete, therapist selected, booking confirmed, payment succeeded, session completed | evented |
| Retention / session number | session_number, session_count, completed_at, continuation flags |
evented |
| Pause / complete / stop | current_pause_flag, expected_return_date, completion_flag, therapy_stop_declared |
evented |
| At-risk score | All §6 inputs except feedback | evented |
| At-risk score (optional quality) | Feedback scores / FDB-07 | import |
| At-risk follow-up signal | INT-03 / INT-09 | evented (+ import supplemental) |
| At-risk serving | risk_level, risk_reasons[], risk_rule_version, risk_snapshot.* |
evented |
| Ops action loop | action.* |
manual |
| Ops extras not in Actions UI | Ticket/call/complaint rows | import (ops_support_ticket) |
8. Alignment with T0.5 source inventory
| Inventory # | System | How this matrix uses it |
|---|---|---|
| 1–8 | Simiaroom backends | evented primary for identity, assessment, matching, booking, payment, session, clinical lifecycle |
| 9 | Product DB CDC | Blocked — not a field class; future reconciliation of evented facts |
| 10 | Payment gateway | Feeds payments-team handler; Orbit still evented from #5 |
| 11–12 | Porsline / MyOutcomes | import until webhook granted; then evented |
| 13 | Collector | Ingress for all evented (and import-emitted) events |
| 14 | Orbit store + jobs | evented derived + INT-*; manual actions/insights; import job owner for ops_support_ticket |
No fantasy sources. Access states stay as in the inventory (requested / blocked / granted).
9. Out of scope (explicit)
| Item | Why deferred |
|---|---|
| T2.7 form importer implementation | P1 task; this matrix only locks class + owner + cadence for FDB fields. Catalog: PORSLINE-FORMS.md |
| Emitting INT-03/INT-09 from T3.8 | Class is evented; emit is a later wiring residual |
| Browser SDK fields (ASM-02/04, BKG-01/05/06, PAY-04, SES-04, RET-01) | P1 behavioral; non-authoritative; do not write P0 current-state facts |
| ClickHouse / sub-second BI | Capacity guardrail; ingest class ≠ query engine |
| Using CSV for booking/payment/session | Forbidden — would contradict T0.6 producer-of-record |
10. Sign-off
| Role | Name | Date | Status |
|---|---|---|---|
| Data lead (cx-orbit-data) | TBD — stakeholder sync | — | Pending named signatory |
| Eng lead (CX Orbit technical lead) | TBD — stakeholder sync | — | Pending named signatory |
| Implement agent (T1.9 deliverable) | cx-orbit-implement-task | 2026-08-12 | Matrix complete; no P0 decision field unclassified; ready for Test Agent |
References
- SOURCE-INVENTORY.md — T0.5 systems, owners, access
- SOURCE-OWNERSHIP.md — producer of record
- SOURCE-EVENT-MAPPING.md — canonical types
- RISK-RULES-V1.md — at-risk inputs
- ops/CSV-IMPORT.md —
ops_support_ticket - TECHNICAL-ARCHITECTURE.md §4.4–§5
- VOLUME-AND-RETENTION.md §6
- DATA-QUALITY-BAR.md — T1.10 numeric gates (completeness / lag / dup / go-live)
- PORSLINE-FORMS.md — T1.5 catalog; Porsline primary; weekly import until webhook