Skip to content
Open
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
10 changes: 10 additions & 0 deletions .task
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"taskId": "1750",
"phase": "execution",
"fenceToken": 9,
"sessionId": "2c16cdd1-6e26-4b9a-9aab-0f83c2d423ab",
"journalPath": "/tmp/taskcore-worktrees/journal-T1750/tasks/T1750/",
"codeWorktree": "/tmp/taskcore-worktrees/code-T1750",
"claimedAt": 1773508861049,
"reviewNotes": []
}
124 changes: 124 additions & 0 deletions core/test/validator.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -458,3 +458,127 @@ test("TaskReparented: reparent of terminal task succeeds", () => {
const error = validateEvent(state, event);
assert.equal(error, null);
});

function reviewActiveState(): SystemState {
let state = createInitialState();
const events: Event[] = [
{
type: "TaskCreated",
taskId: "RV1",
ts: 1,
title: "Review validation",
description: "Review fence coverage",
parentId: null,
rootId: "RV1",
initialPhase: "execution",
initialCondition: "ready",
attemptBudgets: { analysis: { max: 2 }, decomposition: { max: 2 }, execution: { max: 2 }, review: { max: 2 } },
costBudget: 5,
dependencies: [],
reviewConfig: { reviewers: ["overseer"], policy: "all" },
skipAnalysis: true,
metadata: {},
source: { type: "middle", id: "test" },
},
{
type: "LeaseGranted",
taskId: "RV1",
ts: 2,
fenceToken: 1,
agentId: "coder",
phase: "execution",
leaseTimeout: 60_000,
sessionId: "exec-1",
sessionType: "fresh",
contextBudget: 512,
},
{
type: "AgentStarted",
taskId: "RV1",
ts: 3,
fenceToken: 1,
agentContext: {
sessionId: "exec-1",
agentId: "coder",
memoryRef: null,
contextTokens: 128,
modelId: "test",
},
},
{
type: "PhaseTransition",
taskId: "RV1",
ts: 4,
from: { phase: "execution", condition: "active" },
to: { phase: "review", condition: "ready" },
reasonCode: "work_complete",
reason: "work_complete",
fenceToken: 1,
agentContext: {
sessionId: "exec-1",
agentId: "coder",
memoryRef: null,
contextTokens: 128,
modelId: "test",
},
},
{
type: "LeaseGranted",
taskId: "RV1",
ts: 5,
fenceToken: 2,
agentId: "overseer",
phase: "review",
leaseTimeout: 60_000,
sessionId: "review-1",
sessionType: "fresh",
contextBudget: 512,
},
{
type: "AgentStarted",
taskId: "RV1",
ts: 6,
fenceToken: 2,
agentContext: {
sessionId: "review-1",
agentId: "overseer",
memoryRef: null,
contextTokens: 128,
modelId: "test",
},
},
];

for (const event of events) {
const reduced = reduce(state, event);
assert.equal(reduced.ok, true, `Event ${event.type} failed: ${!reduced.ok ? reduced.error.message : ""}`);
state = reduced.ok ? reduced.value.state : state;
}

return state;
}

test("ReviewVerdictSubmitted rejects stale review fence token", () => {
const state = reviewActiveState();
const event: Event = {
type: "ReviewVerdictSubmitted",
taskId: "RV1",
ts: 7,
fenceToken: 1,
reviewer: "overseer",
round: 1,
verdict: "approve",
reasoning: "Looks good",
agentContext: {
sessionId: "review-1",
agentId: "overseer",
memoryRef: null,
contextTokens: 128,
modelId: "test",
},
};

const error = validateEvent(state, event);
assert.ok(error);
assert.equal(error.code, "stale_fence_token");
});
50 changes: 50 additions & 0 deletions docs/daemon-post-endpoints.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Taskcore Daemon POST Endpoints Reference

Source: `middle/http.ts`

## Endpoint Summary

