Propose a shared substrate for per-user prefs, contexts, per-key consents, and per-agent state so adding an agent stays a manifest change. Updates CLAUDE.md, README, and architecture docs to reflect the multi-agent pipeline (ADR-0013) and the registry direction. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
112 lines
4.2 KiB
Markdown
112 lines
4.2 KiB
Markdown
# Data model
|
|
|
|
Durable entities across modules. Per-module databases/schemas own these; cross-module access is only via the module's API.
|
|
|
|
## Core entities
|
|
|
|
```
|
|
User auth + profile
|
|
id (uuid)
|
|
created_at
|
|
email (from IdP)
|
|
preferred_name?
|
|
deleted_at? soft-delete for 30-day recovery; hard-delete after
|
|
|
|
IdentityLink auth
|
|
user_id
|
|
provider "google" | "apple"
|
|
provider_sub subject from IdP
|
|
created_at
|
|
|
|
Session auth
|
|
user_id
|
|
sid (uuid) in JWT
|
|
issued_at
|
|
expires_at
|
|
revoked_at?
|
|
|
|
User (extended) profile ADR-0014
|
|
+ tone 'direct' | 'gentle' | 'motivational'
|
|
+ tip_kinds_json jsonb: allowed tip kinds (stable globals)
|
|
|
|
UserPreference profile ADR-0014
|
|
user_id, scope, key (pk)
|
|
scope 'orchestrator' | 'agent:<id>'
|
|
value_json open-ended; agent validates against its pref_schema on read
|
|
source 'user' | 'inferred' (inferred never overwrites user)
|
|
updated_at
|
|
|
|
UserConsent profile ADR-0014
|
|
user_id, consent_key (pk)
|
|
consent_key 'data:todoist' | 'data:calendar' | 'agent:focus-area' | ...
|
|
granted_at
|
|
revoked_at? null = currently active
|
|
|
|
UserContext profile ADR-0014
|
|
user_id, name (pk) 'work' | 'home' | 'vacation' | user-named
|
|
active manual toggle in M2; auto-inference per agent in #112-#116
|
|
schedule_json? optional: when this context is active
|
|
created_at
|
|
|
|
AgentOutput recommender ADR-0013
|
|
id (pk)
|
|
user_id
|
|
agent_id e.g. 'overdue-task' (matches a manifest)
|
|
prompt_text snippet for the orchestrator prompt
|
|
signals_snapshot jsonb: inputs the agent consumed
|
|
computed_at, expires_at computed_at + manifest.ttl_sec
|
|
agent_version bump to invalidate cached outputs on logic changes
|
|
|
|
Credential integrations
|
|
user_id
|
|
provider "todoist" | "google_calendar" | ...
|
|
ciphertext sealed-box over {access, refresh, scopes, expires_at}
|
|
meta provider-specific (sync_token cursor for Todoist)
|
|
created_at
|
|
last_refreshed_at
|
|
revoked_at?
|
|
|
|
Event events
|
|
event_id (ulid)
|
|
user_id
|
|
schema_version
|
|
kind e.g. "signals.task.updated"
|
|
occurred_at
|
|
ingested_at
|
|
payload protobuf bytes
|
|
|
|
TipInstance recommender
|
|
tip_id (ulid)
|
|
user_id
|
|
policy_name "v4-orchestrator" (ADR-0013) | legacy bandit names retained for history
|
|
policy_version
|
|
candidate_source "todoist" | "advice.library" | "agent-orchestrator" | ...
|
|
context_snapshot jsonb: features + agent snippets seen at decision time
|
|
tip jsonb: {kind,title,body,source,deep_link,meta}
|
|
created_at
|
|
shown_at? set when the client reports render
|
|
reaction? "done" | "snooze" | "dismiss" | null
|
|
reacted_at?
|
|
delivery_id? fk if surfaced via notifier push
|
|
|
|
Delivery notifier
|
|
delivery_id
|
|
user_id
|
|
tip_id
|
|
channel "webpush" | "apns" | "fcm" | "email"
|
|
dispatched_at
|
|
delivered_at?
|
|
failure_reason?
|
|
```
|
|
|
|
## Foreign-key discipline
|
|
|
|
There are no cross-module FKs. Each module owns its tables. References by id are soft; consistency is maintained by events (user-deleted → every module cascades its own cleanup).
|
|
|
|
## Deletion
|
|
|
|
`User.deleted_at` set → a `user.deletion_requested` event goes out → each module soft-deletes its rows → after 30 days a scheduled job hard-deletes. Credentials are **revoked at the provider** (not just erased locally) on soft-delete. See `privacy.md`.
|
|
|
|
## Replay and reproducibility
|
|
|
|
`TipInstance.context_snapshot` captures the exact features that produced the decision. This is what lets offline replay re-score historical tips against a new policy without touching the feature store. |