- integrations.ts: grant data:<provider> on OAuth callback, revoke on disconnect - Backfill migration: INSERT OR IGNORE data:<provider> for all active tokens - Agent manifests: drop agent:<id> from required_consents (momentum, time-of-day, overdue-task, recent-patterns, health-vitals) — per-agent control is a preference - eligibility.ts: update comment to reflect data:-only consent model - test_manifest.py: assert no agent: consents remain in any manifest - migrations.test.ts: backfill idempotency tests for issue #127 - Dockerfile.api: drop --offline flag (fixes ERR_PNPM_NO_OFFLINE_META) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2.1 KiB
2.1 KiB
ADR-0015 — Data-source consents only; drop per-agent consent gate
Date: 2026-05-11
Status: Accepted
Supersedes: ADR-0014 §3 (consent model)
Context
ADR-0014 introduced required_consents on agent manifests. In practice two
unrelated concepts were mixed into that field:
data:<source>— which data source the agent reads.agent:<id>— whether the user opted into this specific agent.
No UI ever granted agent:<id> consents, so the eligibility filter at
services/api/src/profile/eligibility.ts dropped every agent for every real
user. The symptom was confirmed by MLflow trace
tr-591449ea8a72af8e81b6a585234a86ab: user ODGp4Gkr7JWemMsqcMLMn had five
fresh agent_outputs rows but the orchestrator received agent_ids: [].
Decision
Collapse to a single consent dimension: data source.
required_consentsentries must all start withdata:. Agent manifests no longer listagent:<id>entries.- Connecting a data source via the OAuth flow automatically grants
data:<provider>inuser_consents. Disconnecting setsrevoked_at. data:corecontinues to be auto-granted on signup.- Per-agent control becomes a preference (
user_preferences[scope='agent:<id>', key='enabled']), not a consent. The eligibility filter already honours this — the only change is removing theagent:*consent check that was always failing. - Eligibility rule (final): an agent is eligible iff every
data:*it declares is granted and not revoked, no active context is insilenced_in_contexts, and theenabledpreference is notfalse.
Consequences
- Agents that only require
data:core(time-of-day, momentum, recent-patterns) become eligible immediately after signup. - Agents requiring
data:todoistordata:google-healthbecome eligible as soon as the user connects the integration — no extra consent step. - A backfill migration grants
data:<provider>for every existing activeintegration_tokensrow, unblocking users who connected before this change. ml/agents/tests/test_manifest.pyasserts allrequired_consentsstart withdata:, preventing regression.