diff --git a/README.md b/README.md index 19e2fed..5edb5ca 100644 --- a/README.md +++ b/README.md @@ -769,7 +769,7 @@ Repeated failure/recovery notifications are throttled by `notify_cooldown`. - `automation.minio_sync.interval: "6h"` - `automation.minio_sync.notify.channel: webchat` -`flynn setup` now includes an Operator Pack option in Automation that preconfigures scheduled backups, heartbeat alerts, a daily briefing, and a default MinIO sync task. +`flynn setup` now includes an Operator Pack option in Automation that preconfigures scheduled backups, heartbeat alerts, a daily briefing, and a default MinIO sync task, with prompts for output channel/peer routing. ## Gmail Pub/Sub Watcher diff --git a/docs/plans/state.json b/docs/plans/state.json index 56e3dca..ce9c2ec 100644 --- a/docs/plans/state.json +++ b/docs/plans/state.json @@ -193,7 +193,7 @@ "status": "completed", "date": "2026-02-16", "updated": "2026-02-16", - "summary": "Implemented operator-focused hardening and onboarding polish: added setup Operator Pack flows in both Automation menu and first-run wizard to preconfigure scheduled backups, heartbeat alerts, daily briefing, and default MinIO sync; added heartbeat notification throttling via `automation.heartbeat.notify_cooldown`; and added `flynn doctor --strict` to treat warnings as failures. Updated docs/default config examples and onboarding tests accordingly.", + "summary": "Implemented operator-focused hardening and onboarding polish: added setup Operator Pack flows in both Automation menu and first-run wizard to preconfigure scheduled backups, heartbeat alerts, daily briefing, and default MinIO sync; added operator-pack prompts for output channel/peer routing; added heartbeat notification throttling via `automation.heartbeat.notify_cooldown`; and added `flynn doctor --strict` to treat warnings as failures. Updated docs/default config examples and onboarding tests accordingly.", "files_modified": [ "src/cli/setup/config.ts", "src/cli/setup/config.test.ts", @@ -3499,7 +3499,7 @@ } }, "overall_progress": { - "total_test_count": 1868, + "total_test_count": 1869, "all_tests_passing": true, "p0_completion": "3/3 (100%)", "p1_completion": "4/4 (100%)", diff --git a/src/cli/setup/automation.test.ts b/src/cli/setup/automation.test.ts index 6c8b803..86998e9 100644 --- a/src/cli/setup/automation.test.ts +++ b/src/cli/setup/automation.test.ts @@ -43,6 +43,8 @@ describe('setupAutomation', () => { false, // configure google services ], asks: [ + 'telegram', // output channel + '987654321', // output peer '0 3 * * *', // backup schedule '0 7 * * 1-5', // daily briefing schedule ], @@ -61,11 +63,47 @@ describe('setupAutomation', () => { expect(backup.schedule).toBe('0 3 * * *'); expect(heartbeat.enabled).toBe(true); expect(heartbeat.notify_cooldown).toBe('30m'); + expect((heartbeat.notify as Record).channel).toBe('telegram'); + expect((heartbeat.notify as Record).peer).toBe('987654321'); expect(briefing.enabled).toBe(true); expect(briefing.schedule).toBe('0 7 * * 1-5'); expect(minioSync.enabled).toBe(true); }); + it('allows overriding operator pack output routing', async () => { + const builder = new ConfigBuilder(); + + const p = makePrompter({ + confirms: [ + true, // enable operator pack + false, // include minio sync + false, // enable cron scheduler + false, // enable webhook receiver + false, // configure google services + ], + asks: [ + 'discord', // output channel + 'ops-room', // output peer + '0 4 * * *', // backup schedule + '0 9 * * *', // daily briefing schedule + ], + }); + + await setupAutomation(p, builder); + + const config = builder.build() as Record; + const backup = config.backup as Record; + const automation = config.automation as Record; + const heartbeat = automation.heartbeat as Record; + const briefing = automation.daily_briefing as Record; + + expect((backup.notify as Record).channel).toBe('discord'); + expect((backup.notify as Record).peer).toBe('ops-room'); + expect((heartbeat.notify as Record).channel).toBe('discord'); + expect((briefing.output as Record).peer).toBe('ops-room'); + expect(automation.minio_sync).toBeUndefined(); + }); + it('leaves operator pack disabled when not selected', async () => { const builder = new ConfigBuilder(); diff --git a/src/cli/setup/automation.ts b/src/cli/setup/automation.ts index b20838f..bca3cfc 100644 --- a/src/cli/setup/automation.ts +++ b/src/cli/setup/automation.ts @@ -66,19 +66,21 @@ export async function setupAutomation(p: Prompter, builder: ConfigBuilder): Prom const telegramPeer = config.telegram?.allowed_chat_ids?.[0]; const defaultOutputChannel = telegramPeer ? 'telegram' : 'webchat'; const defaultOutputPeer = telegramPeer ? String(telegramPeer) : 'operator'; + const outputChannel = await p.ask('Operator notifications output channel', defaultOutputChannel); + const outputPeer = await p.ask('Operator notifications peer/chat ID', defaultOutputPeer); const backupSchedule = await p.ask('Backup cron schedule', '0 2 * * *'); const dailyBriefingSchedule = await p.ask('Daily briefing cron schedule', '0 8 * * *'); const enableMinioSync = await p.confirm('Include default MinIO sync task?', true); builder.applyOperatorPack({ - outputChannel: defaultOutputChannel, - outputPeer: defaultOutputPeer, + outputChannel, + outputPeer, backupSchedule, dailyBriefingSchedule, enableMinioSync, }); - p.println(`✓ Operator pack enabled (alerts routed to ${defaultOutputChannel}/${defaultOutputPeer})`); + p.println(`✓ Operator pack enabled (alerts routed to ${outputChannel}/${outputPeer})`); } const cron = await p.confirm('Enable cron scheduler?', false); diff --git a/src/cli/setup/integration.test.ts b/src/cli/setup/integration.test.ts index 2ec8c45..e44f104 100644 --- a/src/cli/setup/integration.test.ts +++ b/src/cli/setup/integration.test.ts @@ -80,6 +80,8 @@ describe('first-run wizard integration', () => { 'n', // confirm: Add another channel? (no) 'y', // confirm: Configure automation now? (yes) 'y', // confirm: Enable operator automation pack? (yes) + '', // ask: Operator notifications output channel (default) + '', // ask: Operator notifications peer/chat ID (default) '', // ask: Backup cron schedule (default) '', // ask: Daily briefing cron schedule (default) '', // confirm: Include default MinIO sync task? (default yes)