Skip to content

Add task_stage assign action — claim minus the stage advance#4

Open
memphyssk wants to merge 1 commit intokeshrath:mainfrom
memphyssk:feat/task-stage-assign
Open

Add task_stage assign action — claim minus the stage advance#4
memphyssk wants to merge 1 commit intokeshrath:mainfrom
memphyssk:feat/task-stage-assign

Conversation

@memphyssk
Copy link
Copy Markdown

What

Adds task_stage({action: "assign"}) — same semantics as claim (assign agent, flip status to in_progress, emit task:claimed, respect confidence gate) but does NOT advance the stage.

Why

claim conflates two operations:

  1. assign an agent to the task,
  2. advance from the first stage to the next.

For the default pipeline (backlog → spec → plan → ...) this is correct. backlog isn't a do-work stage; it just means "queued." Claiming naturally advances to spec (the first do-work stage). Same shape for most projects using the default pipeline.

For custom pipelines registered via task_config(action: \"pipeline\") whose first stage is itself a do-work stage, this becomes friction. Concrete example from a downstream framework:

business-influencer pipeline:
  I-0-pull → I-1-plan → I-2-sanity → I-3-exec → I-4-test → I-5-closeout

I-0-pull is the head agent's "read principles + scope interpretation + tooling" stage — a real do-work stage that needs to happen before planning. When the head wants to pick up a queued task at I-0-pull without skipping context-read and jumping straight to I-1-plan, today the only options are:

Option Problem
claim Auto-advances to I-1-plan, skipping I-0-pull
task_update({assign_to: ...}) Loses confidence gate, no task:claimed event, no status flip

Neither is right. The natural verb is "assign without advancing" — claim's contract minus one step.

How

assign is a clone of claim with the firstStage → nextStage logic removed. Stage stays put. Status flips pending → in_progress (a "pending + assigned" state would be incoherent for the next-task picker, which would otherwise re-hand the task out).

Path Same as claim Different from claim
Pending check ✅ same
Confidence gate (min_confidence_for_claim) ✅ same
assigned_to set to claimer ✅ same
Status → in_progress ✅ same
task:claimed event ✅ same
Stage advances ❌ never (stage stays)

Changes

File Change
src/domain/tasks.ts new TaskService.assign() (~30 LOC) — same gating, same event, same error semantics, no stage motion
src/transport/mcp.ts task_stage action enum gains \"assign\"; tool description updated
src/transport/mcp-handlers.ts handleStage dispatches \"assign\"tasks.assign()

REST has no /api/tasks/:id/claim endpoint today, so no REST surface change. (Existing PUT /api/tasks/:id/stage is advance/regress only.)

Tests

10 new tests, all passing:

  • tests/tasks.test.ts (5):
    • assigns without advancing the stage (default pipeline)
    • respects custom pipelines (stays at the first stage)
    • rejects assigning non-pending task
    • emits task:claimed event
    • respects min_confidence_for_claim gate (parity with claim)
  • tests/mcp.test.ts (5):
    • assigns without advancing via MCP handler
    • uses custom assignee name via claimer arg
    • rejects assign on already-claimed task
$ npm run check
> tsc --noEmit                    ✓
> eslint src/ tests/               ✓
> prettier --check .               ✓
> vitest run                       ✓ 440/440 passed

Open design choices (happy to adjust)

  1. Reuses the claimer arg name. MCP handler reads optString(args, 'claimer') for both claim and assign. Avoids adding a new arg name; keeps the schema small. Tool description spells out that it applies to both.
  2. Reuses the task:claimed event channel. Both claim and assign are "agent took ownership" — same event type makes downstream consumers (knowledge bridge, agent-comm, dashboard) work without changes.
  3. Same pending-only restriction as claim. Reassignment is a future PR; this is the minimal surgical addition.

Pairing

This pairs naturally with the task_list tags filter PR (#3) — same downstream framework needed both. Independent reviews welcome; either can land first.


🤖 Generated with Claude Code

Existing `claim` action conflates two operations: assign an agent AND
advance from the first stage to the next. For the default pipeline
(backlog → spec → plan → ...) this is fine — backlog isn't a do-work
stage, so claim's auto-advance from backlog to spec matches intent.

For custom pipelines whose first stage is itself a do-work stage
(read context, pull from queue, load principles), there's no way to
hand a task to an agent without also moving the work forward. Today
that means either:
  (a) call `task_update assign_to=...` and lose the confidence gate +
      claim event + status flip from claim, or
  (b) call `claim` and accept an unwanted stage advance.

This PR adds `task_stage({action: "assign"})` — the same path as claim
(pending check, confidence gate, claim event, status → in_progress)
but the stage stays put. About 30 lines in the domain layer + 4 in the
transport layer.

- domain/tasks.ts: new TaskService.assign() — clone of claim() minus
  the firstStage→nextStage logic. Same gating, same event, same error
  semantics. Status flips pending → in_progress (a "pending + assigned"
  state would be incoherent for the next-task picker).
- transport/mcp.ts: action enum gains "assign"; tool description updated.
- transport/mcp-handlers.ts: handleStage dispatches "assign" → tasks.assign().
- tests/tasks.test.ts: 5 domain tests (default pipeline, custom pipeline,
  reject non-pending, event emission, confidence gate parity).
- tests/mcp.test.ts: 5 MCP tests (assign without advance, custom claimer,
  reject after claim, parity coverage).

REST has no `/claim` endpoint today, so no REST surface change.

All 440 tests pass; npm run check (typecheck + lint + format:check + test) green.
memphyssk pushed a commit to memphyssk/agent-tasks that referenced this pull request May 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant