loom-builder-complexity · git:20261011.dcb2b51 · 2026-10-11 · sha256 6d4d761d82356156
loom-builder-complexity git:20261011.dcb2b51A
Immutable. This exact content never changes and is served at /api/v1/blob/6d4d761d82356156.
---
name: loom-builder-complexity
description: "This document covers complexity assessment, issue decomposition, and scope management for the Builder role. For the core builder workflow, see `builder.md`."
---
<!-- loom-managed-skill -->
<!-- GENERATED FILE — DO NOT EDIT DIRECTLY.
Produced by `loom-daemon generate-agent-skills` from
defaults/.claude/commands/loom/builder-complexity.md (the same source Claude Code
reads as /loom:builder-complexity via .claude/commands/loom/). This is the
cross-vendor skill-discovery surface (.agents/skills/<name>/SKILL.md)
read natively by Codex, Kimi Code, Mistral Vibe, and Grok — see
runtime-adapters.md §5. To change this file, edit the source above
and re-run the generator; CI (`loom-daemon generate-agent-skills
--check`) fails if this file is stale. -->
# Builder: Complexity Assessment and Issue Decomposition
This document covers complexity assessment, issue decomposition, and scope management for the Builder role. For the core builder workflow, see `builder.md`.
<!-- toc:begin generated by scripts/check-doc-tocs.sh --fix; do not edit by hand -->
**Contents**
- [⚠️ `--body @path` Does NOT Expand — It Posts the Literal String](#---body-path-does-not-expand--it-posts-the-literal-string)
- [Never Abandon Work](#never-abandon-work)
- [Auditing Before Decomposition](#auditing-before-decomposition)
- [Assessing Complexity Before Claiming](#assessing-complexity-before-claiming)
- [Scope Management](#scope-management)
<!-- toc:end -->
## ⚠️ `--body @path` Does NOT Expand — It Posts the Literal String
If you post a comment via `gh issue comment` / `gh pr comment` / `gh api ...
comments` from a scratch file, `--body @path` (and `gh api -f body=@path`)
posts the literal string `@path`, not the file's contents. **Full pitfall,
incident citation, and fixes**:
[`comment-body-literal-path.md`](../loom-comment-body-literal-path/SKILL.md).
## Never Abandon Work
**You must NEVER stop work on a claimed issue without creating a clear path forward.**
When you claim an issue with `loom:building`, you are committing to ONE of these outcomes:
1. **Create a PR** - Complete the work and submit for review
2. **Decompose into sub-issues** - Break complex work into smaller, claimable issues
3. **Mark as blocked** - Document the blocker and add `loom:blocked` label
**NEVER do this**:
- Claim an issue, realize it's complex, then abandon it without explanation
- Leave an issue with `loom:building` label but no PR and no sub-issues
- Stop work because "it's too hard" without decomposing or documenting why
> **File issues with `./.loom/scripts/create-issue.sh`, never a bare `gh issue create` (#5047).**
> `gh issue create` is GraphQL-backed and dies outright once the shared GraphQL pool exhausts —
> while the independent REST pool sits ~99% unused. The script takes the same flags (`--title`,
> `--body`/`--body-file`, repeatable `--label`, `--repo`) and prints the same issue URL, but falls
> back to a single REST POST that applies labels **atomically with creation**. Recipe and
> rationale: `.loom/docs/gh-issue-create-rest-fallback.md`.
> (`loom-daemon forge issue create` is a byte-identical `gh` passthrough — NOT a fallback.)
> This matters most exactly here: a decomposition burst files 2-5 issues in a row, so it is the
> single likeliest place in a Builder run to meet an exhausted GraphQL pool mid-sequence.
### If You Discover an Issue Is Too Complex
When you claim an issue and realize mid-work it requires >6 hours or touches >8 files:
**DO THIS** (create path forward):
```bash
# 1. Create 2-5 focused sub-issues, each born at loom:triage -- applied
# atomically at creation (#5047), never a follow-up `gh issue edit
# --add-label`. A separate Curator pass produces loom:curated, and a
# human adds loom:issue (that Curator pass does, for an inherited-star
# child); do NOT add loom:issue yourself to a sub-issue you just created
# -- that would skip both Curator review and the approval gate. --parent writes the loom:parent marker + native
# sub-issue link, and a starred parent's star is inherited (#10012).
./.loom/scripts/create-issue.sh --parent 812 --title "Part 1: Core functionality" --body "..." --label "loom:triage"
./.loom/scripts/create-issue.sh --parent 812 --title "Part 2: Edge cases" --body "..." --label "loom:triage"
# ... create remaining sub-issues ...
# 2. Park the parent on its children: one body park record per child, then
# building -> blocked. NEVER close a parent yourself; humans close it once
# children are filed, and loom:blocked is the terminal state.
loom-daemon park-record apply --issue 812 --blocked-by XXX,YYY,ZZZ --by builder --remove-label loom:building
# Then exit and let the Curator/sweep pipeline pick up each sub-issue.
```
**DON'T DO THIS** (abandon without path forward):
```bash
# WRONG - Just stopping work
# (leaves issue stuck with loom:building, no explanation, no sub-issues)
```
> **`loom:blocked` is the terminal state for a decomposition — not
> `loom:operator-only` (#5819).** Size is not a routing signal: "this is too
> big for one PR" is a `loom:blocked` parent with children filed, and the
> pipeline picks it up again on its own. Reserve `loom:operator-only` for a
> PO-level decision or a human-hands step (#10001; rule: `curator.md` →
> "Applying `loom:operator-only`"), never "requires judgement". When you do
> apply it, **never apply it alone** — add exactly one sub-kind in that command
> (`loom:operator-blocked` / `loom:operator-mechanical` /
> `loom:operator-decision`), e.g.
> `loom-daemon operator-decision apply 812 --input d.json --also-label loom:operator-only --remove-label loom:building`.
> Being unsure which sub-kind fits means the analysis isn't finished — it is
> **not** a reason to default to `loom:operator-decision` (#5826). Full rule,
> including the machine-readable `Blocked by #N` line required with
> `loom:operator-blocked`: `builder.md` → "Applying `loom:operator-only`".
### Sub-issue labeling (mirrors curator.md decomposition rule)
When you create sub-issues during decomposition:
1. **Label each sub-issue `loom:triage` only.** Do NOT apply `loom:issue`, `loom:curated`, or `loom:building` to a sub-issue you just created -- even if your decomposition includes acceptance criteria, file references, and scope guards.
2. **Do NOT self-claim a sub-issue you just created in the same session.** A separate Curator pass must independently review it (-> `loom:curated`), and a human must promote it (-> `loom:issue`; for an inherited-star child the Curator pass does, `curator.md` Priority 0), before any Builder claims it.
3. **Update the parent issue body or add a comment** with a "Decomposed sub-issues" section linking each child.
4. **Do not close the parent yourself if you cannot complete it.** Mark `loom:blocked` with a comment explaining the decomposition; humans close once children are filed.
### Why this matters
A dedicated Curator pass after decomposition catches:
- Acceptance-criteria gaps the decomposer didn't surface
- file:line citations that drift between decomposer-read time and builder-run time
- Sub-issue dependencies the decomposer missed
- Scope-guard sharpening (LOC limits, out-of-scope footnotes)
When skipped, the next Builder hits these issues at implementation time -- usually as a scope-guard trigger or a Doctor cycle -- which is far more expensive than catching at curate time.
**Scope note**: This rule applies *only* to sub-issues created during builder-side decomposition. The Builder's normal claim flow for human-approved `loom:issue` issues (remove `loom:issue`, add `loom:building`) is unchanged.
### Related: Curator-side decomposition
The Curator role enforces the same rule from its side (see `defaults/.claude/commands/loom/curator.md` -> "Decomposing Oversized Issues" section, added in #3266 / PR #3272).
### Decomposition Criteria
**Be ambitious - try to complete issues in a single PR when reasonable.**
**Only decompose if MULTIPLE of these are true**:
- Estimated effort > 6 hours
- Touches > 8 files across multiple components
- Requires > 400 lines of new code
- Has multiple distinct phases with natural boundaries
- Mixes unrelated concerns (e.g., "add feature AND refactor unrelated module")
- Multiple developers could work in parallel on different parts
**Do NOT decompose if**:
- Effort < 4 hours (complete it in one PR)
- Focused change even if it touches several files
- Breaking it up would create tight coupling/dependencies
- The phases are tightly coupled and must ship together
### Why This Matters
**Abandoned issues waste everyone's time**:
- Issue is invisible to other Builders (locked with `loom:building`)
- No progress made, no PR created
- Requires manual intervention to unclaim
- Blocks the workflow and frustrates users
**Decomposition enables progress**:
- Multiple Builders can work in parallel
- Each sub-issue is completable in one iteration
- Work starts immediately instead of waiting
- Clear incremental progress toward the goal
## Auditing Before Decomposition
**CRITICAL**: Before decomposing a large issue into sub-issues, audit the codebase to verify what's actually missing.
### Why Audit First?
**The Problem**:
- Issue descriptions may be outdated
- Features may have been implemented without closing the issue
- Mature codebases often have more functionality than issues suggest
**Without audit**: Create duplicate issues for complete features
**With audit**: Create focused issues for genuine gaps only
### Audit Checklist
Before decomposing an issue into sub-issues:
1. **Search for related code**:
```bash
# Search for feature keywords
grep -r "TRANSACTION\|BEGIN\|COMMIT" src/
# Find relevant files
find . -name "*constraint*.rs" -o -name "*transaction*.rs"
```
2. **Check for implementations**:
- Look for executor/handler files related to the feature
- Check storage layer and data models
- Review parser or API definitions
3. **Verify with tests**:
```bash
# Find related tests
find . -name "*_test*" | xargs grep -l "constraint\|transaction"
# Count test coverage for a feature
grep -c "fn test" tests/constraint_tests.rs
```
4. **Compare findings to issue requirements**:
- **Fully implemented** (verified — you can cite files/lines/tests) -> this is the "Already covered" close case from `builder.md` → "Issues Are Suggestions". Comment the evidence as your rationale, then **close the issue** (`gh issue close <N> --reason "not planned"`); do NOT create duplicate sub-issues. **Under `/loom:sweep` orchestration**, prefer the `.no-changes-needed` marker instead of closing directly so orchestration finalizes the lifecycle (see `builder.md` → "Signaling No Changes Needed"). If the "already implemented" call is **ambiguous** (you cannot fully verify, or it hides a still-pending human decision), do NOT close — route it to `loom:blocked` with a comment per the guardrails.
- **Partially implemented** -> Create sub-issues only for missing parts
- **Not implemented** -> Proceed with decomposition as planned
### Decision Tree
```
Large issue requiring decomposition
|
1. AUDIT: Search codebase for existing implementations
|
2. ASSESS:
|-- Fully implemented (verified)? -> Close with evidence as rationale ("Already covered"); under /loom:sweep use the .no-changes-needed marker. Ambiguous -> loom:blocked with a comment.
|-- Partially implemented? -> Create sub-issues for gaps only
+-- Not implemented? -> Proceed with decomposition
|
3. DECOMPOSE: Create focused sub-issues for genuine gaps
```
### Example: Good Audit Process
```bash
# Issue #341: "Implement E141 Constraints"
# Step 1: Search for constraint enforcement
$ grep -rn "NOT NULL.*constraint\|primary_key\|unique_constraint" src/
# Findings:
# - insert.rs:119-127: NOT NULL enforcement exists
# - insert.rs:129-171: PRIMARY KEY enforcement exists
# - update.rs:173-213: UNIQUE constraint enforcement exists
# - update.rs:215-232: CHECK constraint enforcement exists
# Step 2: Check test coverage
$ find . -name "*_test*" | xargs grep -l constraint
# - tests/constraint_tests.rs (exists)
# - tests/insert_tests.rs (NOT NULL tests)
# Step 3: Compare to issue requirements
# Issue claims: "NOT NULL not enforced, PRIMARY KEY missing, UNIQUE missing"
# Audit shows: All features fully implemented with tests
# Step 4: Decision
# -> Comment the evidence above as the rationale, then CLOSE issue #341 as
# "Already covered" (gh issue close 341 --reason "not planned"). Under
# /loom:sweep, write a .no-changes-needed marker instead and let orchestration
# finalize. If the "already implemented" call is ambiguous, use loom:blocked + a comment.
# -> Do NOT create sub-issues (would be duplicates)
# -> Create separate issue for actual gaps: "Add SQLSTATE codes to constraint errors"
```
### Example: Bad Process (Without Audit)
```bash
# Issue #341: "Implement E141 Constraints"
# WRONG: Skip straight to decomposition without checking
./.loom/scripts/create-issue.sh --title "[Parent #341] Part 1: Implement NOT NULL"
./.loom/scripts/create-issue.sh --title "[Parent #341] Part 2: Implement PRIMARY KEY"
./.loom/scripts/create-issue.sh --title "[Parent #341] Part 3: Implement UNIQUE"
# ... creates 6 duplicate issues for already-complete features
# Result: 6 issues created, all later closed as duplicates
# Wasted effort for Builder, Curator, and Guide roles
```
### Why This Matters
**Real-world impact without audit**:
- 10 duplicate issues created in a single decomposition session
- 59% of open issues were duplicates
- Curator time wasted enhancing issues for complete features
- Guide time wasted triaging and closing duplicates
- Risk of "reimplementing" existing features
**With audit**:
- Create only issues that need real work
- Clean backlog with legitimate work items
- Focus on genuine gaps, not phantom requirements
## Assessing Complexity Before Claiming
**IMPORTANT**: Always assess complexity BEFORE claiming an issue. Never mark an issue as `loom:building` unless you're committed to completing it.
### Why Assess First?
**The Problem with Claim-First-Assess-Later**:
- Issue locked with `loom:building` (invisible to other Builders)
- No PR created if you abandon it (looks stalled)
- Requires manual intervention to unclaim
- Wastes your time reading/planning complex tasks
- Blocks other Builders from finding work
**Better Approach**: Read -> Assess -> Decide -> (Maybe) Claim
### Complexity Assessment Checklist
Before claiming an issue, estimate the work required:
**Time Estimate Guidelines**:
- Count acceptance criteria (each = 30-60 minutes)
- Count files to modify (each = 15-30 minutes)
- Add testing time (= 20-30% of implementation)
- Consider documentation updates
**Complexity Indicators**:
- **Simple** (< 4 hours): Single component, clear path, <= 6 criteria
- **Medium** (4-6 hours): Multiple components, straightforward integration - still claimable
- **Complex** (6-12 hours): Architectural changes, many files - consider decomposition
- **Intractable** (> 12 hours or unclear): Missing requirements, external dependencies
### Decision Tree
**If Simple or Medium (< 6 hours, clear path)**:
1. Claim immediately: `gh issue edit <number> --remove-label "loom:issue" --add-label "loom:building"`
2. Create worktree: `./.loom/scripts/worktree.sh <number>`
3. Implement -> Test -> PR
4. Be ambitious - complete the full issue in one PR
**If Complex (6-12 hours, clear path)**:
1. Assess carefully - can you complete it in one focused session?
2. If YES: Claim and implement (larger PRs are fine if cohesive)
3. If NO: Break down into 2-4 sub-issues, mark parent `loom:blocked` with an explanation (humans close)
4. Prefer completing work over creating more issues
**If Intractable (> 12 hours or unclear)**:
1. DO NOT CLAIM
2. Comment explaining the blocker
3. Mark as `loom:blocked`
4. Pick next available issue
### Issue Decomposition Pattern
**Decomposition should be the exception, not the rule.** Most issues should be completed in a single PR. Only decompose when the issue genuinely has independent, parallelizable parts that would benefit from separate implementation.
**Step 1: Analyze the Work**
- Identify natural phases (infrastructure -> integration -> polish)
- Find component boundaries (frontend -> backend -> tests)
- Look for MVP opportunities (simple version first)
**Step 2: Create Sub-Issues**
```bash
# Create focused sub-issues
./.loom/scripts/create-issue.sh --title "Phase 1: <component> foundation" --body "$(cat <<'EOF'
Parent Issue: #<parent-number>
## Scope
[Specific deliverable for this phase]
## Acceptance Criteria
- [ ] Criterion 1
- [ ] Criterion 2
## Dependencies
- None (this is the foundation)
Estimated: 1-2 hours
EOF
)"
./.loom/scripts/create-issue.sh --title "Phase 2: <component> integration" --body "$(cat <<'EOF'
Parent Issue: #<parent-number>
## Scope
[Specific integration work]
## Acceptance Criteria
- [ ] Criterion 1
- [ ] Criterion 2
## Dependencies
- [ ] #<phase1-number>: Phase 1 must be complete
Estimated: 2-3 hours
EOF
)"
```
**Step 3: Mark Parent Blocked (don't close a decomposition-parent yourself)**
Park-record the children in the parent's body (step 2 of the recipe above), then move the parent to `loom:blocked`. A
freshly-decomposed **parent** is a tracking issue for its children, so it is **not**
a close candidate — a human (or Curator, per `curator.md` item 4) closes it once the
children are filed and curated. This is the decomposition-parent exception, not a
blanket rule: for a **non-parent** issue you verify is already implemented, close it
with a rationale per `builder.md` → "Issues Are Suggestions" (or the `.no-changes-needed`
marker under `/loom:sweep`).
```bash
loom-daemon park-record apply --issue <parent-number> --blocked-by <phase1>,<phase2>,<phase3> --by builder --remove-label loom:building
```
### Real-World Example
**Original Issue #524**: "Track agent activity in local database"
- **Assessment**: 10-14 hours, multiple independent components, clear technical approach
- **Decision**: Complex with parallelizable parts -> decompose
**Decomposition**:
```bash
# Phase 1: Infrastructure
./.loom/scripts/create-issue.sh --title "Create JSON activity log structure and helper functions"
# -> Issue #534 (1-2 hours)
# Phase 2: Integration
./.loom/scripts/create-issue.sh --title "Integrate activity logging into /builder and /judge"
# -> Issue #535 (2-3 hours, depends on #534)
# Phase 3: Querying
./.loom/scripts/create-issue.sh --title "Add activity querying to /loom heuristic"
# -> Issue #536 (1-2 hours, depends on #535)
# Park the parent on its phases (body park records + label, as above)
loom-daemon park-record apply --issue 524 --blocked-by 534,535,536 --by builder --remove-label loom:building
```
**Benefits**:
- Each sub-issue is completable in one iteration
- Can implement MVP first, enhance later
- Multiple builders can work in parallel
- Incremental value delivery
### Complexity Assessment Examples
**Example 1: Simple (Claim It)**
```
Issue: "Fix typo in CLAUDE.md line 42"
Assessment:
- 1 file, 1 line changed
- No acceptance criteria (obvious fix)
- No dependencies
- Estimated: 5 minutes
-> Decision: CLAIM immediately
```
**Example 2: Medium (Claim It)**
```
Issue: "Add dark mode toggle to settings panel"
Assessment:
- 5 files affected (~250 LOC)
- 6 acceptance criteria
- No dependencies
- Estimated: 4 hours
-> Decision: CLAIM and implement in one PR
```
**Example 3: Larger but Cohesive (Still Claim It)**
```
Issue: "Add user preferences panel with theme, notifications, and language settings"
Assessment:
- 8 files affected (~400 LOC)
- 8 acceptance criteria
- All parts are tightly coupled
- Estimated: 5-6 hours
-> Decision: CLAIM - it's one cohesive feature, implement together
```
**Example 4: Complex with Independent Parts (Decompose It)**
```
Issue: "Migrate state management to Redux"
Assessment:
- 15+ files (~800 LOC)
- 12 acceptance criteria
- External dependency (Redux)
- Has independent modules that could be migrated separately
- Estimated: 2-3 days
-> Decision: DECOMPOSE into phases (each module can be migrated independently)
```
**Example 5: Intractable (Block It)**
```
Issue: "Improve performance"
Assessment:
- Vague requirements
- No acceptance criteria
- Unclear what to optimize
-> Decision: BLOCK, request clarification
```
### Key Principles
**Be Ambitious - Complete Work in One PR**:
- Default to implementing the full issue, not breaking it down
- Think: "Can I complete this?" not "How can I break this down?"
- Larger PRs are fine if the changes are cohesive and well-tested
- Only decompose when there are genuinely independent, parallelizable parts
**Prevent Orphaned Issues**:
- Never claim unless you're ready to start immediately
- If you discover mid-work it's too complex, mark `loom:blocked` with explanation
- Other builders can see available work in the backlog
**When to Enable Parallel Work**:
- Only decompose when multiple builders could genuinely work simultaneously
- Don't create artificial phases just to have smaller issues
- A single developer completing one larger issue is often faster than coordination overhead
## Scope Management
**PAUSE immediately when you discover work outside your current issue's scope.**
### When to Pause and Create an Issue
Ask yourself: "Is this required to complete my assigned issue?"
**If NO, stop and create an issue for:**
- Missing infrastructure (test frameworks, build tools, CI setup)
- Technical debt needing refactoring
- Missing features or improvements
- Documentation gaps
- Architecture changes or design improvements
**If YES, continue only if:**
- It's a prerequisite for your issue (e.g., can't write tests without test framework)
- It's a bug blocking your work
- It's explicitly mentioned in the issue requirements
### How to Handle Out-of-Scope Work
1. **PAUSE** - Stop implementing the out-of-scope work immediately
2. **ASSESS** - Determine if it's required for your current issue
3. **CREATE ISSUE** - If separate, create an unlabeled issue NOW (examples below)
4. **RESUME** - Return to your original task
5. **REFERENCE** - Mention the new issue in your PR if relevant
### When NOT to Create Issues
Don't create issues for:
- Minor code style fixes (just fix them in your PR)
- Already tracked TODOs
- Vague "nice to haves" without clear value
- Improvements you've already completed (document them in your PR instead)
### Example: Out-of-Scope Discovery
```bash
# While implementing feature, you discover missing test framework
# PAUSE: Stop trying to implement it
# CREATE: Make an issue for it
./.loom/scripts/create-issue.sh --title "Add Vitest testing framework for frontend unit tests" --body "$(cat <<'EOF'
## Problem
While working on #38, discovered we cannot write unit tests for the state management refactor because no test framework is configured for the frontend.
## Requirements
- Add Vitest as dev dependency
- Configure vitest.config.ts
- Add test scripts to package.json
- Create example test to verify setup
## Context
Discovered during #38 implementation. Required for testing state management but separate concern from the refactor itself.
EOF
)"
# RESUME: Return to #38 implementation
```