feat(setup): surface operator-pack status and add operations runbook
This commit is contained in:
@@ -770,6 +770,7 @@ Repeated failure/recovery notifications are throttled by `notify_cooldown`.
|
|||||||
- `automation.minio_sync.notify.channel: webchat`
|
- `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.
|
`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:
|
Example Operator Pack output routing:
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ This documentation is written to be useful to both humans and AI agents. If you
|
|||||||
5. Production and performance
|
5. Production and performance
|
||||||
- `docs/deployment/PRODUCTION.md`
|
- `docs/deployment/PRODUCTION.md`
|
||||||
- `docs/performance/TUNING.md`
|
- `docs/performance/TUNING.md`
|
||||||
|
6. Operations runbooks
|
||||||
|
- `docs/operations/OPERATOR_PACK.md`
|
||||||
|
|
||||||
## Quick Map (One Diagram)
|
## Quick Map (One Diagram)
|
||||||
|
|
||||||
|
|||||||
@@ -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.
|
||||||
@@ -193,7 +193,7 @@
|
|||||||
"status": "completed",
|
"status": "completed",
|
||||||
"date": "2026-02-16",
|
"date": "2026-02-16",
|
||||||
"updated": "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": [
|
"files_modified": [
|
||||||
"CHANGELOG.md",
|
"CHANGELOG.md",
|
||||||
"src/cli/setup/config.ts",
|
"src/cli/setup/config.ts",
|
||||||
@@ -213,9 +213,11 @@
|
|||||||
"src/cli/doctor.test.ts",
|
"src/cli/doctor.test.ts",
|
||||||
"config/default.yaml",
|
"config/default.yaml",
|
||||||
"README.md",
|
"README.md",
|
||||||
|
"docs/README.md",
|
||||||
|
"docs/operations/OPERATOR_PACK.md",
|
||||||
"docs/plans/state.json"
|
"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": {
|
"backup-session-summary-audit-trail": {
|
||||||
"status": "completed",
|
"status": "completed",
|
||||||
@@ -3501,7 +3503,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"overall_progress": {
|
"overall_progress": {
|
||||||
"total_test_count": 1872,
|
"total_test_count": 1874,
|
||||||
"all_tests_passing": true,
|
"all_tests_passing": true,
|
||||||
"p0_completion": "3/3 (100%)",
|
"p0_completion": "3/3 (100%)",
|
||||||
"p1_completion": "4/4 (100%)",
|
"p1_completion": "4/4 (100%)",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { Prompter } from './prompts.js';
|
import type { Prompter } from './prompts.js';
|
||||||
import type { ConfigBuilder } from './config.js';
|
import type { ConfigBuilder } from './config.js';
|
||||||
import { renderSummary } from './summary.js';
|
import { renderOperatorPackStatus, renderSummary } from './summary.js';
|
||||||
import { setupProviders } from './providers.js';
|
import { setupProviders } from './providers.js';
|
||||||
import { setupChannels } from './channels.js';
|
import { setupChannels } from './channels.js';
|
||||||
import { setupMemory } from './memory.js';
|
import { setupMemory } from './memory.js';
|
||||||
@@ -28,9 +28,11 @@ const SECTION_HANDLERS: Record<string, (p: Prompter, b: ConfigBuilder) => Promis
|
|||||||
|
|
||||||
export async function runMenu(p: Prompter, builder: ConfigBuilder): Promise<void> {
|
export async function runMenu(p: Prompter, builder: ConfigBuilder): Promise<void> {
|
||||||
while (true) {
|
while (true) {
|
||||||
|
const config = builder.build();
|
||||||
p.println();
|
p.println();
|
||||||
p.println('Flynn Setup — Current Configuration');
|
p.println('Flynn Setup — Current Configuration');
|
||||||
p.println(renderSummary(builder.build()));
|
p.println(renderSummary(config));
|
||||||
|
p.println(` ${renderOperatorPackStatus(config)}`);
|
||||||
p.println();
|
p.println();
|
||||||
p.println('What would you like to configure?');
|
p.println('What would you like to configure?');
|
||||||
for (let i = 0; i < MENU_OPTIONS.length; i++) {
|
for (let i = 0; i < MENU_OPTIONS.length; i++) {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { describe, it, expect } from 'vitest';
|
import { describe, it, expect } from 'vitest';
|
||||||
import { renderSummary } from './summary.js';
|
import { renderOperatorPackStatus, renderSummary } from './summary.js';
|
||||||
import type { SetupConfig } from './config.js';
|
import type { SetupConfig } from './config.js';
|
||||||
|
|
||||||
describe('renderSummary', () => {
|
describe('renderSummary', () => {
|
||||||
@@ -24,4 +24,43 @@ describe('renderSummary', () => {
|
|||||||
expect(output).toContain('Automation: heartbeat, daily-briefing, minio-sync');
|
expect(output).toContain('Automation: heartbeat, daily-briefing, minio-sync');
|
||||||
expect(output).toContain('Backup: enabled (cron 0 2 * * *)');
|
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');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,5 +1,26 @@
|
|||||||
import type { SetupConfig } from './config.js';
|
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 {
|
export function renderSummary(config: SetupConfig): string {
|
||||||
const lines: string[] = [];
|
const lines: string[] = [];
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user