fix(ci): align tests/docs with issue-fix commits (recoveryRehome args, pi-ai mock Unknown/Record, new agent tools doc, hex-pattern comment reword)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
gsxdsm
2026-06-04 10:56:47 -07:00
parent 0b1c8fb56b
commit 0257bdf9d6
9 changed files with 40 additions and 18 deletions

View File

@@ -17,6 +17,11 @@ These tools are **not** part of the user-invokable extension surface. They are i
| `fn_task_document_read` | triage, executor, heartbeat | Read one task document or list all | `key?` (string) |
| `fn_workflow_list` | executor | List the project's custom workflows (read-only built-ins plus user definitions) | none |
| `fn_workflow_select` | executor | Assign a custom workflow to a task (defaults to the current task) | `workflow_id` (string), `task_id?` (string) |
| `fn_workflow_create` | executor | Create a custom workflow definition from a graph IR (validated server-side) | `name` (string), `description?` (string), `ir` (object), `layout?` (object) |
| `fn_workflow_update` | executor | Update a custom workflow definition's name/description/ir/layout (built-ins cannot be edited) | `workflow_id` (string), `name?` (string), `description?` (string), `ir?` (object), `layout?` (object), `rehome_to?` (string) |
| `fn_workflow_delete` | executor | Delete a custom workflow definition (built-ins cannot be deleted); selecting tasks are re-homed to the default workflow's entry column | `workflow_id` (string) |
| `fn_task_promote` | executor | Promote a held task out of a manual-release hold column (defaults to the current task) | `task_id?` (string) |
| `fn_trait_list` | executor | List the registered column trait catalog (built-in and plugin traits) | none |
| `fn_memory_search` | triage, executor, heartbeat | Search project memory plus per-agent layered memory snippets | `query` (string), `limit?` (number) |
| `fn_memory_get` | triage, executor, heartbeat | Read a bounded memory file window (including bounded per-agent layered paths) | `path` (string), `startLine?` (number), `lineCount?` (number) |
| `fn_memory_append` | executor, heartbeat (when writable backend enabled) | Append memory notes with explicit scope: `scope="agent"` for private operating context, `scope="project"` for workspace-wide durable knowledge | `scope?` (`project` \| `agent`), `layer` (`long-term` \| `daily`), `content` (string) |

View File

@@ -144,7 +144,7 @@ function isAgentCreatedTask(task: Task): boolean {
// ── Constants ───────────────────────────────────────────────────────────────
// #1403: widened to ColumnId so `.has(task.column)` accepts custom column ids
// Issue 1403: widened to ColumnId so `.has(task.column)` accepts custom column ids
// (which are not members and correctly resolve to false).
const EDITABLE_COLUMNS: Set<ColumnId> = new Set<ColumnId>(["triage", "todo"]);
@@ -2004,7 +2004,7 @@ function TaskCardComponent({
className="card-progress-fill"
style={{
width: `${progressPercent}%`,
// #1403: custom columns have no legacy progress color → fall back to accent.
// Issue 1403: custom columns have no legacy progress color → fall back to accent.
backgroundColor:
(COLUMN_PROGRESS_COLOR_MAP as Record<string, string>)[task.column] ?? "var(--accent)",
}}

View File

@@ -10,6 +10,12 @@ vi.mock("@earendil-works/pi-ai", () => ({
Array: (schema: unknown, opts?: unknown) => ({ type: "array", items: schema, ...((opts as object) ?? {}) }),
Union: (schemas: unknown[], opts?: unknown) => ({ anyOf: schemas, ...((opts as object) ?? {}) }),
Literal: (value: unknown) => ({ const: value }),
Unknown: (opts?: unknown) => ({ ...((opts as object) ?? {}) }),
Record: (_key: unknown, value: unknown, opts?: unknown) => ({
type: "object",
additionalProperties: value,
...((opts as object) ?? {}),
}),
},
}));

View File

@@ -101,7 +101,7 @@ describe("reliability interactions: executor no-fn_task_done vs worktree reclaim
branch: null,
worktreeSessionRetryCount: 1,
}));
expect(store.moveTask).toHaveBeenCalledWith("FN-4601", "todo", { preserveProgress: true });
expect(store.moveTask).toHaveBeenCalledWith("FN-4601", "todo", { preserveProgress: true, moveSource: "engine", recoveryRehome: true });
// FN-4806: session-start missing-worktree is engine self-heal, must not burn retry budget
// and must not mark the task failed.
expect(store.updateTask).not.toHaveBeenCalledWith(

View File

@@ -82,7 +82,7 @@ describe("FN-5219 reliability interactions: in-progress limbo recovery", () => {
expect(first).toBe(1);
expect(second).toBe(0);
expect(mockStore.moveTask).toHaveBeenCalledWith("FN-5149", "todo", { preserveProgress: true });
expect(mockStore.moveTask).toHaveBeenCalledWith("FN-5149", "todo", { preserveProgress: true, moveSource: "engine", recoveryRehome: true });
expect(mockStore.recordRunAuditEvent).toHaveBeenCalledWith(expect.objectContaining({
mutationType: "task:auto-recover-in-progress-limbo",
target: "FN-5149",

View File

@@ -87,8 +87,7 @@ describe("reliability interactions: FN-4917 worktree incomplete session-start",
}),
}));
expect(store.moveTask.mock.calls).toContainEqual(["FN-4917-T", "todo"]);
expect(store.moveTask.mock.calls.some((call: any[]) => call.length > 2)).toBe(false);
expect(store.moveTask.mock.calls).toContainEqual(["FN-4917-T", "todo", { moveSource: "engine", recoveryRehome: true }]);
for (const call of store.logEntry.mock.calls) {
const leaked = call.some((arg: unknown) => typeof arg === "string" && /Refusing to start coding agent/.test(arg));
expect(leaked).toBe(false);
@@ -113,7 +112,7 @@ describe("reliability interactions: FN-4917 worktree incomplete session-start",
await runRecovery(store, task, "Refusing to start coding agent in incomplete worktree: /tmp/wt", events);
expect(store.moveTask).toHaveBeenCalledWith("FN-4917-T", "todo", { preserveProgress: true });
expect(store.moveTask).toHaveBeenCalledWith("FN-4917-T", "todo", { preserveProgress: true, moveSource: "engine", recoveryRehome: true });
expect(store.moveTask.mock.calls).not.toContainEqual(["FN-4917-T", "todo"]);
for (const call of store.logEntry.mock.calls) {
const leaked = call.some((arg: unknown) => typeof arg === "string" && /Refusing to start coding agent/.test(arg));

View File

@@ -235,6 +235,12 @@ vi.mock("@earendil-works/pi-ai", () => ({
Array: (schema: unknown, opts?: unknown) => ({ type: "array", items: schema, ...((opts as object) ?? {}) }),
Union: (schemas: unknown[], opts?: unknown) => ({ anyOf: schemas, ...((opts as object) ?? {}) }),
Literal: (value: unknown) => ({ const: value }),
Unknown: (opts?: unknown) => ({ ...((opts as object) ?? {}) }),
Record: (_key: unknown, value: unknown, opts?: unknown) => ({
type: "object",
additionalProperties: value,
...((opts as object) ?? {}),
}),
},
}));
vi.mock("@earendil-works/pi-coding-agent", () => {

View File

@@ -118,7 +118,7 @@ describe("recoverInProgressLimbo", () => {
taskDoneRetryCount: null,
sessionFile: null,
}));
expect(store.moveTask).toHaveBeenCalledWith("FN-5149", "todo", { preserveProgress: true });
expect(store.moveTask).toHaveBeenCalledWith("FN-5149", "todo", { preserveProgress: true, moveSource: "engine", recoveryRehome: true });
expect(store.recordRunAuditEvent).toHaveBeenCalledWith(expect.objectContaining({
domain: "database",
mutationType: "task:auto-recover-in-progress-limbo",

View File

@@ -458,6 +458,8 @@ describe("SelfHealingManager", () => {
expect(store.moveTask).toHaveBeenCalledWith("FN-001", "todo", {
preserveProgress: true,
preserveStatus: true,
moveSource: "engine",
recoveryRehome: true,
});
expect(store.updateTask).toHaveBeenLastCalledWith("FN-001", expect.objectContaining({
stuckKillCount: 7,
@@ -494,6 +496,8 @@ describe("SelfHealingManager", () => {
expect(store.moveTask).toHaveBeenCalledWith("FN-001", "todo", {
preserveProgress: true,
preserveStatus: true,
moveSource: "engine",
recoveryRehome: true,
});
expect(store.updateTask).not.toHaveBeenCalledWith("FN-001", expect.objectContaining({
paused: false,
@@ -530,6 +534,8 @@ describe("SelfHealingManager", () => {
expect(store.moveTask).toHaveBeenCalledWith("FN-001", "todo", {
preserveProgress: true,
preserveStatus: true,
moveSource: "engine",
recoveryRehome: true,
});
expect(store.logEntry).toHaveBeenCalledWith(
"FN-001",
@@ -1364,7 +1370,7 @@ describe("SelfHealingManager", () => {
"FN-1473",
expect.stringContaining("no-progress no-task_done failure"),
);
expect(store.moveTask).toHaveBeenCalledWith("FN-1473", "todo");
expect(store.moveTask).toHaveBeenCalledWith("FN-1473", "todo", { moveSource: "engine", recoveryRehome: true });
managerWithRecovery.stop();
});
@@ -2233,7 +2239,7 @@ describe("SelfHealingManager", () => {
"FN-3900",
expect.stringContaining("session-start unusable-worktree assertion"),
);
expect(store.moveTask).toHaveBeenCalledWith("FN-3900", "todo", { preserveProgress: true });
expect(store.moveTask).toHaveBeenCalledWith("FN-3900", "todo", { preserveProgress: true, moveSource: "engine", recoveryRehome: true });
managerWithRecovery.stop();
});
@@ -2277,7 +2283,7 @@ describe("SelfHealingManager", () => {
"FN-4559",
expect.stringContaining("session-start unusable-worktree assertion"),
);
expect(store.moveTask).toHaveBeenCalledWith("FN-4559", "todo", { preserveProgress: true });
expect(store.moveTask).toHaveBeenCalledWith("FN-4559", "todo", { preserveProgress: true, moveSource: "engine", recoveryRehome: true });
managerWithRecovery.stop();
});
@@ -2309,7 +2315,7 @@ describe("SelfHealingManager", () => {
"FN-4560",
expect.stringContaining("session-start unusable-worktree assertion"),
);
expect(store.moveTask).toHaveBeenCalledWith("FN-4560", "todo", { preserveProgress: true });
expect(store.moveTask).toHaveBeenCalledWith("FN-4560", "todo", { preserveProgress: true, moveSource: "engine", recoveryRehome: true });
managerWithRecovery.stop();
});
@@ -2349,7 +2355,7 @@ describe("SelfHealingManager", () => {
"FN-4651",
expect.stringContaining("Auto-recovered (no-progress): session-start refused unusable worktree"),
);
expect(store.moveTask).toHaveBeenCalledWith("FN-4651", "todo");
expect(store.moveTask).toHaveBeenCalledWith("FN-4651", "todo", { moveSource: "engine", recoveryRehome: true });
managerWithRecovery.stop();
});
@@ -2574,7 +2580,7 @@ describe("SelfHealingManager", () => {
"FN-2164",
expect.stringContaining("Auto-retry 1/3"),
);
expect(store.moveTask).toHaveBeenCalledWith("FN-2164", "todo", { preserveProgress: true });
expect(store.moveTask).toHaveBeenCalledWith("FN-2164", "todo", { preserveProgress: true, moveSource: "engine", recoveryRehome: true });
managerWithRecovery.stop();
});
@@ -3912,7 +3918,7 @@ describe("SelfHealingManager", () => {
"FN-1572",
expect.stringContaining("in-review task still had incomplete steps"),
);
expect(store.moveTask).toHaveBeenCalledWith("FN-1572", "todo", { preserveProgress: true });
expect(store.moveTask).toHaveBeenCalledWith("FN-1572", "todo", { preserveProgress: true, moveSource: "engine", recoveryRehome: true });
managerWithRecovery.stop();
});
@@ -3971,7 +3977,7 @@ describe("SelfHealingManager", () => {
const result = await managerWithRecovery.recoverStaleIncompleteReviewTasks();
expect(result).toBe(1);
expect(store.moveTask).toHaveBeenCalledWith("FN-407-test-1", "todo", { preserveProgress: true });
expect(store.moveTask).toHaveBeenCalledWith("FN-407-test-1", "todo", { preserveProgress: true, moveSource: "engine", recoveryRehome: true });
managerWithRecovery.stop();
});
@@ -3999,7 +4005,7 @@ describe("SelfHealingManager", () => {
const result = await managerWithRecovery.recoverStaleIncompleteReviewTasks();
expect(result).toBe(1);
expect(store.moveTask).toHaveBeenCalledWith("FN-407-test-2", "todo", { preserveProgress: true });
expect(store.moveTask).toHaveBeenCalledWith("FN-407-test-2", "todo", { preserveProgress: true, moveSource: "engine", recoveryRehome: true });
managerWithRecovery.stop();
});
@@ -5808,7 +5814,7 @@ describe("SelfHealingManager", () => {
expect(result).toBe(1);
expect(store.updateTask).not.toHaveBeenCalled();
expect(store.moveTask).toHaveBeenCalledWith("FN-9003", "todo", { preserveProgress: true });
expect(store.moveTask).toHaveBeenCalledWith("FN-9003", "todo", { preserveProgress: true, moveSource: "engine", recoveryRehome: true });
managerWithRecovery.stop();
});