enhance(#4570): allow disabling planner stall detection (#4585)

* enhance(#4570): allow disabling planner stall detection

* docs(#4570): add changelog fragment

Emitted-Drift-Ack-Growth: plan-phase.md — the explicit opt-out gate covers all five planner and checker spawn classes
Emitted-Drift-Ack-Growth: settings-advanced.md — the toggle prompt and bounded-recovery warning expose the new setting

* chore(#4570): refresh compact-content baseline

* fix(#4570): preserve default-on watchdog fallback

* docs(#4570): qualify the chunked-mode orchestrator rules with the toggle

The two chunked-planning-mode stall-watch imperatives read as
unconditional, with the opt-out stated only in a following bullet. State
the PLANNER_STALL_DETECTION_ENABLED condition inline, matching the three
sites already qualified in plan-phase.md.

* chore(#4570): refresh compact-content baselines against rebased next

* fix(#4570): sync planner stall launcher

Keep the stall-detection helper aligned with the canonical runtime launcher.

* chore(#4570): refresh compact baseline after rebase

---------

Co-authored-by: Tom Boucher <trekkie@nomorestars.com>
This commit is contained in:
Michel Moreira
2026-09-23 19:46:23 -03:00
committed by GitHub
parent 17bf25a3d2
commit 1e3e1f7cd8
21 changed files with 417 additions and 53 deletions

View File

@@ -17,7 +17,7 @@ import cliExitMod = require('./cli-exit.cjs');
const { ExitError } = cliExitMod;
// eslint-disable-next-line @typescript-eslint/no-require-imports
import configLoader = require('./config-loader.cjs');
const { CONFIG_DEFAULTS } = configLoader;
const { CONFIG_DEFAULTS, resolvePlannerStallDetectionEnabled } = configLoader;
import { platformWriteSync, platformEnsureDir } from './shell-command-projection.cjs';
// eslint-disable-next-line @typescript-eslint/no-require-imports
import planningWorkspace = require('./planning-workspace.cjs');
@@ -107,6 +107,7 @@ const SCHEMA_DEFAULTS: Record<string, unknown> = {
'context_window': 200000,
'executor.stall_detect_interval_minutes': 5,
'executor.stall_threshold_minutes': 10,
'planner.stall_detection_enabled': CONFIG_DEFAULTS.planner_stall_detection_enabled,
'planner.stall_detect_interval_minutes': 5,
'planner.stall_threshold_minutes': 10,
'git.create_tag': true,
@@ -185,12 +186,15 @@ function resolveSchemaDefault(cwd: string, kp: string): { found: boolean; value:
* Centralizing emission here means masking can't be missed at a call site.
*/
function emitResolvedDefault(kp: string, value: unknown, raw: boolean): void {
const resolvedValue = kp === 'planner.stall_detection_enabled'
? resolvePlannerStallDetectionEnabled(value)
: value;
if (isSecretKey(kp)) {
const masked = maskSecret(value as Parameters<typeof maskSecret>[0]);
const masked = maskSecret(resolvedValue as Parameters<typeof maskSecret>[0]);
output(masked, raw, masked);
return;
}
output(value, raw, String(value));
output(resolvedValue, raw, String(resolvedValue));
}
// ─── Validation helpers ───────────────────────────────────────────────────────
@@ -955,6 +959,14 @@ function cmdConfigSet(cwd: string, keyPath: string | undefined, value: string |
}
}
// Planner watchdog opt-out (#4570) — only a real boolean may change the
// default-on policy. In particular, string "false" must not disable it.
if (kp === 'planner.stall_detection_enabled') {
if (typeof parsedValue !== 'boolean') {
error(`Invalid planner.stall_detection_enabled '${val}'. Must be a boolean (true or false).`);
}
}
// #3086 — git.create_tag: boolean only
if (kp === 'git.create_tag') {
if (typeof parsedValue !== 'boolean') {
@@ -1251,6 +1263,10 @@ function cmdConfigGet(cwd: string, keyPath: string | undefined, raw: boolean, de
error(`Key not found: ${kp}`, ERROR_REASON.CONFIG_KEY_NOT_FOUND);
}
if (kp === 'planner.stall_detection_enabled') {
current = resolvePlannerStallDetectionEnabled(current);
}
// Never echo plaintext for sensitive keys via config-get. Plaintext lives
// in config.json on disk; the CLI surface always shows the masked form.
if (isSecretKey(kp)) {