-
Notifications
You must be signed in to change notification settings - Fork 10
t1408.2: Wire task decomposition into dispatch pipeline #2997
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -76,6 +76,85 @@ Claim → Branch Setup → Task Development → Preflight → PR Create → PR R | |||||||||
|
|
||||||||||
| ## Workflow | ||||||||||
|
|
||||||||||
| ### Step 0.45: Task Decomposition Check (t1408.2) | ||||||||||
|
|
||||||||||
| Before claiming and starting work, classify the task to determine if it should be decomposed into subtasks. This catches over-scoped tasks before a worker spends hours on something that should be multiple focused PRs. | ||||||||||
|
|
||||||||||
| **When to run:** After resolving the task description (Step 0) and before claiming (Step 0.5). Skip if `--no-decompose` flag is passed or if the task already has subtasks in TODO.md. | ||||||||||
|
|
||||||||||
| **How it works:** | ||||||||||
|
|
||||||||||
| ```bash | ||||||||||
| DECOMPOSE_HELPER="$HOME/.aidevops/agents/scripts/task-decompose-helper.sh" | ||||||||||
|
|
||||||||||
| # Only run if the helper exists (t1408.1 must be merged) | ||||||||||
| if [[ -x "$DECOMPOSE_HELPER" && -n "$TASK_ID" ]]; then | ||||||||||
| # Check if subtasks already exist | ||||||||||
| EXISTING=$(/bin/bash "$DECOMPOSE_HELPER" classify --task-id "$TASK_ID" --repo-path "$(git rev-parse --show-toplevel)" --quiet 2>/dev/null) || EXISTING="" | ||||||||||
| EXISTING_KIND=$(echo "$EXISTING" | jq -r '.kind // "atomic"' 2>/dev/null || echo "atomic") | ||||||||||
|
|
||||||||||
| if [[ "$EXISTING_KIND" == "composite" ]]; then | ||||||||||
| EXISTING_SUBS=$(echo "$EXISTING" | jq -r '.existing_subtasks // empty' 2>/dev/null || echo "") | ||||||||||
| if [[ -n "$EXISTING_SUBS" && "$EXISTING_SUBS" != "[]" ]]; then | ||||||||||
| # Subtasks already exist — skip decomposition | ||||||||||
| echo "[t1408.2] Task $TASK_ID already has subtasks — proceeding with implementation" | ||||||||||
| fi | ||||||||||
| fi | ||||||||||
|
|
||||||||||
| # If no existing subtasks, classify the task description | ||||||||||
| if [[ -z "$EXISTING_SUBS" || "$EXISTING_SUBS" == "[]" ]]; then | ||||||||||
| CLASSIFY=$(/bin/bash "$DECOMPOSE_HELPER" classify --task "$TASK_DESC" --task-id "$TASK_ID" --repo-path "$(git rev-parse --show-toplevel)" --quiet 2>/dev/null) || CLASSIFY="" | ||||||||||
| TASK_KIND=$(echo "$CLASSIFY" | jq -r '.kind // "atomic"' 2>/dev/null || echo "atomic") | ||||||||||
| fi | ||||||||||
| fi | ||||||||||
| ``` | ||||||||||
|
|
||||||||||
| **If atomic (or helper unavailable):** Proceed to Step 0.5 (claim and implement directly). This is the default path — most tasks are atomic. | ||||||||||
|
|
||||||||||
| **If composite — interactive mode:** | ||||||||||
|
|
||||||||||
| Show the decomposition tree and ask for confirmation: | ||||||||||
|
|
||||||||||
| ```bash | ||||||||||
| DECOMPOSE=$(/bin/bash "$DECOMPOSE_HELPER" decompose --task "$TASK_DESC" --task-id "$TASK_ID" --repo-path "$(git rev-parse --show-toplevel)" --quiet 2>/dev/null) | ||||||||||
| SUBTASK_COUNT=$(echo "$DECOMPOSE" | jq '.subtasks | length' 2>/dev/null || echo 0) | ||||||||||
|
Comment on lines
+119
to
+120
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The bash example in this section uses
Suggested change
References
|
||||||||||
| ``` | ||||||||||
|
|
||||||||||
| Present to the user: | ||||||||||
|
|
||||||||||
| ```text | ||||||||||
| This task appears to be composite (contains 2+ independent concerns). | ||||||||||
| Suggested decomposition: | ||||||||||
|
|
||||||||||
| 1. {subtask_1_description} (~{estimate}) | ||||||||||
| 2. {subtask_2_description} (~{estimate}) [depends on: 1] | ||||||||||
| 3. {subtask_3_description} (~{estimate}) | ||||||||||
|
|
||||||||||
| Options: | ||||||||||
| Y - Create subtasks and dispatch them separately (recommended) | ||||||||||
| n - Implement as a single task anyway | ||||||||||
| e - Edit the decomposition before creating subtasks | ||||||||||
| ``` | ||||||||||
|
|
||||||||||
| If the user confirms (Y): | ||||||||||
|
|
||||||||||
| 1. Create child task IDs using `claim-task-id.sh` for each subtask | ||||||||||
| 2. Add child entries to TODO.md with `blocked-by:` edges from the decomposition | ||||||||||
| 3. Create briefs for each child task (inheriting parent context + subtask-specific scope) | ||||||||||
| 4. Label the parent task `status:blocked` with `blocked-by:` refs to children | ||||||||||
| 5. Ask: "Implement the first leaf task now, or queue all for dispatch?" | ||||||||||
|
|
||||||||||
| **If composite — headless mode:** | ||||||||||
|
|
||||||||||
| Auto-decompose without confirmation (the pulse already classified this as composite): | ||||||||||
|
|
||||||||||
| 1. Create child tasks, briefs, and TODO entries automatically | ||||||||||
| 2. Label parent as `status:blocked` | ||||||||||
| 3. Exit cleanly with: `DECOMPOSED: task $TASK_ID split into $SUBTASK_COUNT subtasks ($CHILD_IDS). Parent blocked. Children queued for dispatch.` | ||||||||||
| 4. The next pulse cycle dispatches the leaf tasks | ||||||||||
|
|
||||||||||
| **Depth limit:** Controlled by `DECOMPOSE_MAX_DEPTH` env var (default: 3). At depth 3+, tasks are always treated as atomic regardless of classification. | ||||||||||
|
|
||||||||||
| ### Step 0.5: Claim Task (t1017) | ||||||||||
|
|
||||||||||
| If the first argument is a task ID (`t\d+`), claim it before starting work. This prevents two agents (or a human and an agent) from working on the same task concurrently. | ||||||||||
|
|
||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -110,25 +110,48 @@ Use `templates/brief-template.md` as the base. Populate from conversation contex | |||||||||
| - Claude Code: `$CLAUDE_SESSION_ID` or the conversation ID from the CLI | ||||||||||
| - If unavailable: use `{app}:unknown-{ISO-date}` and note "session ID not captured" | ||||||||||
|
|
||||||||||
| ### Step 5.5: Decomposition Check (t1408) | ||||||||||
| ### Step 5.5: Classify and Decompose (t1408.2) | ||||||||||
|
|
||||||||||
| After creating the brief, classify the task to check if it should be decomposed: | ||||||||||
| After creating the brief, classify the task to determine if it should be decomposed into subtasks. This is the earliest point where decomposition can happen — before the task enters the dispatch queue. | ||||||||||
|
|
||||||||||
| ```bash | ||||||||||
| CLASSIFY=$(~/.aidevops/agents/scripts/task-decompose-helper.sh classify "$TASK_TITLE" 2>/dev/null || echo '{"kind":"atomic"}') | ||||||||||
| TASK_KIND=$(echo "$CLASSIFY" | sed -n 's/.*"kind"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p') | ||||||||||
| DECOMPOSE_HELPER="$HOME/.aidevops/agents/scripts/task-decompose-helper.sh" | ||||||||||
|
|
||||||||||
| if [[ -x "$DECOMPOSE_HELPER" ]]; then | ||||||||||
| CLASSIFY=$(/bin/bash "$DECOMPOSE_HELPER" classify --task "{title}" --quiet 2>/dev/null) || CLASSIFY="" | ||||||||||
| TASK_KIND=$(echo "$CLASSIFY" | jq -r '.kind // "atomic"' 2>/dev/null || echo "atomic") | ||||||||||
|
Comment on lines
+121
to
+122
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The bash example in this section uses
Suggested change
References
|
||||||||||
| fi | ||||||||||
| ``` | ||||||||||
|
|
||||||||||
| If `TASK_KIND` is `composite`, offer to decompose: | ||||||||||
| **If atomic:** Proceed to Step 6 (add single entry to TODO.md). This is the default. | ||||||||||
|
|
||||||||||
| **If composite:** Present the decomposition to the user: | ||||||||||
|
|
||||||||||
| ```text | ||||||||||
| This task appears to have multiple independent concerns. Would you like to split it into subtasks? | ||||||||||
| [Y] Yes — decompose into 2-5 subtasks with dependency edges | ||||||||||
| [n] No — keep as a single task | ||||||||||
| [e] Edit — show the decomposition and let me adjust | ||||||||||
| This task appears composite — it contains 2+ independent concerns. | ||||||||||
| Suggested decomposition: | ||||||||||
|
|
||||||||||
| {task_id}.1: {subtask_1_description} (~{estimate}) | ||||||||||
| {task_id}.2: {subtask_2_description} (~{estimate}) [depends on: .1] | ||||||||||
| {task_id}.3: {subtask_3_description} (~{estimate}) | ||||||||||
|
|
||||||||||
| Options: | ||||||||||
| 1. Create parent + subtasks (recommended for auto-dispatch) | ||||||||||
| 2. Keep as single task (implement all at once) | ||||||||||
| 3. Edit decomposition | ||||||||||
| ``` | ||||||||||
|
|
||||||||||
| On confirmation, run `task-decompose-helper.sh decompose "$TASK_TITLE"` and create child task IDs with `claim-task-id.sh` for each subtask. Set `blocked-by:` edges between siblings as indicated by the decomposition output. | ||||||||||
| If the user chooses option 1: | ||||||||||
|
|
||||||||||
| 1. Create the parent task entry in TODO.md (with `status:blocked`) | ||||||||||
| 2. For each subtask, run `claim-task-id.sh` to allocate `{task_id}.N` IDs | ||||||||||
| 3. Create a brief for each subtask (inheriting parent context) | ||||||||||
| 4. Add subtask entries to TODO.md with `blocked-by:` edges | ||||||||||
| 5. The parent entry gets `blocked-by:{task_id}.1,{task_id}.2,...` | ||||||||||
|
|
||||||||||
| Each subtask brief references the parent: `**Parent task:** {task_id} — see [todo/tasks/{task_id}-brief.md]` | ||||||||||
|
|
||||||||||
| **Skip decomposition when:** `--no-decompose` flag is passed, or the helper script is not available (t1408.1 not yet merged). | ||||||||||
|
|
||||||||||
| ### Step 6: Add to TODO.md | ||||||||||
|
|
||||||||||
|
|
@@ -139,6 +162,7 @@ Format the TODO.md entry using the allocated ID: | |||||||||
| ``` | ||||||||||
|
|
||||||||||
| **Auto-dispatch eligibility**: Only add `#auto-dispatch` if the brief has: | ||||||||||
|
|
||||||||||
| - At least 2 acceptance criteria beyond "tests pass" and "lint clean" | ||||||||||
| - A non-empty "How (Approach)" section with file references | ||||||||||
| - A non-empty "What" section with clear deliverable | ||||||||||
|
|
@@ -213,11 +237,14 @@ AI: Brief: todo/tasks/t326-brief.md | |||||||||
|
|
||||||||||
| ## CRITICAL: Supervisor Subtask Creation | ||||||||||
|
|
||||||||||
| When the AI supervisor creates subtasks (e.g., decomposing t005 into t005.1-t005.5), it MUST: | ||||||||||
| When the AI supervisor creates subtasks (e.g., decomposing t005 into t005.1-t005.5) — whether manually or via `task-decompose-helper.sh` (t1408.2) — it MUST: | ||||||||||
|
|
||||||||||
| 1. Create a brief for EACH subtask at `todo/tasks/{subtask_id}-brief.md` | ||||||||||
| 2. Reference the parent task's brief: `**Parent task:** {parent_id} — see [todo/tasks/{parent_id}-brief.md]` | ||||||||||
| 3. Inherit context from the parent but add subtask-specific details | ||||||||||
| 4. Include the session ID of the supervisor session that created the subtask | ||||||||||
| 5. Set `blocked-by:` edges between subtasks based on dependency analysis from the decomposition | ||||||||||
|
|
||||||||||
| When using `task-decompose-helper.sh decompose`, the output includes dependency edges (`depends_on` array) that map to `blocked-by:` references in TODO.md. The decompose output also suggests a `batch_strategy` (depth-first or breadth-first) — use this to inform dispatch ordering in the pulse. | ||||||||||
|
|
||||||||||
| A subtask without a brief is a knowledge loss. The parent task's rich context (from the original conversation) must flow down to every subtask. | ||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -186,7 +186,7 @@ When closing any issue, ALWAYS add a comment first explaining: (1) why you're cl | |||||||||||||||||||||
| - **`status:done` label or body says "completed"** → find the PR(s) that delivered the work, comment with links, then close. If no PR exists (pre-existing completed work), explain that in the comment. | ||||||||||||||||||||||
| - **`status:blocked` but blockers are resolved** (merged PR exists for each `blocked-by:` ref) → remove `status:blocked`, add `status:available`, comment explaining what unblocked it. It's now dispatchable this cycle. | ||||||||||||||||||||||
| - **Duplicate issues for the same task ID** (multiple open issues whose titles start with the same `tNNN:` prefix) → keep the one referenced by `ref:GH#` in TODO.md; close the others with a comment like "Duplicate of #NNN — closing in favour of the canonical issue." This happens when issue-sync-helper and a manual/agent creation race, or when a task ID is reused after a collision. Check TODO.md's `ref:GH#` to determine which is canonical. If neither is referenced, keep the older one. | ||||||||||||||||||||||
| - **Too large for one worker session** (multiple independent changes, 5+ checklist items, "audit all", "migrate everything") → create subtask issues, label parent `status:blocked` with `blocked-by:` refs to subtasks | ||||||||||||||||||||||
| - **Too large for one worker session** (multiple independent changes, 5+ checklist items, "audit all", "migrate everything") → auto-decompose using `task-decompose-helper.sh` (see "Task decomposition before dispatch" below), or manually create subtask issues. Label parent `status:blocked` with `blocked-by:` refs to subtasks | ||||||||||||||||||||||
| - **`status:queued` or `status:in-progress`** → likely being worked on (possibly on another machine). Check the `updatedAt` timestamp: if the issue was updated within the last 3 hours, skip it. If it's been 3+ hours with no open PR and no recent commits on a related branch, the worker likely died — relabel to `status:available`, unassign, and comment explaining the recovery. It's now dispatchable. | ||||||||||||||||||||||
| - **`status:available` or no status label** → dispatch a worker (see below) | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
|
|
@@ -221,16 +221,85 @@ The "Active Workers" section in the pre-fetched state includes a `struggle_ratio | |||||||||||||||||||||
| - `STRUGGLE_RATIO_THRESHOLD` — ratio above which to flag (default: 30) | ||||||||||||||||||||||
| - `STRUGGLE_MIN_ELAPSED_MINUTES` — minimum runtime before flagging (default: 30) | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| ### Task decomposition before dispatch (t1408.2) | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| Before dispatching a worker for an issue, classify the task to determine if it's too large for a single worker session. This catches over-scoped tasks before they waste a worker slot. | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| **When to classify:** For each dispatchable issue (after passing the skip checks below), run the classify step. Skip classification for issues that already have subtask issues (check if issues with `tNNN.1`, `tNNN.2` etc. exist in the title search). | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| **How to classify:** | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| ```bash | ||||||||||||||||||||||
| # Extract task description from the issue title/body | ||||||||||||||||||||||
| TASK_DESC="<issue title and first paragraph of body>" | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| # Classify — uses haiku-tier LLM call (~$0.001) | ||||||||||||||||||||||
| CLASSIFY_RESULT=$(/bin/bash ~/.aidevops/agents/scripts/task-decompose-helper.sh classify \ | ||||||||||||||||||||||
| --task "$TASK_DESC" --repo-path "$path" --quiet 2>/dev/null) || CLASSIFY_RESULT="" | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| # Parse result | ||||||||||||||||||||||
| TASK_KIND=$(echo "$CLASSIFY_RESULT" | jq -r '.kind // "atomic"' 2>/dev/null || echo "atomic") | ||||||||||||||||||||||
|
Comment on lines
+237
to
+241
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The bash example in this section uses
Suggested change
References
|
||||||||||||||||||||||
| ``` | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| **If atomic:** Dispatch the worker directly (unchanged flow — proceed to step 6 below). | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| **If composite:** Auto-decompose and create child tasks instead of dispatching: | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| ```bash | ||||||||||||||||||||||
| # Decompose into subtasks | ||||||||||||||||||||||
| DECOMPOSE_RESULT=$(/bin/bash ~/.aidevops/agents/scripts/task-decompose-helper.sh decompose \ | ||||||||||||||||||||||
| --task "$TASK_DESC" --repo-path "$path" \ | ||||||||||||||||||||||
| --depth 0 --max-depth "${DECOMPOSE_MAX_DEPTH:-3}" --quiet 2>/dev/null) || DECOMPOSE_RESULT="" | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| SUBTASK_COUNT=$(echo "$DECOMPOSE_RESULT" | jq '.subtasks | length' 2>/dev/null || echo 0) | ||||||||||||||||||||||
|
Comment on lines
+250
to
+254
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The bash example in this section uses
Suggested change
References
|
||||||||||||||||||||||
| ``` | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| If decomposition succeeds (`SUBTASK_COUNT >= 2`): | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| 1. For each subtask, create a child task using `claim-task-id.sh`: | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| ```bash | ||||||||||||||||||||||
| for i in $(seq 0 $((SUBTASK_COUNT - 1))); do | ||||||||||||||||||||||
| SUB_DESC=$(echo "$DECOMPOSE_RESULT" | jq -r ".subtasks[$i].description") | ||||||||||||||||||||||
| SUB_ESTIMATE=$(echo "$DECOMPOSE_RESULT" | jq -r ".subtasks[$i].estimate // \"~2h\"") | ||||||||||||||||||||||
| SUB_DEPS=$(echo "$DECOMPOSE_RESULT" | jq -r ".subtasks[$i].depends_on | map(\"blocked-by:${TASK_ID}.\" + tostring) | join(\" \")" 2>/dev/null || echo "") | ||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Suppressing stderr with
Suggested change
References
|
||||||||||||||||||||||
|
|
||||||||||||||||||||||
| # Claim child task ID | ||||||||||||||||||||||
| CHILD_OUTPUT=$(/bin/bash ~/.aidevops/agents/scripts/claim-task-id.sh \ | ||||||||||||||||||||||
| --repo-path "$path" --title "${TASK_ID}.${i+1}: $SUB_DESC" --parent "$TASK_ID") | ||||||||||||||||||||||
| CHILD_ID=$(echo "$CHILD_OUTPUT" | grep '^TASK_ID=' | cut -d= -f2) | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| # Add to TODO.md (planning-commit-helper handles commit+push) | ||||||||||||||||||||||
| # Format: - [ ] tNNN.N Description ~Nh blocked-by:tNNN.M ref:GH#NNN | ||||||||||||||||||||||
| done | ||||||||||||||||||||||
| ``` | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| 2. Label the parent issue `status:blocked` with a comment explaining the decomposition | ||||||||||||||||||||||
| 3. Create a brief for each child task from the parent brief + decomposition context | ||||||||||||||||||||||
| 4. The child tasks enter the normal dispatch queue — the next pulse cycle picks up the leaves (tasks with no unresolved `blocked-by:` refs) | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| **Depth limit:** `DECOMPOSE_MAX_DEPTH` env var (default: 3). Tasks at depth 3+ are always treated as atomic. This prevents infinite decomposition. | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| **Skip decomposition when:** | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| - The issue already has subtask issues (titles matching `tNNN.N:`) | ||||||||||||||||||||||
| - The issue body contains `skip-decompose` or `atomic` markers | ||||||||||||||||||||||
| - Classification fails (API unavailable) — default to atomic and dispatch directly | ||||||||||||||||||||||
| - The task is a bug fix, CI fix, or docs update (these are almost always atomic) | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| **Cost:** ~$0.001-0.005 per classify+decompose call (haiku tier). A single avoided over-scoped worker failure saves $0.50-5.00 in wasted compute. | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| ### Dispatch workers for open issues | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| For each dispatchable issue: | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| 1. Skip if a worker is already running for it locally (check `ps` output for the issue number) | ||||||||||||||||||||||
| 2. Skip if an open PR already exists for it (check PR list) | ||||||||||||||||||||||
| 3. Skip if the issue has `status:queued`, `status:in-progress`, or `status:in-review` labels — but only if the issue was updated within the last 3 hours. These labels indicate a worker is handling it (possibly on another machine). If the label is stale (3+ hours, no PR, no recent branch activity), the worker likely died — recover the issue: relabel to `status:available`, unassign, and comment explaining the recovery. It becomes dispatchable this cycle. | ||||||||||||||||||||||
| 4. Skip if the issue is assigned and was updated within the last 3 hours — someone is actively working on it. If assigned but stale (3+ hours, no PR), treat as abandoned: unassign and relabel to `status:available`. | ||||||||||||||||||||||
| 5. Read the issue body briefly — if it has `blocked-by:` references, check if those are resolved (merged PR exists). If not, skip it. | ||||||||||||||||||||||
| 6. **Task decomposition check (t1408):** Before dispatching, check if the task should be decomposed. Run `task-decompose-helper.sh has-subtasks <task-id>` — if it already has subtasks, dispatch the leaf subtasks instead. If not, the `/full-loop` worker will classify and decompose if needed (Step 0.8). For tasks that are clearly composite from the issue title (multiple independent features listed), consider pre-decomposing before dispatch to avoid wasting a worker session on classification. | ||||||||||||||||||||||
| 7. Dispatch: | ||||||||||||||||||||||
| 5.5. **Classify and decompose (t1408.2):** Run the task decomposition check described in "Task decomposition before dispatch" above. If the task is composite, create child tasks and skip direct dispatch. If atomic (or classification unavailable), proceed to dispatch. | ||||||||||||||||||||||
| 6. Dispatch: | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| ```bash | ||||||||||||||||||||||
| # Assign the issue to prevent duplicate work by other runners/humans | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The bash example in this section uses
2>/dev/nullto suppress errors fromclassifyandjqcommands. This practice hides valuable debugging information, such as API failures, malformed JSON, or missing dependencies, which goes against the repository's general rules. The||construct already handles command failures gracefully. Removing2>/dev/nullwill make this example script more robust and easier to debug without changing its behavior on success.References
2>/dev/nullin shell scripts. The||construct handles command failures, and leaving stderr visible is crucial for debugging issues like authentication failures, syntax errors, or missing dependencies.