feat(setup): skip redundant operator-pack prompts and add strict-mode docs/tests

This commit is contained in:
William Valentin
2026-02-16 15:27:27 -08:00
parent 7ce58f5966
commit dc1c691ea5
6 changed files with 103 additions and 8 deletions
+13
View File
@@ -6,6 +6,16 @@ All notable changes to Flynn are documented in this file.
### Added ### Added
- **Operator Setup Pack + Strict Doctor Mode** -- `flynn setup` Automation now
supports an Operator Pack that preconfigures backup scheduling/notifications,
heartbeat alerts, daily briefing, and MinIO sync with configurable output
channel/peer routing. `flynn doctor --strict` now treats warnings as failures
for CI and preflight gating workflows.
- **Heartbeat Notification Cooldown Config** -- `automation.heartbeat.notify_cooldown`
adds throttling for repeated failure/recovery alerts (default `30m`) to reduce
noisy notifications during unstable periods.
- **Native Audio Support** -- Smart routing for voice messages: audio-capable models - **Native Audio Support** -- Smart routing for voice messages: audio-capable models
(Gemini, OpenAI, GitHub) receive raw audio directly via `AudioSource` content parts; (Gemini, OpenAI, GitHub) receive raw audio directly via `AudioSource` content parts;
non-audio models (Anthropic, Bedrock, Ollama, llama.cpp) get Whisper transcription non-audio models (Anthropic, Bedrock, Ollama, llama.cpp) get Whisper transcription
@@ -130,6 +140,9 @@ All notable changes to Flynn are documented in this file.
### Fixed ### Fixed
- **Heartbeat Cooldown Parse Resilience** -- Invalid `automation.heartbeat.notify_cooldown`
values no longer throw during alerting; monitor falls back to `30m` and logs a warning.
- **Voice Message Failure Handling** -- Telegram voice/audio messages now send user feedback on - **Voice Message Failure Handling** -- Telegram voice/audio messages now send user feedback on
download failures instead of silently dropping. When audio transcription is not configured for download failures instead of silently dropping. When audio transcription is not configured for
non-audio-capable models, a graceful error message is sent to the user instead of non-audio-capable models, a graceful error message is sent to the user instead of
+29
View File
@@ -771,6 +771,35 @@ Repeated failure/recovery notifications are throttled by `notify_cooldown`.
`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.
Example Operator Pack output routing:
```yaml
backup:
enabled: true
schedule: "0 2 * * *"
notify:
channel: telegram
peer: "123456789"
automation:
heartbeat:
enabled: true
notify:
channel: telegram
peer: "123456789"
notify_cooldown: "30m"
daily_briefing:
enabled: true
output:
channel: telegram
peer: "123456789"
minio_sync:
enabled: true
notify:
channel: telegram
peer: "123456789"
```
## Gmail Pub/Sub Watcher ## Gmail Pub/Sub Watcher
Monitor a Gmail inbox and forward new messages into the agent pipeline. Monitor a Gmail inbox and forward new messages into the agent pipeline.
+5 -3
View File
@@ -193,8 +193,9 @@
"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; added heartbeat notification throttling via `automation.heartbeat.notify_cooldown` (with invalid-value fallback handling); 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 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.",
"files_modified": [ "files_modified": [
"CHANGELOG.md",
"src/cli/setup/config.ts", "src/cli/setup/config.ts",
"src/cli/setup/config.test.ts", "src/cli/setup/config.test.ts",
"src/cli/setup/automation.test.ts", "src/cli/setup/automation.test.ts",
@@ -203,6 +204,7 @@
"src/cli/setup/integration.test.ts", "src/cli/setup/integration.test.ts",
"src/cli/setup/summary.ts", "src/cli/setup/summary.ts",
"src/cli/setup/summary.test.ts", "src/cli/setup/summary.test.ts",
"src/cli/index.test.ts",
"src/automation/heartbeat.ts", "src/automation/heartbeat.ts",
"src/automation/heartbeat.test.ts", "src/automation/heartbeat.test.ts",
"src/config/schema.ts", "src/config/schema.ts",
@@ -213,7 +215,7 @@
"README.md", "README.md",
"docs/plans/state.json" "docs/plans/state.json"
], ],
"test_status": "pnpm test:run src/cli/setup/summary.test.ts src/cli/setup/integration.test.ts src/cli/setup/automation.test.ts src/cli/setup/config.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/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"
}, },
"backup-session-summary-audit-trail": { "backup-session-summary-audit-trail": {
"status": "completed", "status": "completed",
@@ -3499,7 +3501,7 @@
} }
}, },
"overall_progress": { "overall_progress": {
"total_test_count": 1870, "total_test_count": 1872,
"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%)",
+7
View File
@@ -21,6 +21,13 @@ describe('CLI program', () => {
expect(commandNames).toContain('zai-auth'); expect(commandNames).toContain('zai-auth');
}); });
it('registers doctor strict flag on doctor command', () => {
const program = createProgram();
const doctor = program.commands.find((c) => c.name() === 'doctor');
const strictOption = doctor?.options.find((o) => o.long === '--strict');
expect(strictOption).toBeDefined();
});
it('has version info', () => { it('has version info', () => {
const program = createProgram(); const program = createProgram();
expect(program.version()).toBeDefined(); expect(program.version()).toBeDefined();
+30
View File
@@ -127,4 +127,34 @@ describe('setupAutomation', () => {
expect(automation?.heartbeat).toBeUndefined(); expect(automation?.heartbeat).toBeUndefined();
expect(automation?.minio_sync).toBeUndefined(); expect(automation?.minio_sync).toBeUndefined();
}); });
it('skips operator-pack reconfiguration prompts when already configured and not reconfigured', async () => {
const builder = new ConfigBuilder();
builder.applyOperatorPack({
outputChannel: 'telegram',
outputPeer: '123',
backupSchedule: '0 2 * * *',
dailyBriefingSchedule: '0 8 * * *',
enableMinioSync: true,
});
const p = makePrompter({
confirms: [
false, // reconfigure operator pack?
false, // enable cron scheduler
false, // enable webhook receiver
false, // configure google services
],
});
await setupAutomation(p, builder);
const config = builder.build() as Record<string, unknown>;
const backup = config.backup as Record<string, unknown>;
const automation = config.automation as Record<string, unknown>;
const briefing = automation.daily_briefing as Record<string, unknown>;
expect(backup.schedule).toBe('0 2 * * *');
expect(briefing.schedule).toBe('0 8 * * *');
});
}); });
+17 -3
View File
@@ -57,12 +57,26 @@ const GOOGLE_SERVICES: GoogleService[] = [
]; ];
export async function setupAutomation(p: Prompter, builder: ConfigBuilder): Promise<void> { export async function setupAutomation(p: Prompter, builder: ConfigBuilder): Promise<void> {
const enableOperatorPack = await p.confirm( const config = builder.build();
const automation = (config.automation ?? {}) as Record<string, unknown>;
const backup = (config.backup ?? {}) as Record<string, unknown>;
const operatorPackConfigured = Boolean(
backup.enabled === true
&& (automation.heartbeat as { enabled?: boolean } | undefined)?.enabled
&& (automation.daily_briefing as { enabled?: boolean } | undefined)?.enabled,
);
const configureOperatorPack = operatorPackConfigured
? await p.confirm(
'Operator automation pack appears enabled. Reconfigure operator pack settings now?',
false,
)
: await p.confirm(
'Enable operator automation pack (scheduled backups + heartbeat alerts + daily briefing + MinIO sync)?', 'Enable operator automation pack (scheduled backups + heartbeat alerts + daily briefing + MinIO sync)?',
false, false,
); );
if (enableOperatorPack) {
const config = builder.build(); if (configureOperatorPack) {
const telegramPeer = config.telegram?.allowed_chat_ids?.[0]; const telegramPeer = config.telegram?.allowed_chat_ids?.[0];
const defaultOutputChannel = telegramPeer ? 'telegram' : 'webchat'; const defaultOutputChannel = telegramPeer ? 'telegram' : 'webchat';
const defaultOutputPeer = telegramPeer ? String(telegramPeer) : 'operator'; const defaultOutputPeer = telegramPeer ? String(telegramPeer) : 'operator';