feat(consents): auto-grant data:<provider> on connect; remove agent: consents (ADR-0015)
- 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>
This commit is contained in:
@@ -85,3 +85,45 @@ describe('runMigrations — idempotency', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('runMigrations — issue #127 backfill', () => {
|
||||
it('grants data:<provider> consent for existing active integration tokens', () => {
|
||||
const sqlite = freshDb();
|
||||
runMigrations(sqlite);
|
||||
|
||||
// Seed a user + active Todoist token (simulates pre-#127 state)
|
||||
sqlite.exec(`
|
||||
INSERT INTO users (id, email, role, created_at) VALUES ('u2', 'u2@test.com', 'user', '2026-01-01T00:00:00Z');
|
||||
INSERT INTO user_consents (user_id, consent_key, granted_at) VALUES ('u2', 'data:core', '2026-01-01T00:00:00Z');
|
||||
INSERT INTO integration_tokens (id, user_id, provider, access_token, token_status, connected_at)
|
||||
VALUES ('tok1', 'u2', 'todoist', 'secret', 'active', '2026-01-02T00:00:00Z');
|
||||
`);
|
||||
|
||||
// Re-run migrations — the backfill should insert data:todoist
|
||||
runMigrations(sqlite);
|
||||
|
||||
const rows = sqlite
|
||||
.prepare(`SELECT consent_key FROM user_consents WHERE user_id = 'u2' ORDER BY consent_key`)
|
||||
.all() as { consent_key: string }[];
|
||||
expect(rows.map((r) => r.consent_key)).toEqual(['data:core', 'data:todoist']);
|
||||
});
|
||||
|
||||
it('is idempotent — running twice does not duplicate consent rows', () => {
|
||||
const sqlite = freshDb();
|
||||
runMigrations(sqlite);
|
||||
|
||||
sqlite.exec(`
|
||||
INSERT INTO users (id, email, role, created_at) VALUES ('u3', 'u3@test.com', 'user', '2026-01-01T00:00:00Z');
|
||||
INSERT INTO integration_tokens (id, user_id, provider, access_token, token_status, connected_at)
|
||||
VALUES ('tok2', 'u3', 'todoist', 'secret', 'active', '2026-01-02T00:00:00Z');
|
||||
`);
|
||||
|
||||
runMigrations(sqlite);
|
||||
runMigrations(sqlite);
|
||||
|
||||
const count = (sqlite
|
||||
.prepare(`SELECT COUNT(*) as n FROM user_consents WHERE user_id = 'u3' AND consent_key = 'data:todoist'`)
|
||||
.get() as { n: number }).n;
|
||||
expect(count).toBe(1);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user