Bundle Tasks Skill — Meta-Task Bundling (Task 110)
Use this skill when the Manager wants to execute 4–6 small related tasks together instead of sequentially. It eliminates the backlog → in-progress → qa → completed round-trip overhead by bundling them into one branch, one Factual Git Diff, and one all-or-nothing QA gate.
When to Use
- Manager says: "bundle tasks 1, 2, 5, 10, 15, 20", "create a meta-task from 12 15 20", "combine these polish tasks", or any note about "meta-task", "bundle", "supersede", "archive and bundle"
- Tasks are small, same stack/domain (e.g., all
android-kotlin, all react-vite, all docs), and would be inefficient to run one-by-one
- You are in any project that has the
mcp-context-server MCP server — the bundler is available as the bundle_tasks MCP tool (pure MCP, Task 155)
Do NOT use for: large refactors, tasks with conflicting files that would cause merge conflicts in one diff, or tasks >6 without explicit --force.
Core Contract (Deterministic, No LLM, No Hallucination)
- Verbatim Preservation: Every source
## Goal, ## Manager's Notes / ## Blueprint Reference, ## Acceptance Criteria (including multi-line continuations and indented sub-bullets), ## Local TODOs, ## Risk & Rollback is copied verbatim into ### Source Task XX blocks. No summarization. The ## Bundled Checklist is derived by prefixing each source AC root bullet with [XX] and preserving all indented continuation lines.
- Single QA Gate: All bundled criteria are
all-or-nothing. If any line fails QA, the entire META is QA_REJECTED.
- Archive, Not Purge (with Transactional Rollback): Sources are moved via
git mv to tasks/archive/ with **Superseded-By:** <META_ID>-<slug> until META is completed. History stays reachable via git log --follow. If ANY archive operation fails, ALL previously archived files are rolled back to their original locations, the META file is deleted, and the operation aborts cleanly.
- Guardrails:
MAX_BUNDLE_SIZE=6 (reject >6 without --force), combined LOC >400 warning, missing-ID and duplicate-ID checks (hard halt on duplicate active IDs), stack conflict detection (warn or require --force), SHA verbatim checksum validation, atomic Next-ID creation with retry loop for concurrent safety.
Invocation — Pure MCP Tool (Task 155)
The mcp-context-server/server.py:bundle_tasks tool is fully self-contained — it does NOT require scripts/bundle-tasks.py. All helpers (kebab_case, discover_next_id, find_task_file, extract_section, build_meta_content, git_mv_or_fallback, patch_archived_file) are inlined inside the MCP tool function. Invoke via the Hands' MCP interface:
{
"tool": "bundle_tasks",
"arguments": {
"task_ids": ["12", "15", "20"],
"title": "android-polish-bundle",
"dry_run": true,
"force": false
}
}
Tool name: bundle_tasks on mcp-context-server (custom_context FastMCP server). It validates IDs, discovers NEXT_ID, slugifies title, generates META with verbatim preservation, and auto-archives sources. Dry-run prints preview without file creation. Pure MCP — no external script dependency.
What Happens (Deterministic Steps)
- Validate IDs: Search
tasks/backlog/ tasks/in-progress/ tasks/qa/ tasks/completed/ (active only, tasks/archive/ excluded per task-generator duplicate-ID contract) for each <id>-*.md. HALT if any missing; note if found in archive (already superseded). Reject non-numeric IDs.
- Discover NEXT_ID:
find tasks -type f -name "*.md" | grep -Eo '^[0-9]+' | sort -n | tail -1 | awk '{print $1+1}' across ALL dirs including archive (no collision). Zero-padded 02d for <100, raw for ≥100.
- Slugify Title:
title → kebab-case (android Polish_Bundle → android-polish-bundle). Output file: tasks/backlog/<NEXT_ID>-<slug>.md.
- Verbatim Extraction: For each source, extract
## Goal, ## Manager's Notes/## Blueprint Reference, ## Acceptance Criteria, ## Local TODOs, ## Risk & Rollback verbatim via regex ^## Heading$(.*?)(?=^## |\n---\s*\n|\Z). No summarization.
- Generate META File: Canonical task template +
**Supersedes:** [12, 15, 20] + **Meta:** true + **Created:** + per-source appendix ### Source Task XX: Title + ## Source Bundles (Verbatim Preservation) + ## Bundled Checklist (All-or-Nothing) (every source AC line prefixed [XX], single QA gate) + aggregated ## Local TODOs ([XX]-prefixed) + guardrail notes (LOC warning if >400).
- Auto-Archive (unless dry_run):
git mv <src> tasks/archive/<src> (fallback to mv + git add for untracked) then patch archived file: **File:** → tasks/archive/<file>, **Status:** superseded, add **Superseded-By:** <META_ID>-<slug> + **Superseded-At:** YYYY-MM-DD, inject superseded footer before ## Execution Log. History remains reachable: git log --oneline --follow -- tasks/archive/<file> — never git rm until META is completed.
- Kanban: META follows normal
tasks/backlog/<META> → tasks/in-progress/<META> → tasks/qa/<META> → tasks/completed/<META> with one injected Factual Git Diff. QA is all-or-nothing.
Guardrails (Hard Stops & Warnings)
- Cap:
MAX_BUNDLE_SIZE=6 — rejects >6 without --force (mega-diff prevention). Use --force to override.
- Diff-size: Warns if combined source LOC >400 (
> ⚠️ 400 in notes) — "consider split".
- Missing / Duplicate / Collision: Missing IDs →
❌ Missing tasks; duplicate active IDs → hard halt (returns None, exits with error); NEXT_ID collision → atomic creation with retry loop (up to 5 re-discoveries).
- Stack Conflict (M1): Auto-detects stack from content (android, react, fastapi, spring, ios, go). If tasks have conflicting stacks → requires
--force to proceed.
- Verbatim Checksum (M2): After META generation, verifies every AC line from source tasks appears in the META. Fails if any text was dropped.
- Archive-only: Sources go to
tasks/archive/ via git mv only. Purge (git rm) is blocked until META is completed. On ANY archive failure: transactional rollback restores all archived files to original locations, deletes META, exits with clear error.
- Unicode/Persian Slugs (B4):
_kebab_case() normalizes via NFKD and preserves Persian/Arabic characters (\u0600-\u06FF). Persian titles produce valid slugs like تست-باندل instead of losing all characters.
Verification (Must Pass Before QA)
# via MCP (pure MCP, Task 155):
bundle_tasks(task_ids=["12","15","20"], title="test-bundle", dry_run=true)
# then after real bundle (if not dry_run):
lint_task_file tasks/backlog/<NEXT_ID>-<slug>.md
lint_task_file tasks/archive/12-*.md
git log --oneline --follow -- tasks/archive/12-*.md | head
py_compile: python3 -m py_compile mcp-context-server/server.py
- META must contain
**Supersedes:** + every source ### Source Task block + ## Bundled Checklist with [XX] prefixes.
lint_task_file must pass on META (fixed --- → ---\n\n blank-line; **Type:** allows feature + Meta:true and also meta) and on both archived files (**Status:** superseded is allowed; **File:** matches archive path).
git log --follow must show the source's history through the rename.
Skill Loading
Load this skill when you handle bundling:
skill("bundle-tasks")
If you also need ID discovery or template generation, also load task-generator (this skill complements it, not replaces it). For lint, load task-lint; for context gathering before bundling, load code-search to ensure sources are in the expected Kanban dirs.
Rollback
If META is abandoned or fails QA permanently:
git mv tasks/archive/12-*.md tasks/backlog/12-*.md
git mv tasks/archive/15-*.md tasks/backlog/15-*.md
rm tasks/backlog/<NEXT_ID>-<slug>.md # or: git mv tasks/backlog/<NEXT_ID>-<slug>.md tasks/archive/<NEXT_ID>-<slug>.md # mark abandoned
No HQ code beyond the bundler is affected. If META already reached tasks/completed/, its archived sources stay in tasks/archive/ permanently (they are superseded, not purged).
Reference
- MCP:
mcp-context-server/server.py:bundle_tasks (self-contained, verbatim helpers inlined, task_ids: list[str], title: str, dry_run, force, path-traversal safe, cap 6)
- Docs:
AGENTS.md ## 🛑 META-TASK BUNDLE LIFECYCLE + **Bundle Tool:**, CHANGELOG.md [Unreleased]
- Lint:
mcp-lint-server/server.py Type regex now ...|meta
- Registry:
prompts/fragments/07-agent_skills_registry.md lists bundle-tasks
1---2name: bundle-tasks3description: Deterministic meta-task bundling — bundles 2–6 small related tasks into one META for unified execution with verbatim preservation and auto-archive. Exposed as the bundle_tasks MCP tool (Task 155).4---56# Bundle Tasks Skill — Meta-Task Bundling (Task 110)78Use this skill when the Manager wants to execute 4–6 small related tasks together instead of sequentially. It eliminates the `backlog → in-progress → qa → completed` round-trip overhead by bundling them into one branch, one `Factual Git Diff`, and one all-or-nothing QA gate.910## When to Use1112- Manager says: "bundle tasks 1, 2, 5, 10, 15, 20", "create a meta-task from 12 15 20", "combine these polish tasks", or any note about "meta-task", "bundle", "supersede", "archive and bundle"13- Tasks are small, same stack/domain (e.g., all `android-kotlin`, all `react-vite`, all docs), and would be inefficient to run one-by-one14- You are in any project that has the `mcp-context-server` MCP server — the bundler is available as the `bundle_tasks` MCP tool (pure MCP, Task 155)1516**Do NOT use for:** large refactors, tasks with conflicting files that would cause merge conflicts in one diff, or tasks >6 without explicit `--force`.1718## Core Contract (Deterministic, No LLM, No Hallucination)19201. **Verbatim Preservation:** Every source `## Goal`, `## Manager's Notes` / `## Blueprint Reference`, `## Acceptance Criteria` (including multi-line continuations and indented sub-bullets), `## Local TODOs`, `## Risk & Rollback` is copied verbatim into `### Source Task XX` blocks. No summarization. The `## Bundled Checklist` is derived by prefixing each source AC root bullet with `[XX]` and preserving all indented continuation lines.212. **Single QA Gate:** All bundled criteria are `all-or-nothing`. If any line fails QA, the entire META is `QA_REJECTED`.223. **Archive, Not Purge (with Transactional Rollback):** Sources are moved via `git mv` to `tasks/archive/` with `**Superseded-By:** <META_ID>-<slug>` until META is `completed`. History stays reachable via `git log --follow`. If ANY archive operation fails, ALL previously archived files are rolled back to their original locations, the META file is deleted, and the operation aborts cleanly.234. **Guardrails:** `MAX_BUNDLE_SIZE=6` (reject >6 without `--force`), combined LOC >400 warning, missing-ID and duplicate-ID checks (hard halt on duplicate active IDs), stack conflict detection (warn or require `--force`), SHA verbatim checksum validation, atomic Next-ID creation with retry loop for concurrent safety.2425## Invocation — Pure MCP Tool (Task 155)2627The `mcp-context-server/server.py:bundle_tasks` tool is **fully self-contained** — it does NOT require `scripts/bundle-tasks.py`. All helpers (kebab_case, discover_next_id, find_task_file, extract_section, build_meta_content, git_mv_or_fallback, patch_archived_file) are inlined inside the MCP tool function. Invoke via the Hands' MCP interface:2829```json30{31 "tool": "bundle_tasks",32 "arguments": {33 "task_ids": ["12", "15", "20"],34 "title": "android-polish-bundle",35 "dry_run": true,36 "force": false37 }38}39```4041**Tool name:** `bundle_tasks` on `mcp-context-server` (`custom_context` FastMCP server). It validates IDs, discovers NEXT_ID, slugifies title, generates META with verbatim preservation, and auto-archives sources. Dry-run prints preview without file creation. Pure MCP — no external script dependency.4243## What Happens (Deterministic Steps)44451. **Validate IDs:** Search `tasks/backlog/ tasks/in-progress/ tasks/qa/ tasks/completed/` (active only, `tasks/archive/` excluded per `task-generator` duplicate-ID contract) for each `<id>-*.md`. HALT if any missing; note if found in archive (already superseded). Reject non-numeric IDs.462. **Discover NEXT_ID:** `find tasks -type f -name "*.md" | grep -Eo '^[0-9]+' | sort -n | tail -1 | awk '{print $1+1}'` across **ALL** dirs including `archive` (no collision). Zero-padded `02d` for <100, raw for ≥100.473. **Slugify Title:** `title` → kebab-case (`android Polish_Bundle` → `android-polish-bundle`). Output file: `tasks/backlog/<NEXT_ID>-<slug>.md`.484. **Verbatim Extraction:** For each source, extract `## Goal`, `## Manager's Notes`/`## Blueprint Reference`, `## Acceptance Criteria`, `## Local TODOs`, `## Risk & Rollback` verbatim via regex `^## Heading$(.*?)(?=^## |\n---\s*\n|\Z)`. No summarization.495. **Generate META File:** Canonical task template + `**Supersedes:** [12, 15, 20]` + `**Meta:** true` + `**Created:**` + per-source appendix `### Source Task XX: Title` + `## Source Bundles (Verbatim Preservation)` + `## Bundled Checklist (All-or-Nothing)` (every source AC line prefixed `[XX]`, single QA gate) + aggregated `## Local TODOs` (`[XX]`-prefixed) + guardrail notes (LOC warning if >400).506. **Auto-Archive (unless dry_run):** `git mv <src> tasks/archive/<src>` (fallback to `mv` + `git add` for untracked) then patch archived file: `**File:**` → `tasks/archive/<file>`, `**Status:** superseded`, add `**Superseded-By:** <META_ID>-<slug>` + `**Superseded-At:** YYYY-MM-DD`, inject superseded footer before `## Execution Log`. History remains reachable: `git log --oneline --follow -- tasks/archive/<file>` — **never** `git rm` until META is `completed`.517. **Kanban:** META follows normal `tasks/backlog/<META>` → `tasks/in-progress/<META>` → `tasks/qa/<META>` → `tasks/completed/<META>` with one injected `Factual Git Diff`. QA is all-or-nothing.5253## Guardrails (Hard Stops & Warnings)5455- **Cap:** `MAX_BUNDLE_SIZE=6` — rejects >6 without `--force` (mega-diff prevention). Use `--force` to override.56- **Diff-size:** Warns if combined source LOC >400 (`> ⚠️ 400` in notes) — "consider split".57- **Missing / Duplicate / Collision:** Missing IDs → `❌ Missing tasks`; duplicate active IDs → **hard halt** (returns None, exits with error); `NEXT_ID` collision → atomic creation with retry loop (up to 5 re-discoveries).58- **Stack Conflict (M1):** Auto-detects stack from content (android, react, fastapi, spring, ios, go). If tasks have conflicting stacks → requires `--force` to proceed.59- **Verbatim Checksum (M2):** After META generation, verifies every AC line from source tasks appears in the META. Fails if any text was dropped.60- **Archive-only:** Sources go to `tasks/archive/` via `git mv` only. Purge (`git rm`) is blocked until META is `completed`. On ANY archive failure: **transactional rollback** restores all archived files to original locations, deletes META, exits with clear error.61- **Unicode/Persian Slugs (B4):** `_kebab_case()` normalizes via NFKD and preserves Persian/Arabic characters (\u0600-\u06FF). Persian titles produce valid slugs like `تست-باندل` instead of losing all characters.6263## Verification (Must Pass Before QA)6465```bash66# via MCP (pure MCP, Task 155):67bundle_tasks(task_ids=["12","15","20"], title="test-bundle", dry_run=true)6869# then after real bundle (if not dry_run):70lint_task_file tasks/backlog/<NEXT_ID>-<slug>.md71lint_task_file tasks/archive/12-*.md72git log --oneline --follow -- tasks/archive/12-*.md | head73py_compile: python3 -m py_compile mcp-context-server/server.py74```7576- META must contain `**Supersedes:**` + every source `### Source Task` block + `## Bundled Checklist` with `[XX]` prefixes.77- `lint_task_file` must pass on META (fixed `---` → `---\n\n` blank-line; `**Type:**` allows `feature` + `Meta:true` and also `meta`) and on both archived files (`**Status:** superseded` is allowed; `**File:**` matches archive path).78- `git log --follow` must show the source's history through the rename.7980## Skill Loading8182Load this skill when you handle bundling:8384```bash85skill("bundle-tasks")86```8788If you also need ID discovery or template generation, also load `task-generator` (this skill complements it, not replaces it). For lint, load `task-lint`; for context gathering before bundling, load `code-search` to ensure sources are in the expected Kanban dirs.8990## Rollback9192If META is abandoned or fails QA permanently:9394```bash95git mv tasks/archive/12-*.md tasks/backlog/12-*.md96git mv tasks/archive/15-*.md tasks/backlog/15-*.md97rm tasks/backlog/<NEXT_ID>-<slug>.md # or: git mv tasks/backlog/<NEXT_ID>-<slug>.md tasks/archive/<NEXT_ID>-<slug>.md # mark abandoned98```99100No HQ code beyond the bundler is affected. If META already reached `tasks/completed/`, its archived sources stay in `tasks/archive/` permanently (they are superseded, not purged).101102## Reference103104- **MCP:** `mcp-context-server/server.py:bundle_tasks` (self-contained, verbatim helpers inlined, `task_ids: list[str], title: str, dry_run, force`, path-traversal safe, cap 6)105- **Docs:** `AGENTS.md` `## 🛑 META-TASK BUNDLE LIFECYCLE` + `**Bundle Tool:**`, `CHANGELOG.md` `[Unreleased]`106- **Lint:** `mcp-lint-server/server.py` Type regex now `...|meta`107- **Registry:** `prompts/fragments/07-agent_skills_registry.md` lists `bundle-tasks`