feat(FN-4946): complete steps 5-8 implicit refusal docs and budget coverage
Fusion-Task-Id: FN-4946 Fusion-Task-Lineage: 9a48f72f-d2cc-4950-8a00-f69053dd1163
This commit is contained in:
committed by
gsxdsm
parent
c1a01fbb8a
commit
28595f57e9
5
.changeset/fn-4946-implicit-task-done-guards.md
Normal file
5
.changeset/fn-4946-implicit-task-done-guards.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@runfusion/fusion": patch
|
||||
---
|
||||
|
||||
Extend FN-4851 fn_task_done refusal guards (pending-code-review-revise, bulk-step-completion-without-review) to the implicit-completion path so agents cannot bypass them by drip-marking every step done via fn_task_update and exiting without calling fn_task_done. Implicit refusals share the existing requeue budget and escalate to in-review on exhaustion.
|
||||
@@ -367,6 +367,7 @@ The Ink-based TUI is part of `fn` (no separate `@fusion/tui` package). Implement
|
||||
Structured logging via `createLogger()` from `packages/engine/src/logger.ts`. All lines prefixed with subsystem name. See [docs/diagnostics.md](./docs/diagnostics.md) for the full key-diagnostic-points catalog. Notable subsystems include `[executor]`, `[scheduler]`, `[stuck-detector]`, `[auto-claim-snapshot]`, `[prompt-size]`, `[wake-trigger-diagnostics]`, `[retry-burned]`, and `[room-ambiguity]`.
|
||||
|
||||
`AgentSemaphore` (`packages/engine/src/concurrency.ts`) has defensive guards: `limit` getter returns minimum 1; `availableCount` returns 0 for invalid limits.
|
||||
- `[executor] FN-XXX: fn_task_done refused (<class>) — <reason>` (explicit tool path) and `[executor] FN-XXX: fn_task_done refused (<class>) — <reason> (implicit completion)` (implicit all-steps-done path) now share refusal-class diagnostics for `summary-claims-incomplete` (explicit only), `bulk-step-completion-without-review`, and `pending-code-review-revise`; both paths consume the same `MAX_TASK_DONE_REQUEUE_RETRIES` budget and escalate to `in-review` with `status: "failed"` on exhaustion.
|
||||
|
||||
## Dashboard UI Styling Guide
|
||||
|
||||
|
||||
@@ -77,10 +77,23 @@ describe("FN-4946 implicit refusal budget handling", () => {
|
||||
const executor = new TaskExecutor(store as any, "/repo");
|
||||
await executor.execute(currentTask);
|
||||
|
||||
// Burn explicit-path refusal budget from 2 -> 3 (still todo), then implicit refusal should escalate immediately.
|
||||
await doneTool.execute("d1", { summary: "I am not done yet." });
|
||||
await (executor as any).handleImplicitTaskDoneRefusal({ ...currentTask, id: "FN-4946-B2" }, "/repo/.worktrees/swift-falcon", refusal());
|
||||
expect(currentTask.taskDoneRetryCount).toBe(3);
|
||||
|
||||
expect(store.moveTask).toHaveBeenCalledWith("FN-4946-B2", "in-review");
|
||||
await (executor as any).handleImplicitTaskDoneRefusal(
|
||||
{ ...currentTask, id: "FN-4946-B2", column: "todo" },
|
||||
"/repo/.worktrees/swift-falcon",
|
||||
refusal(),
|
||||
);
|
||||
|
||||
const inReviewMoves = store.moveTask.mock.calls.filter((call: any[]) => call[0] === "FN-4946-B2" && call[1] === "in-review");
|
||||
expect(inReviewMoves.length).toBeGreaterThanOrEqual(1);
|
||||
const implicitEscalationUpdate = store.updateTask.mock.calls.find(
|
||||
([id, patch]: [string, Record<string, unknown>]) =>
|
||||
id === "FN-4946-B2" && patch.status === "failed" && !("taskDoneRetryCount" in patch),
|
||||
);
|
||||
expect(implicitEscalationUpdate).toBeTruthy();
|
||||
});
|
||||
|
||||
it("resets taskDoneRetryCount after later clean completion", async () => {
|
||||
|
||||
Reference in New Issue
Block a user