Repository navigation
feat(tasks): let a task complete with its subtasks - #157
chriscoveries wants to merge 2 commits into
Conversation
A task that groups subtasks has to be closed by hand once the last of them
is done, and nothing reopens it when one of them comes back, so a parent
can say done while work under it is open again.
A task can now set `rollup: true`. Its completion then follows its
subtasks. When the last subtask that is not archived is done, the task
moves to done in the same write. When a subtask leaves done, or a new one
is added, while the task is done, the task moves back to active (or to
planned if no subtask is active), and the same rule applies to its own
parent. A rollup task cannot be moved to done while a subtask is open.
Only a change to the subtasks, or setting the flag, triggers this, so a
direct move of the task itself stands. Each automatic move is listed in
the change entry of the operation that caused it
(`rollup: [{task_id, status}]`), so the `context.sync` event records it.
The flag is off unless set, and existing tasks are unchanged.
|
The opt-in is preferable to unconditional rollup, but I am deferring this feature because it changes task completion semantics and has no corresponding Web indication or controls. A parent can represent an aggregate, or work that still needs its own validation after all children finish; those meanings should be distinguishable to the user. Please first define the smallest complete behavior: whether parent acceptance/checklist work remains, how explicit parent moves interact with rollup, and what happens on reopen, reparenting, archive and an empty child set. Show the rollup state and explain blocked completion in Web, rather than having status changes or rejected moves appear without context. Keep non-rollup tasks and current authorization unchanged. A derived child-progress display would be a lower-risk first step if automatic parent completion is not essential. This is a product-semantics decision, not a regression demonstrated merely by new-feature tests failing on main. |
Brings in caller task ids (`client_request_id`, `parent_ref`, `blocked_by_refs`) and the ordered persistence of task targets. - context/apply.rs: `apply_all` keeps main's `task.create` path, which records the created or replayed task id, and wraps every task op in the rollup shape check, so a create's rollup moves land in the same change entry. - context/task_apply.rs: `create` is main's, with the `rollup` flag validated before the task is built. - MCP mapping, tool schema and the Context ops standard list both sets of fields. - Tests: main's versions, with the rollup tests appended unchanged. - Persistence (yaml_storage.rs, yaml_files.rs) is main's, unchanged.
A task that groups subtasks has to be closed by hand once the last of them
is done, and nothing reopens it when one of them comes back. So a parent
can say done while work under it is open again, and whoever runs the board
has to keep checking parents against their subtasks.
A task can now set
rollup: true, ontask.createortask.update(andon the MCP
cccc_taskcreate and update actions). Its status then followsits subtasks, the tasks whose
parent_idis that task, not countingarchived ones:
the same write;
done, the task moves back to active if a subtask is active, otherwise to
planned;
error names the open subtasks.
Only a change to the subtasks (a move, a re-parent, a create or a delete)
or setting the flag triggers this, so a direct move of the task itself
stands. Each automatic move is listed in the change entry of the operation
that caused it (
rollup: [{task_id, status}]), so thecontext.syncevent in the ledger records it and who caused it. The flag is off unless
set; existing tasks are unchanged.
The automatic move does not check whether the caller may move the parent:
a peer that finishes the last subtask completes a parent it does not own.
Setting the flag is the parent owner's opt-in to that.
How we built it. Base
3d3d10cb. Before each task operation,apply_allnotes every task's status, parent and flag; after it, only theparents whose subtasks changed are settled, walking up the tree
(
task_apply.rs:roll_up,settle).task.moverefuses done on arollup task with open subtasks. The MCP mapping passes
rollupthrough oncreate and update, and the tool schema and
CCCC_CONTEXT_OPS_V1.mddescribe it. 7 files, +387/−8, about half of it tests.
How we tested it.
context_storage.rs), through the real context store: therefusal while subtasks are open; done with the last subtask, and the
change entry; back to active when a subtask reopens; archived subtasks
not counted; back to planned when a new subtask is added; a rollup
grandparent following in the same operation; nothing moves without the
flag; a non-boolean flag rejected.
integration_ops.rs), through the daemon'scontext_syncwith a foreman and a peer: the foreman cannot close the task while work
is open; the peer finishing the last subtask completes the foreman's
task, and the ledger's
context.syncevent records the rollup with thepeer as its author.
on
3d3d10cb,https://github.com/chriscoveries/cccc/actions/runs/39704475937 (2683
passed, 2 failed).
the core and MCP suites and the daemon's integration ops (578 tests);
clippy
-D warningsand rustfmt clean. At the red reference each of thetwo targets fails exactly its new test.
through the
cccc mcpstdio server as the Actor making it (a foremanand a peer). With 0.4.44 the same calls leave the parent planned after
its last subtask is done; once the foreman closes it by hand, it stays
done when a subtask reopens or a new one is added; and closing a rollup
task with an open subtask is accepted. With this build each step behaves
as described above, across a daemon restart, and the refused close reads
task T005 completes with its subtasks; still open: T006.-D warnings, nextest, Windows smoke):https://github.com/chriscoveries/cccc/actions/runs/39704473266
Tested on: claw, Debian 13, Skylake 8vCPU AVX2/AVX-512, 15G RAM.