feat(maintenance): add stale-task cleanup scheme
- scripts/cleanup_stale_tasks.py: daily script that cancels open tasks in completed/archived workstreams; handles 307 redirects; emits a cleanup progress event summarising results - Makefile: add cleanup-stale target (also suitable for cron) - ADR-001: append Workstream Closure Protocol section — mandatory closure review before marking workstream completed, with task classification table (done/cancelled/carry-forward) and Closure Review file format - WP-0002 + WP-0005: append Closure Review sections documenting the 2026-03-02 cleanup run (26 stale DB rows cancelled — all were legacy pre-ADR-001 DB-first records; file status was already done) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -174,6 +174,77 @@ and others as "file-authoritative."
|
||||
Rejected: introduces ambiguity about which records matter; violates the
|
||||
"single source of truth" principle.
|
||||
|
||||
## Workstream Closure Protocol
|
||||
|
||||
When a workstream is about to be marked `completed`, the responsible agent
|
||||
MUST perform a closure review before writing the status change. This prevents
|
||||
the stale-task accumulation that this ADR was designed to make detectable.
|
||||
|
||||
### Steps
|
||||
|
||||
1. **Query all non-done tasks** in the workstream via
|
||||
`GET /tasks/?workstream_id=<uuid>` (filter for `todo`, `in_progress`,
|
||||
`blocked`).
|
||||
|
||||
2. **Classify each task** into one of three outcomes:
|
||||
|
||||
| Outcome | Action |
|
||||
|---------|--------|
|
||||
| **Done** — work was completed, DB record just wasn't updated | `PATCH /tasks/{id}/ {"status": "done"}` |
|
||||
| **Cancelled** — dropped, superseded, or out of scope | `PATCH /tasks/{id}/ {"status": "cancelled", "blocking_reason": "<why>"}` |
|
||||
| **Carry-forward** — genuinely unfinished, belongs in the next run | Leave open; note in closure review; trigger new workplan |
|
||||
|
||||
3. **Append a `## Closure Review` section** to the workplan file:
|
||||
|
||||
```markdown
|
||||
## Closure Review — YYYY-MM-DD
|
||||
|
||||
**Outcome:** All tasks completed / N tasks carried forward / N tasks dropped.
|
||||
|
||||
### Completed (DB updated)
|
||||
- TASK-ID — title
|
||||
|
||||
### Cancelled (dropped)
|
||||
| Task | Reason |
|
||||
|------|--------|
|
||||
| TASK-ID — title | Superseded by X |
|
||||
|
||||
### Carried forward
|
||||
| Task | Target workplan |
|
||||
|------|----------------|
|
||||
| TASK-ID — title | CUST-WP-XXXX |
|
||||
```
|
||||
|
||||
4. **If any tasks are carried forward**: do not mark the workstream
|
||||
`completed` yet. Create the new workplan file (or amend an existing active
|
||||
one), then close the current workstream.
|
||||
|
||||
5. **Update the workplan frontmatter** `status: completed` and `updated:` date.
|
||||
|
||||
6. **Mark the workstream `completed`** in the state hub via MCP or API.
|
||||
|
||||
### Daily Stale-Task Cleanup
|
||||
|
||||
As a safety net for cases where the closure review was skipped or incomplete,
|
||||
a cleanup script cancels any surviving open tasks in completed/archived
|
||||
workstreams:
|
||||
|
||||
```bash
|
||||
cd ~/the-custodian/state-hub
|
||||
make cleanup-stale # run immediately
|
||||
# or add to cron:
|
||||
# 0 3 * * * cd ~/the-custodian/state-hub && make cleanup-stale
|
||||
```
|
||||
|
||||
The script (`scripts/cleanup_stale_tasks.py`) emits a `cleanup` progress event
|
||||
recording which tasks were cancelled and in which workstreams. Tasks cancelled
|
||||
by the cleanup carry a `blocking_reason` noting they should be verified against
|
||||
the workplan file.
|
||||
|
||||
The closure review is the primary mechanism; the cleanup is the fallback. If
|
||||
the cleanup regularly cancels tasks, it signals that closure reviews are being
|
||||
skipped — that is the process failure to address, not just the stale tasks.
|
||||
|
||||
## Related
|
||||
|
||||
- Custodian Constitution v0.1 §2 (Powers) — canon changes require review gate
|
||||
|
||||
Reference in New Issue
Block a user