GiveCare SMS Runtime Contract¶
This reviewed document is the canonical public contract for the current SMS
runtime. Cross-repo consumers use its exact gc-sms.care Hound owner
projection. They do not infer current behavior from source files.
GiveCare SMS is organized around a small runtime kernel:
The product invariant is:
State-Machine Kernel¶
The kernel above is one instance of a broader rule: every durable concern in this runtime is a revisioned state machine advanced by a pure transition policy, not a process that keeps running on its own.
- One state machine per concern. Turns (
received -> reasoning -> committed -> review -> failed), loops (open -> blocked -> closed -> dismissed, via the puretransitionLooppolicy indomain/index.ts), outbox rows (queued -> sending -> accepted -> delivered -> undelivered -> failed -> blocked), reviews (open -> resolved), memories (active -> superseded -> deleted), consent (active -> paused -> stopped -> safety_hold), and assessment runs (in_progress -> complete) are each one row with an enumerable set of legal transitions. - Gates are transition guards. A gate does not exist to classify a message; it exists to decide whether a transition may happen at all.
- Evidence is the price of a transition. A loop or memory intent must carry an
evidenceQuotethat appears verbatim in the current caregiver message, and a source reference must come from tool evidence observed in the same reasoning span. No evidence, no transition; see Commit Rules below for how this is enforced. - Events are history, not state. Append-oriented event rows explain how a row got here; nothing reads them back as the source of truth for what happens next.
- No self-continuation. Nothing runs between events. A loop exists only as a row, and it advances only when an external event carries new evidence. The only timers in the system are consented, revision-bound solicitations —
convex/followups.ts#deliverDueFollowUpandconvex/turns.ts#deliverAssessmentInvitere-check consent at fire time, block the loop instead of sending if consent lapsed, and only ask the caregiver what happened. A timer may solicit evidence; it may never manufacture it or advance a transition itself. - Progressive disclosure is readiness-guarded transitions. Assent, offer cooldown, and natural-pause checks are guards on when an assessment offer or deeper instrument may transition into view, not steps in a fixed workflow sequence.
Design test for any new feature: what row, what transitions, what guard, what evidence, what revision? A feature that cannot answer all five does not yet fit this kernel.
Layers¶
Domain Layer¶
domain/ defines the product contract in plain TypeScript:
- inbound shape
- boundary decisions
- context shape
ReasonResultProposedReplyProposedIntent- deterministic gates
- validation rules
This layer should not depend on Convex or Pi. It is the shared contract the runtime must preserve.
Agent Layer¶
agent/ adapts Pi Agent Core to the GiveCare contract.
It owns:
- the Mira system prompt
- approved
AgentTooldefinitions - final
ReasonResultTypeBox parameters - redacted Pi event trace capture
- provider-request, tool-call, and total-span bounds
- final result validation before returning to Convex
The live Convex action registers only the Fireworks Pi provider. Deployment configuration chooses a model from that provider; simulation and bakeoff code may register broader provider sets inside an isolated evaluation runtime whose outbox dispatch is disabled.
It does not own memory, scheduling, outbox, consent, transport, proof, or learning promotion.
Caregiver skills are versioned, static procedures inside this layer. The model
may load at most one reviewed skill from agent/careSkills.ts during a reasoning
span. A skill organizes questions and actions; it contains no caregiver state,
current external facts, permissions, or safety authority. Skill use is emitted
as versioned reasoning evidence so scenario outcomes and operator review can
distinguish the procedure from the model and prompt around it.
scripts/scenarioRunner.ts and scripts/scenarioKernel.ts seed isolated Convex
state and compose the production gate, context, Pi turn, commit, fallback,
review, and outbox path for private scenario trees. State commits are local to
the evaluation instance and outbox dispatch is disabled. This is an evaluation
adapter over the runtime, not a second product commit path.
Convex Layer¶
convex/ owns durable reality:
- caregiver and channel state
- conversations, turns, messages
- memory and loop commits
- assessment runs and score snapshots
- events and outbox
- durable operator reviews
- operator/proof projections
- revisioned partner-health snapshots
- scheduling the Pi action
- validating and applying proposed intents
Convex is not the entire domain harness by itself, but it hosts and enforces most of the harness.
Owner Projection Layer¶
knowledge/ adapts generated JSON bundles into runtime search contracts:
benefitsresourcesstrategiesguidance
All generated inputs are downstream artifacts. Each source owner projects its
truth through Hound. Each gc-sms consumer accepts one exact Hound run,
resolves a pinned owner-projection ArtifactRef through the shared GiveCare
protocol, verifies the artifact digest, validates the owner schema, and replaces
only fixed local files atomically.
gc-benefits.registryprojects the benefits search bundle.gc-wiki.knowledgeprojects resources, strategies, and reviewed guidance.tools.assessmentsprojects the assessment instruments.evals.datasetprojects the public cases used by the retrieval quality gate.
These consumers are declared as owner-projection-sync adapters in
.givecare/module.json. They do not import another repository, fetch live owner
data, accept an unverified file path, or maintain another source of truth.
Main Flow¶
- The signed Twilio webhook passes its posted message fields directly to
convex/turns.ts#receiveInbound, which records durable inbound state. evaluateGatehandles hard boundaries before model reasoning.- Boundary outcomes commit immediately.
- Allowed turns enqueue
convex/mira.ts#runTurn. convex/context.ts#assembleForMirabuilds a small safe model context with the prior turn's stored goal and exchange plus up to three latest fresh ordinary memories.agent/piRunner.ts#runMiraAgentTurnruns Pi Agent Core with approved tools; one optional memory recall remains caregiver-scoped inside Convex, and one optional nearby-place result remains typed for deterministic rendering.- Pi must call the single-assignment
final_reason_result; one invalid result may receive one in-span repair, and only a validated result can terminate successfully. turns.commitAgentReasonResultverifies and commits. Semantic or contract failure recovers immediately; only a transient action or expired reasoning lease gets one retry before recovery and durable review.
Signup Flow¶
Web enrollment is a separate adapter, not an inbound turn or onboarding workflow:
POST /api/signupvalidates required phone, first name, email, and explicit SMS consent, a separate optional GiveCare-update opt-in, and bounded attribution.- Component-backed limits apply to hashed IP and phone keys.
- A supplied sponsor code must resolve to an active organization with capacity. Referral codes remain reported attribution.
- A supplied assessment handoff must be unused, unexpired, and bound to the normalized signup email.
- One Convex mutation creates the caregiver, active SMS channel, conversation, outbound message, consent/signup events, email contact, and
welcomeoutbox row; optional GiveCare subscription, organization count, and handoff redemption commit atomically with them. It schedules one transactional signup receipt after commit. - Existing phone enrollment is a no-op; it does not overwrite identity or reactivate stopped consent.
- Transport re-checks consent and safety before delivery and blocks stale welcome work after 15 minutes.
- The welcome asks who the caregiver supports. Its answer enters the normal SMS flow with that prior question as bounded context; a caregiver-stated relationship or situation is remembered before the required, assent-based GC-SDOH-6 baseline begins.
First-Party Web Adapter¶
The active web properties keep three narrow contracts: event capture, one named-publication opt-in, and transactional email delivery. Inputs are origin-restricted, rate-limited, allowlisted, and bounded. Transactional signup/assessment delivery is independent of marketing consent. Each publication unsubscribes independently; bounce and complaint suppress the contact globally. The server derives the native BSFC-s total and published interpretation from submitted answers; the browser cannot supply scoring or local subscales.
This surface is intentionally an adapter around current domain and Convex primitives. It does not add campaign workflows, generic admin actions, or client-authored assessment results. The /api/admin route exposes only an atomic authenticated read of the latest committed population-health snapshot consumed by gc-web; it never fans out over caregivers on request.
Boundary Paths¶
Boundary decisions are deterministic for:
- exact STOP/HELP transport-managed SMS responses
- pause/restart language
- safety-hold blocking
- obvious emergency/self-harm/poison/abuse signals
- identity-sensitive account requests when identity is unverified
The model is used only after the gate returns allow.
Safety is enforced at increasing-cost boundaries: deterministic Gate decisions,
typed and tool-bounded Reason output, reply/intent verification, and
effect-preflighted Commit. Offline gc-bench verification consumes transcripts
afterward and never substitutes for the live gate.
The code-coupled hazard and proof contract is in safety-contract.md.
Commit Rules¶
Commit re-checks channel state before creating an outbound outbox row. This protects against races where a caregiver opts out while Pi is reasoning.
Transport owns delivery state. The committing mutation schedules due outbox work immediately; the one-minute drain is recovery, not the normal delivery path, and fans out each due row as its own send action. Convex mutations atomically move an outbox row from queued to sending before Twilio is called; the action records provider acceptance, and later callbacks reconcile delivered/undelivered/failed status. If a signed callback races ahead of SID attachment, Convex stores it by provider SID and replays it transactionally when acceptance commits. The model never participates in delivery state.
Consent is send-class based. Welcome messages, ordinary replies, deterministic system replies, safety replies, and accepted follow-ups have explicit delivery rules, so enrollment or a same-turn reply does not become blanket follow-up permission. The pure delivery policy returns send, defer, or block; transport is the sole final evaluator and reuses sendAfter for caregiver-local follow-up windows.
Follow-up consent is observed, not model-inferred. Mira uses the explicit CHECK BACK affordance, and a schedule intent can commit only when the inbound turn clearly asks for follow-up — a CHECK BACK-class request or a plain yes to Mira's check-back offer — or prior follow-up permission already exists for the same revision-bound loop. When permission first becomes allowed after a structural reading, Commit reconciles that reading's unscheduled assessment cadence through the existing idempotent invitation path.
Reply and intent effects are preflighted together, and intents apply before the reply row is written. If a promised memory, schedule, assessment, or loop effect cannot commit at either stage, the effect-claiming reply is replaced with a bounded truthful fallback and the turn enters review.
Reasoning is admitted only after deterministic policy-gate paths. Per-caregiver/global windows bound model admission, while the Pi adapter independently caps provider tokens, each request's time, four provider requests, tool calls, and the total reasoning-span deadline. The final-result tool permits one in-span repair and never captures an invalid result. The single retry for a transient action or expired lease does not consume a second admission slot; semantic failures recover immediately. Repeated admission notices are suppressed so cost protection does not become SMS spam.
The caregiver loop is deliberately small: one active loop stores the larger objective, one current action, the expected observation, and a stop condition. Observed results update that same row through a pure transition policy; append-only events retain history. Parallel active loops, nested plans, and model-owned workflow state are not allowed.
A low GC-SDOH-6 domain derives a pending targeted refinement without adding workflow state. The first post-score turn is reserved for support; only a later natural pause exposes the four-question GC-SDOH-30 offer. MORE supplies assent, current needs still interrupt, and the refinement is never scheduled by cadence.
Current Production Gaps¶
The runtime is not yet a full SMS production service. Missing pieces include:
- deployed Twilio smoke tests against the exact component and account configuration
Operator notification uses one timestamp-and-creation cursor per source.
notifyOperatorWork runs every 15 minutes for open reviews, new safety holds,
and blocked loops. sendOperatorAlert advances those cursors only after at
least one configured channel accepts the alert (webhook 2xx or Resend enqueue).
A failed channel set therefore re-alerts instead of dropping work; channel
acceptance is not delivery confirmation.