diff --git a/README.md b/README.md index 71795e7..ff0e267 100644 --- a/README.md +++ b/README.md @@ -770,6 +770,7 @@ Repeated failure/recovery notifications are throttled by `notify_cooldown`. - `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, with prompts for output channel/peer routing. +See `docs/operations/OPERATOR_PACK.md` for an operations runbook and verification checklist. Example Operator Pack output routing: diff --git a/docs/README.md b/docs/README.md index d05fb8a..3bdeca8 100644 --- a/docs/README.md +++ b/docs/README.md @@ -19,6 +19,8 @@ This documentation is written to be useful to both humans and AI agents. If you 5. Production and performance - `docs/deployment/PRODUCTION.md` - `docs/performance/TUNING.md` +6. Operations runbooks + - `docs/operations/OPERATOR_PACK.md` ## Quick Map (One Diagram) diff --git a/docs/operations/OPERATOR_PACK.md b/docs/operations/OPERATOR_PACK.md new file mode 100644 index 0000000..3461542 --- /dev/null +++ b/docs/operations/OPERATOR_PACK.md @@ -0,0 +1,72 @@ +# Operator Pack Runbook + +This runbook documents the setup and operating model for Flynn's Operator Pack. + +## What It Configures + +When enabled in `flynn setup` Automation, Operator Pack preconfigures: + +- `backup.enabled: true` with cron schedule and output notifications. +- `automation.heartbeat.enabled: true` with output notifications. +- `automation.daily_briefing.enabled: true` with scheduled briefing output. +- `automation.minio_sync.enabled: true` with a default knowledge sync task (optional during setup). + +Setup prompts for: + +- Output routing: `channel` and `peer` (for backup + heartbeat + daily briefing + minio sync notifications). +- Backup cron schedule. +- Daily briefing cron schedule. +- Include/skip default MinIO sync task. + +## Recommended Baseline + +```yaml +backup: + enabled: true + schedule: "0 2 * * *" + notify: + channel: telegram + peer: "123456789" + +automation: + heartbeat: + enabled: true + interval: "5m" + notify: + channel: telegram + peer: "123456789" + notify_cooldown: "30m" + daily_briefing: + enabled: true + schedule: "0 8 * * *" + output: + channel: telegram + peer: "123456789" + minio_sync: + enabled: true + interval: "6h" + run_on_start: true + notify: + channel: telegram + peer: "123456789" + tasks: + - prefix: "knowledge/" + namespace_base: "global/knowledge/minio" + mode: append + max_objects: 20 + max_chars_per_object: 8000 + force: false +``` + +## Verification Checklist + +1. Run `flynn doctor --strict` and verify zero failures/warnings. +2. Confirm heartbeat route is valid for the configured channel/peer. +3. Confirm backup cron and daily briefing cron schedules match operator expectations. +4. If using MinIO ingestion, confirm extractor dependencies via doctor output (`MinIO ingest extractors`). + +## Notes + +- Heartbeat notification noise is controlled by `automation.heartbeat.notify_cooldown` (default `30m`). +- If `notify_cooldown` is invalid, Flynn falls back to `30m` and logs a warning. +- Re-running setup Automation detects an existing Operator Pack and asks whether to reconfigure. diff --git a/docs/plans/state.json b/docs/plans/state.json index b9a6d60..2fd3b35 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 operator-pack prompts for output channel/peer routing with redundant prompt skip when already configured; added heartbeat notification throttling via `automation.heartbeat.notify_cooldown` (with invalid-value fallback handling); and added `flynn doctor --strict` to treat warnings as failures. Added docs/changelog updates and a consolidated regression run record.", + "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 with redundant prompt skip when already configured; added a compact Operator Pack status line in setup menu output; added heartbeat notification throttling via `automation.heartbeat.notify_cooldown` (with invalid-value fallback handling); and added `flynn doctor --strict` to treat warnings as failures. Added docs/changelog updates, including a dedicated Operator Pack operations runbook, and a consolidated regression run record.", "files_modified": [ "CHANGELOG.md", "src/cli/setup/config.ts", @@ -213,9 +213,11 @@ "src/cli/doctor.test.ts", "config/default.yaml", "README.md", + "docs/README.md", + "docs/operations/OPERATOR_PACK.md", "docs/plans/state.json" ], - "test_status": "pnpm test:run src/cli/index.test.ts src/cli/setup/automation.test.ts src/cli/setup/integration.test.ts src/cli/setup/summary.test.ts src/automation/heartbeat.test.ts src/config/schema.test.ts src/cli/doctor.test.ts + pnpm typecheck passing" + "test_status": "pnpm test:run src/cli/setup/summary.test.ts src/cli/setup/orchestrator.test.ts src/cli/setup/automation.test.ts src/cli/setup/integration.test.ts + pnpm typecheck passing" }, "backup-session-summary-audit-trail": { "status": "completed", @@ -3501,7 +3503,7 @@ } }, "overall_progress": { - "total_test_count": 1872, + "total_test_count": 1874, "all_tests_passing": true, "p0_completion": "3/3 (100%)", "p1_completion": "4/4 (100%)", diff --git a/src/cli/setup/orchestrator.ts b/src/cli/setup/orchestrator.ts index 61183d9..a715780 100644 --- a/src/cli/setup/orchestrator.ts +++ b/src/cli/setup/orchestrator.ts @@ -1,6 +1,6 @@ import type { Prompter } from './prompts.js'; import type { ConfigBuilder } from './config.js'; -import { renderSummary } from './summary.js'; +import { renderOperatorPackStatus, renderSummary } from './summary.js'; import { setupProviders } from './providers.js'; import { setupChannels } from './channels.js'; import { setupMemory } from './memory.js'; @@ -28,9 +28,11 @@ const SECTION_HANDLERS: Record Promis export async function runMenu(p: Prompter, builder: ConfigBuilder): Promise { while (true) { + const config = builder.build(); p.println(); p.println('Flynn Setup — Current Configuration'); - p.println(renderSummary(builder.build())); + p.println(renderSummary(config)); + p.println(` ${renderOperatorPackStatus(config)}`); p.println(); p.println('What would you like to configure?'); for (let i = 0; i < MENU_OPTIONS.length; i++) { diff --git a/src/cli/setup/summary.test.ts b/src/cli/setup/summary.test.ts index c678e1d..2da1bf5 100644 --- a/src/cli/setup/summary.test.ts +++ b/src/cli/setup/summary.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect } from 'vitest'; -import { renderSummary } from './summary.js'; +import { renderOperatorPackStatus, renderSummary } from './summary.js'; import type { SetupConfig } from './config.js'; describe('renderSummary', () => { @@ -24,4 +24,43 @@ describe('renderSummary', () => { expect(output).toContain('Automation: heartbeat, daily-briefing, minio-sync'); expect(output).toContain('Backup: enabled (cron 0 2 * * *)'); }); + + it('renders compact operator pack status line with routing when enabled', () => { + const config = { + models: { + default: { provider: 'anthropic', model: 'claude-sonnet' }, + }, + server: { port: 18800, localhost: true }, + automation: { + heartbeat: { + enabled: true, + notify: { channel: 'telegram', peer: '123' }, + }, + daily_briefing: { enabled: true }, + }, + backup: { + enabled: true, + notify: { channel: 'telegram', peer: '123' }, + }, + } as unknown as SetupConfig; + + expect(renderOperatorPackStatus(config)).toBe('Operator Pack: enabled (telegram/123)'); + }); + + it('renders compact operator pack status line as disabled when partial config', () => { + const config = { + models: { + default: { provider: 'anthropic', model: 'claude-sonnet' }, + }, + server: { port: 18800, localhost: true }, + automation: { + heartbeat: { enabled: true }, + }, + backup: { + enabled: true, + }, + } as unknown as SetupConfig; + + expect(renderOperatorPackStatus(config)).toBe('Operator Pack: disabled'); + }); }); diff --git a/src/cli/setup/summary.ts b/src/cli/setup/summary.ts index 5b2ef19..8713f5a 100644 --- a/src/cli/setup/summary.ts +++ b/src/cli/setup/summary.ts @@ -1,5 +1,26 @@ import type { SetupConfig } from './config.js'; +export function renderOperatorPackStatus(config: SetupConfig): string { + const automation = config.automation ?? {}; + const backup = config.backup; + const enabled = Boolean( + backup?.enabled + && automation.heartbeat?.enabled + && automation.daily_briefing?.enabled, + ); + + if (!enabled) { + return 'Operator Pack: disabled'; + } + + const notify = backup?.notify ?? (automation.heartbeat as { notify?: { channel?: string; peer?: string } } | undefined)?.notify; + if (notify?.channel && notify?.peer) { + return `Operator Pack: enabled (${notify.channel}/${notify.peer})`; + } + + return 'Operator Pack: enabled'; +} + export function renderSummary(config: SetupConfig): string { const lines: string[] = [];