Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion loopx/control_plane/todos/context_projection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,8 @@ export function projectTodoSummaryDisplay(input: JsonObject, onSource?: (key: st
return gate.gate_state === "cleared_with_successor" && Array.isArray(gate.successor_todo_ids) ? gate.successor_todo_ids.map(id).filter(Boolean) : [];
}));
const selected: {item: JsonObject; text: unknown}[] = [], seen = new Set<string>();
const rawIds = new Set<unknown>();
const coordinates = new Map<unknown, {all: Set<string>; anonymous?: Set<string>}>();
if (source) {
for (const key of planning ? PLANNING_SOURCES : SUMMARY_SOURCES) {
for (const [index, value] of (Array.isArray(source[key]) ? source[key] as unknown[] : []).entries()) {
Expand All @@ -267,7 +269,20 @@ export function projectTodoSummaryDisplay(input: JsonObject, onSource?: (key: st
let todo = id(item.todo_id);
if (terminal && !todo) {const legacy = stripPythonWhitespace(pythonFieldText(item, "todo_id")); if (legacy.startsWith("todo_")) todo = legacy;}
if (!body || (planning ? !todo || !terminal && item.status === "done" || seen.has(todo!) : item.done === true)) continue;
if (!planning && selected.some(({item: prior, text}) => item.todo_id && prior.todo_id ? item.todo_id === prior.todo_id : prior.index === item.index && text === body)) continue;
if (!planning) {
// Either missing ID makes legacy coordinates decisive. Preserve raw
// IDs and strict equality: unlike Map keys, NaN never equals itself.
const rawId = item.todo_id, coordinate = item.index;
const prior = coordinate === coordinate ? coordinates.get(coordinate) : undefined;
if (rawId ? rawIds.has(rawId) || prior?.anonymous?.has(body) : prior?.all.has(body)) continue;
if (rawId) rawIds.add(rawId);
if (coordinate === coordinate) {
const entry = prior ?? {all: new Set<string>(), anonymous: undefined};
entry.all.add(body);
if (!rawId) (entry.anonymous ??= new Set<string>()).add(body);
coordinates.set(coordinate, entry);
}
}
if (todo) seen.add(todo);
selected.push({item, text: body});
onSource?.(key, index);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@ const CANONICAL_CONSUMERS = [
"control_plane/work_items/task_lease_lifecycle.ts",
"control_plane/work_items/task_lease_lifecycle_request.ts",
"control_plane/work_items/task_lease_workspace.ts",
"control_plane/work_items/yardstick_decision.ts",
"zcode_goal_mode/cli.ts",
"zcode_goal_mode/runtime.ts",
];
Expand Down
47 changes: 46 additions & 1 deletion tests/control_plane_ts/todo_summary_lanes.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ test("conflicting display fragments cannot certify a complete source", () => {
assert.equal(result.complete, false);
});

import {projectTodoSummaryDisplay} from "../../loopx/control_plane/todos/context_projection.ts";
import {projectTodoContextPage, projectTodoSummaryDisplay} from "../../loopx/control_plane/todos/context_projection.ts";

test("display source lanes preserve first identity, legacy coordinates and explicit readiness", () => {
const record = {active_next_action_items: [{todo_id: "todo_first", text: " First ", status: "open", resume_when: "todo_done:todo_gate"}],
Expand Down Expand Up @@ -132,3 +132,48 @@ test("planning keeps distinct domain states and terminal frontier evidence, with
assert.deepEqual(terminal[0]!.depends_on_todo_ids, ["todo_dependency"]);
assert.equal(terminal[1]!.note, "Evidence tail");
});

test("summary source identity keeps raw IDs and strict legacy coordinates", () => {
const cases: {name: string; items: JsonObject[]; selected: number[]}[] = [
{name: "raw identities and anonymous collisions", items: [
{todo_id: "todo_same", index: 1, text: " First "},
{todo_id: "todo_same", index: 2, text: "Duplicate id"},
{todo_id: " todo_same ", index: 1, text: "Second spelling"},
{todo_id: "todo_a", index: 7, text: " Same "},
{todo_id: "todo_b", index: 7, text: "Same"},
{index: 7, text: "Same"}, {index: 8, text: " Legacy "},
{todo_id: "todo_c", index: 8, text: "Legacy"},
{index: 9, text: "Legacy"}], selected: [0, 2, 3, 4, 6, 8]},
{name: "strict coordinates", items: [
{index: 2, text: "Coordinate"}, {index: "2", text: "Coordinate"},
{text: "Missing"}, {index: null, text: "Missing"},
{index: NaN, text: "Nonfinite"}, {index: NaN, text: "Nonfinite"},
{index: 0, text: "Zero"}, {index: -0, text: "Zero"},
{index: 2, text: "Coordinate"}], selected: [0, 1, 2, 3, 4, 5, 6]},
{name: "truthy and falsy raw identities", items: [
{todo_id: 1, index: 1, text: "Number id"},
{todo_id: true, index: 2, text: "Boolean id"},
{todo_id: 1, index: 3, text: "Same number id"},
{todo_id: 0, index: 4, text: "Zero id"},
{todo_id: 0, index: 5, text: "Zero id"},
{todo_id: "todo_falsy", index: 6, text: "False id collision"},
{todo_id: false, index: 6, text: "False id collision"},
{todo_id: "", index: 7, text: "Empty id"},
{todo_id: null, index: 7, text: "Empty id"}], selected: [0, 1, 3, 4, 5, 7]},
];
for (const {name, items, selected} of cases) {
const before = structuredClone(items), visited: number[] = [];
const source = {record: {items}};
const rows = projectTodoSummaryDisplay({source}, (_key, index) => visited.push(index));
assert.deepEqual(visited, selected, name);
assert.equal(rows.length, selected.length, name);
assert.deepEqual(projectTodoContextPage({summary_display: {source}}).items, rows, name);
assert.deepEqual(items, before, name);
}
});

test("flat summary items keep duplicate rows without source selection", () => {
const record = {todo_id: "todo_same", text: "Same row"};
const result = projectTodoContextPage({summary_display: {items: [{record}, {record}]}});
assert.equal((result.items as JsonObject[]).length, 2);
});
Loading