| Route | Purpose | Key Body Fields |
|-------|---------|-----------------|
| `POST /tasks` | Create task | title*, description*, assignee, reviewer, priority, parentId, dependsOn, costBudget, skipAnalysis |
| `POST /tasks/:id/events` | Raw event | type*, (any core event fields) |
| `POST /tasks/:id/status` | Status transition | status* (review/done/blocked/pending/execute/decompose/cancel), evidence, blocker, stateRef |
| `POST /tasks/:id/reparent` | Reparent task | newParentId* |
| `POST /tasks/:id/revive` | Revive failed/blocked | phase, resetAttempts, reason |
| `POST /tasks/:id/budget` | Increase budget | attemptBudgetIncrease, costBudgetIncrease, reason |
| `POST /tasks/:id/decompose/start` | Begin decomposition | (none) |
| `POST /tasks/:id/decompose/add-child` | Add decomp child | title*, description*, costAllocation*, skipAnalysis, assignee, reviewer, dependsOnSiblings |
| `POST /tasks/:id/decompose/commit` | Finalize decomp | approach |
| `POST /tasks/:id/decompose` | One-shot decomp | children* (array), approach |

Also: `PATCH /tasks/:id/metadata` — update metadata fields (priority, assignee, etc.)

\* = required

## Status Transition Map

```
status="execute": analysis.active → execution.ready
status="review": execution.active → review.ready
status="done": review.active → terminal:done
status="pending": review.active → execution.ready (changes requested)
status="blocked": any non-terminal → terminal:blocked
status="cancel": any non-terminal → terminal:canceled
status="decompose": analysis.active → decomposition.ready
```

## Decomposition Flow (Incremental)

```
POST /tasks/:id/decompose/start → creates in-memory session, returns budget
POST /tasks/:id/decompose/add-child → adds child spec (repeat)
POST /tasks/:id/decompose/commit → creates all children, parent → review.waiting
```

## Key Behaviors

- **Fencing**: All writes use the task's `currentFenceToken` to prevent stale mutations
- **Registry validation**: Assignee/reviewer validated against `agents.json`
- **Cost enforcement**: Decomposition child costs must sum ≤ parent remaining budget
- **Completion checks**: Tasks with `metadata.repo` require a valid stateRef; parent tasks with `completionRule='and'` require all children done
- **Notifications**: `done` and `blocked` transitions notify Telegram targets in `metadata.informed`
122 changes: 122 additions & 0 deletions docs/ops/taskcore/t1750-execution-plan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
# T1750: Taskcore Control Plane Fixes — Execution Plan

**Epic owner:** ceo
**Review owner:** kelvin
**Status:** Open coordination epic
**Updated:** 2026-03-14 heartbeat session

---

## What changed in this update

This plan was corrected to match the **live** task graph rather than the earlier aspirational decomposition draft:

- removed canceled/done children from the active-work list (`T2272` canceled, `T2273` done)
- restored the **full** metadata-inheritance scope for the active child (`T2271`), based on the earlier `T2267` root-cause analysis
- documented that **T2268 owns the merged T2268 + T2272 scope** (scheduler/dispatcher + claimability.js/CLI + reviewer deadlock detection)
- backfilled `parentId`, `reviewer`, and `repo` metadata onto the outstanding children so prompts and worktree provisioning line up with the parent epic

---

## Live child status

| Task | Status | Purpose | Notes |
|---|---|---|---|
| T2267 | blocked | Historical metadata-inheritance attempt | Earlier execution was misprovisioned against the wrong repo. Metadata now corrected to point at `/home/ubuntu/taskcore`; use as historical evidence, not as the primary forward work item. |
| T2268 | analysis.waiting | Queue claimability / review deadlocks | Active child. Includes merged T2272 claimability + CLI scope. |
| T2269 | analysis.waiting | Decomposition state reconciliation | Active child. |
| T2270 | analysis.waiting | Attention alerting integration | Active child; depends on T2274. |
| T2271 | analysis.waiting | Metadata inheritance | Active child. Scope is full parent metadata propagation, not only priority/assignee/reviewer. |
| T2272 | canceled | Duplicate of T2268 | Do not schedule. |
| T2273 | done | Completed decomposition slice | Do not schedule. |
| T2274 | analysis.waiting | Attention formatter crash fix | Active child. |
| T2275 | analysis.waiting | Obsolete / blocked task cleanup | Active child. |

---

## Active execution waves

### Wave 1 — parallel now

1. **T2271 — Metadata inheritance**
- Required propagation set: `repo`, `base_branch`, `informed`, `consulted`, `parentId`, reviewer / assignee / priority defaults, and any other operational metadata that decomposition currently drops.
- Why first: fixes future child provisioning / context loading and removes one of the root causes already observed in T2267.

2. **T2274 — Attention formatter crash**
- Harden the formatter / endpoint path so null or malformed task data cannot blow up the attention surface.
- Why first: prerequisite safety work for T2270.

3. **T2275 — Obsolete / blocked task cleanup tooling**
- Build cleanup tooling with dry-run safety and blocker-aware filters.
- Why first: operational hygiene without dependency on the other bug fixes.

### Wave 2 — after Wave 1 context is stable

4. **T2268 — Queue claimability false positives + review deadlocks**
- Dispatch-side: exclude `review.waiting` / non-claimable states from scheduler availability.
- Claim-side: fix CLI/claimability false positives, active-lease visibility bugs, and reviewer-role deadlock detection.
- This is the merged successor for T2272's canceled scope.

5. **T2269 — Decomposition state reconciliation**
- Make decomposition retries / partial materialization idempotent.
- Ensure superseded children are handled safely when a later decomposition version exists.

### Wave 3 — depends on T2274

6. **T2270 — Wire attention output to automated alerting**
- Only start once the formatter path is hardened.

---

## Metadata backfill applied during this session

The following outstanding children were patched in live task state:

- `T2267`
- `T2268`
- `T2269`
- `T2270`
- `T2271`
- `T2274`
- `T2275`

Backfilled fields:

- `parentId=1750`
- `reviewer=kelvin`
- `repo=/home/ubuntu/taskcore`

Why this matters:

- child prompts can now load the parent journal context
- review-ready children will not silently deadlock on a missing reviewer
- future code worktrees will target the real taskcore repo instead of the wrong workspace or no repo at all

Verification snapshot after the backfill:

| Task | `parentId` | `reviewer` | `repo` |
|---|---|---|---|
| T2267 | `1750` | `kelvin` | `/home/ubuntu/taskcore` |
| T2268 | `1750` | `kelvin` | `/home/ubuntu/taskcore` |
| T2269 | `1750` | `kelvin` | `/home/ubuntu/taskcore` |
| T2270 | `1750` | `kelvin` | `/home/ubuntu/taskcore` |
| T2271 | `1750` | `kelvin` | `/home/ubuntu/taskcore` |
| T2274 | `1750` | `kelvin` | `/home/ubuntu/taskcore` |
| T2275 | `1750` | `kelvin` | `/home/ubuntu/taskcore` |

---

## Open coordination risks

- **T2267 vs T2271 overlap:** T2267 remains blocked historical evidence while T2271 is the intended forward execution slot. Future submissions must not pretend the blocked historical attempt is the live implementation path.
- **Immutable task descriptions:** live task descriptions still reflect earlier wording; until taskcore gains a description-edit path, the parent journal and this plan are the authoritative coordination layer for corrected scope.
- **Parent should stay open:** no T1750 submission should claim epic completion until the remaining active children are actually closed out.

---

## Immediate next actions

1. Route coders to **T2271**, **T2274**, and **T2275** first.
2. Treat **T2268** as the merged owner of the canceled **T2272** claimability/CLI work.
3. Keep **T2270** behind **T2274**.
4. Do not list **T2272** or **T2273** as active child work in future evidence.
52 changes: 52 additions & 0 deletions docs/t1750-decomposition-strategy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# T1750 — Live Decomposition / Coordination State

Updated during the 2026-03-14 heartbeat work session.

## Epic status

T1750 remains **open**. Do not close the epic until every required child is either `done` or explicitly superseded/canceled with the parent state reconciled.

## Current child inventory

| Task | Live status | Role in the epic | Coordination notes |
|---|---|---|---|
| T2267 | `blocked` | Historical metadata-inheritance attempt | Blocked earlier because the task was provisioned against the wrong repo/worktree. During this session the child metadata was backfilled to `repo=/home/ubuntu/taskcore`, `parentId=1750`, `reviewer=kelvin` so a future revive/retry will point at the correct repo. Treat T2271 as the forward execution slot for this bug. |
| T2268 | `analysis.waiting` | Active queue claimability / review-deadlock fix | **Merged scope from canceled T2272 lives here.** This task now owns both dispatch-side guards (`scheduler.ts`, `dispatcher.ts`) and claim-side classifier / CLI listing work (`claimability.js`, queue listing, reviewer deadlock detection). |
| T2269 | `analysis.waiting` | Active decomposition-state reconciliation fix | Covers idempotent / partial `DecompositionCreated` handling and cleanup of superseded children. |
| T2270 | `analysis.waiting` | Active attention-alert wiring fix | Depends on T2274 hardening the formatter path first. |
| T2271 | `analysis.waiting` | Active metadata-inheritance fix | **Full scope, not the earlier narrowed wording.** This child must propagate parent metadata needed for real execution: `repo`, `base_branch`, `informed`, `consulted`, `parentId`, and other operational/custom fields, while letting child-specified values override defaults. |
| T2272 | `canceled` | Duplicate | Canceled intentionally after its claimability / CLI scope was merged into T2268. Do not treat it as an active child. |
| T2273 | `done` | Completed decomposition-state slice | Finished. Keep it out of the active-child plan. |
| T2274 | `analysis.waiting` | Active attention formatter crash fix | Independent Wave 1 work. |
| T2275 | `analysis.waiting` | Active obsolete / blocked task cleanup tooling | Independent Wave 1 work. |

## Execution waves

### Wave 1 — ready in parallel
- **T2271** — metadata inheritance (full parent-metadata propagation scope)
- **T2274** — attention formatter crash hardening
- **T2275** — obsolete / blocked task cleanup tooling

### Wave 2 — after Wave 1 context is in place
- **T2268** — merged queue claimability + CLI + review deadlock fix
- **T2269** — decomposition state reconciliation / idempotent child materialization

### Wave 3 — depends on T2274
- **T2270** — wire attention output into automated alerting

## Coordination repairs applied in this session

To reconcile live task state with the plan, the following metadata was backfilled onto the outstanding children (`T2267`, `T2268`, `T2269`, `T2270`, `T2271`, `T2274`, `T2275`):

- `parentId=1750` — so child prompts can load parent journal context
- `reviewer=kelvin` — so review-ready children do not deadlock with a null reviewer
- `repo=/home/ubuntu/taskcore` — so future code worktrees target the actual taskcore repo instead of the wrong workspace or no repo at all

This does **not** change the immutable task descriptions already stored in state, so the parent journal + this document are the live source of truth for the merged / corrected scope until the children are executed and closed.

## Practical guidance

- If a coder picks up **T2271**, they should implement the **full** metadata propagation bug described in the parent journal, not just priority/assignee/reviewer defaults.
- If a coder picks up **T2268**, they should treat the canceled **T2272** claimability / CLI scope as in-scope.
- Do **not** list T2272 or T2273 as active work items in future T1750 submissions.
- Keep the epic open; this session only repaired coordination / metadata drift and did not complete the children.
Loading