feat(headless,auto): surface self-feedback queue at autonomous-loop idle
Two thin slices toward sf-mp4rxkwb-l4baga: 1. Help text. The triage and reflect commands have shipped over the last few commits but neither was discoverable via `sf headless help`. Add both to the command list + add five usage examples covering the piping and --run patterns. 2. Bail-time queue notifier. When the autonomous loop is about to break for "no-active-milestone" or "milestone-complete" while open self-feedback entries still exist, surface the queue with a clear pointer to `sf headless triage --list` / `--run`. Best-effort wrapper that never throws — the proper fix (triage as a real unit type with begin/dispatch/checkpoint/complete lifecycle) is the larger remaining slice of the parent entry; this just makes the queue VISIBLE at the exact moment operators historically lost track of it. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
34521814cc
commit
001740680b
2 changed files with 38 additions and 0 deletions
|
|
@ -225,6 +225,7 @@ const SUBCOMMAND_HELP: Record<string, string> = {
|
|||
" query Machine snapshot: JSON state + next dispatch + costs (no LLM)",
|
||||
" usage Live LLM-provider usage snapshot (today: gemini-cli tier + per-model quota)",
|
||||
" reflect Assemble reflection corpus + render prompt for cross-corpus pattern analysis (--json for raw, --run to dispatch to gemini-cli, --model <id> to override)",
|
||||
" triage Render canonical self-feedback triage prompt for piping into a model (--list for digest, --json for structured, --max N to cap, --run to dispatch + write decisions to .sf/triage/decisions/, --model <id> to override)",
|
||||
"",
|
||||
"new-milestone flags:",
|
||||
" --context <path> Path to spec/PRD file (use '-' for stdin)",
|
||||
|
|
@ -255,6 +256,11 @@ const SUBCOMMAND_HELP: Record<string, string> = {
|
|||
" sf headless query Instant machine JSON state snapshot",
|
||||
" sf headless status uok UOK gate health table (last 24h)",
|
||||
" sf headless status uok --json UOK gate health as JSON",
|
||||
" sf headless triage --list Self-feedback queue digest (impact↓ effort↑ ts↑)",
|
||||
" sf headless triage | sf-some-model Pipe triage prompt to any model",
|
||||
" sf headless triage --run Dispatch triage to default model + write decisions",
|
||||
" sf headless reflect Render reflection prompt for piping",
|
||||
" sf headless reflect --run Dispatch reflection + write report",
|
||||
"",
|
||||
"Exit codes: 0 = success, 1 = error/timeout, 10 = blocked, 11 = cancelled",
|
||||
].join("\n"),
|
||||
|
|
|
|||
|
|
@ -63,6 +63,7 @@ import {
|
|||
rollbackToCheckpoint,
|
||||
} from "../safety/git-checkpoint.js";
|
||||
import { resolveSafetyHarnessConfig } from "../safety/safety-harness.js";
|
||||
import { selectInlineFixCandidates } from "../self-feedback-drain.js";
|
||||
import { recordSelfFeedback } from "../self-feedback.js";
|
||||
import {
|
||||
checkpointWal,
|
||||
|
|
@ -123,6 +124,35 @@ import {
|
|||
} from "./types.js";
|
||||
import { closeoutAndStop, generateMilestoneReport, maybeFireProductAudit, shouldRunPlanningFlowGate } from "./phases-helpers.js";
|
||||
|
||||
/**
|
||||
* Surface the open self-feedback queue to the operator at idle-bail time.
|
||||
*
|
||||
* Why here (sf-mp4rxkwb-l4baga partial): the autonomous loop has historically
|
||||
* exited silently on "no-active-milestone" / "milestone-complete" while the
|
||||
* self-feedback queue still had unresolved entries. Operators didn't know
|
||||
* the queue was waiting, so triage never ran. This helper makes the queue
|
||||
* visible at the exact moment the loop is about to bail — the proper fix
|
||||
* (triage as a real unit type) is the larger remaining slice of the parent
|
||||
* entry.
|
||||
*
|
||||
* Best-effort: never throws, never blocks. If candidate selection fails or
|
||||
* the basePath isn't a forge repo, this is a no-op.
|
||||
*/
|
||||
function surfaceSelfFeedbackQueueOnIdle(ctx, basePath, exitReason) {
|
||||
try {
|
||||
const candidates = selectInlineFixCandidates(basePath);
|
||||
if (!Array.isArray(candidates) || candidates.length === 0) return;
|
||||
const n = candidates.length;
|
||||
const noun = n === 1 ? "entry" : "entries";
|
||||
ctx.ui.notify(
|
||||
`Idle (${exitReason}) but ${n} self-feedback ${noun} still open. Run \`sf headless triage --list\` to scan, or \`sf headless triage --run\` to dispatch the canonical triage prompt.`,
|
||||
"warning",
|
||||
);
|
||||
} catch {
|
||||
// Best-effort — never block the loop's bail path on a queue probe.
|
||||
}
|
||||
}
|
||||
|
||||
// ─── runPreDispatch ───────────────────────────────────────────────────────────
|
||||
/**
|
||||
* Phase 1: Pre-dispatch — resource guard, health gate, state derivation,
|
||||
|
|
@ -616,6 +646,7 @@ export async function runPreDispatch(ic, loopState) {
|
|||
`No active milestone — ${incomplete.length} incomplete (${ids}), see diagnostic above`,
|
||||
);
|
||||
}
|
||||
surfaceSelfFeedbackQueueOnIdle(ctx, s.basePath, "no-active-milestone");
|
||||
debugLog("autoLoop", { phase: "exit", reason: "no-active-milestone" });
|
||||
deps.emitJournalEvent({
|
||||
ts: new Date().toISOString(),
|
||||
|
|
@ -709,6 +740,7 @@ export async function runPreDispatch(ic, loopState) {
|
|||
);
|
||||
deps.logCmuxEvent(prefs, `Milestone ${mid} complete.`, "success");
|
||||
await closeoutAndStop(ctx, pi, s, deps, `Milestone ${mid} complete`);
|
||||
surfaceSelfFeedbackQueueOnIdle(ctx, s.basePath, "milestone-complete");
|
||||
debugLog("autoLoop", { phase: "exit", reason: "milestone-complete" });
|
||||
deps.emitJournalEvent({
|
||||
ts: new Date().toISOString(),
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue