mirror of
https://github.com/backnotprop/plannotator.git
synced 2026-09-14 14:17:26 +08:00
daa26faa2b
* fix(pi): persist approved plan checklist progress * test(pi): restore checklist progress from prior sessions * feat(pi): mark plan steps during execution * fix(pi): align mark_done instructions and harden checklist rendering Review fixes for #1496: - phase-tools-runtime: pin plannotator_mark_done in the executing-phase active tool union and its release at agent_end. - Align the plan-approved doneMsg with the executing framing: call plannotator_mark_done after each completed step, [DONE:n] markers as the fallback for interrupted executions. - Harden renderCompletedChecklist: restrict the shared checklist pattern's whitespace to [^\S\n] so a whitespace-only checkbox line can never swallow the next line (the root cause the review named — \s+ crossing newlines), replace the marker at the match's own capture position instead of first-occurrence line.replace("[ ]", ...), and mirror parseChecklist's empty-text skip so ordinals stay aligned. Regression test: blank checkbox placeholders around real steps; the real step's box flips, the placeholders are never touched (revert-verified). - Document the one-turn ordinal-desync window when an agent edits the plan's checkboxes mid-turn, bounded by upgrade-only writes plus the per-turn re-parse. --------- Co-authored-by: Michael Ramos <mdramos8@gmail.com>
79 lines
2.2 KiB
TypeScript
79 lines
2.2 KiB
TypeScript
import { describe, expect, test } from "bun:test";
|
|
import { parseChecklist, renderCompletedChecklist } from "./checklist";
|
|
|
|
describe("renderCompletedChecklist", () => {
|
|
test("marks completed items without changing other Markdown", () => {
|
|
const content = [
|
|
"# Plan",
|
|
"",
|
|
"- [ ] First step",
|
|
" Supporting detail",
|
|
"* [x] Second step",
|
|
"- [ ] Third step",
|
|
"",
|
|
].join("\r\n");
|
|
const items = parseChecklist(content);
|
|
items[0]!.completed = true;
|
|
items[2]!.completed = true;
|
|
|
|
expect(renderCompletedChecklist(content, items)).toBe([
|
|
"# Plan",
|
|
"",
|
|
"- [x] First step",
|
|
" Supporting detail",
|
|
"* [x] Second step",
|
|
"- [x] Third step",
|
|
"",
|
|
].join("\r\n"));
|
|
});
|
|
|
|
test("a whitespace-only checkbox line never receives another step's marker", () => {
|
|
// Degenerate plan: blank checkbox placeholders sit between the real
|
|
// steps. Before hardening, the shared pattern's \s+ crossed the newline,
|
|
// pairing the blank line's marker with the next real line, and the
|
|
// first-occurrence `replace("[ ]", ...)` then wrote the [x] into the
|
|
// blank placeholder instead of the real step's box.
|
|
const content = [
|
|
"# Plan",
|
|
"",
|
|
"- [ ]",
|
|
"- [ ] Step one",
|
|
"- [ ] ",
|
|
"- [ ] Step two",
|
|
"",
|
|
].join("\n");
|
|
const items = parseChecklist(content);
|
|
expect(items.map((item) => item.text)).toEqual(["Step one", "Step two"]);
|
|
|
|
items[0]!.completed = true;
|
|
expect(renderCompletedChecklist(content, items)).toBe([
|
|
"# Plan",
|
|
"",
|
|
"- [ ]",
|
|
"- [x] Step one",
|
|
"- [ ] ",
|
|
"- [ ] Step two",
|
|
"",
|
|
].join("\n"));
|
|
|
|
items[1]!.completed = true;
|
|
expect(renderCompletedChecklist(content, items)).toBe([
|
|
"# Plan",
|
|
"",
|
|
"- [ ]",
|
|
"- [x] Step one",
|
|
"- [ ] ",
|
|
"- [x] Step two",
|
|
"",
|
|
].join("\n"));
|
|
});
|
|
|
|
test("does not clear existing completed items", () => {
|
|
const content = "- [x] Completed step\n- [ ] Unfinished step\n";
|
|
const items = parseChecklist(content);
|
|
items[0]!.completed = false;
|
|
|
|
expect(renderCompletedChecklist(content, items)).toBe(content);
|
|
});
|
|
});
|