# Parallel Plan

> Prepare a set of thematically unrelated items for concurrent execution before any execution begins - item intake over issue numbers and potential-entry paths, concurrent preparation-mode child orchestrator delegations, blast-radius computation and V1-V3 validation, cohort seeding with a recomputation-parity check, run-manifest and planner-checkpoint authoring, and the parallel-orchestrator kickoff artifact.

- Skill: `drmoisan/parallel-plan` (Agent Skill)
- Install (CLI): `npx skillmds@latest add drmoisan/parallel-plan`
- Raw SKILL.md: https://api.skillmd.com/api/skills/drmoisan/parallel-plan/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Data & Analytics
- Author: drmoisan (https://skillmd.com/u/drmoisan)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/drmoisan/parallel-plan

---


# Parallel Plan Skill

A user invocation (`/parallel-plan <slug> <item> [<item> ...]`) forks the `parallel-planner` agent
with this procedure in context. The run slug and item list for this run are:

$ARGUMENTS

This skill frames work for the `parallel-planner` agent, parallel to how
`.claude/skills/epic-plan/SKILL.md` frames work for `epic-planner`. The `parallel` surface
schedules thematically unrelated items by computed blast-radius contention: there is no worthiness
assessment, no operator-supplied ordering, and no shared branch into which item work merges.
Planning ends at preflight clearance and cohort seeding; no atomic execution, PR authoring, or CI
monitoring occurs under this skill.

## Prerequisites

Before proceeding, `parallel-planner` must:

1. Read `CLAUDE.md` for repository tone policy and architectural context.
2. Read applicable `.claude/rules/` files for the languages in scope, including
   `.claude/rules/parallel-orchestration.md`, which is the schema authority for the run manifest,
   both parallel checkpoints, and the nine parallel enums.
3. Read the policy files listed in the compliance reading order section of `CLAUDE.md`.

## Item Intake

Initial intake must provide the complete item set in one `/parallel-plan <slug> <item> [<item> ...]` invocation before waves are calculated. `/parallel-add` is not an initial-intake path.

Invocation shape: `/parallel-plan <slug> <item> [<item> ...]`, where each `<item>` is either a
GitHub issue number (already-promoted work) or a potential-entry path (unpromoted work). This is
the same intake domain as `/parallel-add`, so initial intake here and F6's add operation accept
identical forms.

- **Promotion.** Unpromoted items are promoted by their own preparation-mode child, not by the
  planner. The `preparation` route already carries the promotion MCP tools (`new_potential_entry`,
  `potential_to_issue`, `new_active_feature_folder`).
- **`issue_num` resolution.** `issue_num` is the primary key for every item reference. It is known
  at intake for issue-number items. For potential-entry items, record negative placeholders in
  intake order (`-1`, `-2`, ...) and back-fill the real number from each child's promotion receipt
  as preparation completes. Ordering is safe by construction: cohort seeding requires declared
  radii, radii require approved plans, and plans require promotion, so every placeholder resolves
  before seeding runs.
- **Fully resolved before kickoff.** The manifest is committed in fully resolved form — no negative
  `issue_num` remaining — before the kickoff artifact is written.
- **`feature_folder`.** Recorded at intake as a resolvable-hint basename, then resolved to a
  concrete `docs/features/active/<basename>` path after promotion with any lifecycle prefix
  stripped, per the `epic-orchestrate` convention. The concrete location is additionally fixed by
  the per-item `branch_name` recorded in the checkpoint.
- **`kind`.** Record `kind: feature | bug` at intake from the item source (issue labels, or the
  potential entry's declared kind). When the source is indeterminate, default to `feature` and note
  the choice `[recommendation, not upstream-constrained]`.

The operator is never asked for ordering edges, cohort assignments, or a worthiness verdict.
Intake proceeds directly to preparation fan-out.

## Preparation Fan-Out

One preparation-mode `Agent(orchestrator)` run per item. Preparation produces documents and plans
rather than code, and items carry no ordering constraint, so preparations may run concurrently —
but they are BOUNDED, not unbounded.

**Launch preparations in waves of at most `max_concurrency`.** Compute the waves with the same
deterministic chunker the execution phase uses:

```
bash .claude/lib/bash/compute-concurrency-batches.sh --keys "<all item keys>" --max-concurrency <n>
```

(already granted to this agent at `.claude/agents/parallel-planner.md:18`). It prints a compact
JSON array of arrays, returns the batches in order, and sorts the keys itself, so wave membership
does not depend on caller ordering. Launch wave *k* as one message carrying that wave's `Agent`
calls, each `isolation: "worktree"` and `run_in_background: true`.
Create each preparation worktree's branch from `origin/main`.
Launch wave *k+1* only after every child of wave *k* has TERMINATED — not merely reported
progress. All of this happens inside a single `/parallel-plan` invocation with no operator action
between waves.

The bound is `max_concurrency` itself, and no new knob is introduced. A preparation child and an
execution child are the same workload class — one background orchestrator per item — so the
operator's declared appetite for concurrent children applies to both phases. At the motivating
scale, `max_concurrency: 13` over 69 items runs `ceil(69 / 13) = 6` waves.

**A `max_preparation_concurrency` manifest key was considered and is explicitly NOT adopted now.**
Such a key would carry the same `1..32` bounds and the same boolean rejection as M4 and would
default to `max_concurrency`. It is deferred because no evidence yet shows the two phases need
different caps, and adding a second knob would force every operator to reason about two numbers
where one suffices. Revisit it only if a real run shows preparation and execution have materially
different concurrency profiles.

**`/parallel-add` is NOT the intake path.** It performs incremental admission into an
already-running open-mode queue: exactly one item per invocation, with a single sequential
preparation child. Preparing 69 items through it would take 57 or more separate operator
invocations after the initial plan. Use `/parallel-plan` for intake and `/parallel-add` only to
admit an item into a run that is already in flight.

Each delegation prompt includes this literal kickoff line, followed by the model-budget marker
line:

> `Preparation mode: true. route_id: preparation. parallel_slug: <slug>. Perform promotion, research, feature documents (spec.md, user-story.md), atomic planning, and preflight clearance only. Atomic execution, PR authoring, and CI monitoring are out of scope for this run and are executed later by parallel-orchestrator. After the atomic-executor preflight returns PREFLIGHT: ALL CLEAR, commit the feature folder and plan to the current branch, push the current branch to origin, set out-of-scope step statuses to not-applicable, set next_step to S5_atomic_execution, and stop, reporting the plan-path and preflight status.`
>
> `model_budget.fable_policy: <disabled|available|preferred>.`

Properties of that line, each individually load-bearing:

- The markers `Preparation mode: true.` and `route_id: preparation.` are reused verbatim. Route
  selection is marker-driven, so no issuer identity is required.
- `parallel_slug: <slug>.` replaces the epic context fields.
- The push instruction is an addition to the planner's own prompt, not to the child contract text
  in `.claude/skills/orchestrate/SKILL.md`. It is required so each item's prepared work is durable
  before its worktree is removed.
- Downstream attribution reads "executed later by parallel-orchestrator". The planner authors its
  own prompt; the `orchestrate` skill text is untouched.
- The `model_budget.fable_policy` marker line is appended per the pattern in
  `.claude/skills/epic-orchestrate/SKILL.md` `## Model Selection`. The default is `disabled` when
  the marker is absent.

**Deliberate omissions.** The kickoff line above carries neither mode marker. It contains no
`Epic mode: true`, so `enforce-epic-wave-barrier.ps1` — which gates execution-phase delegations —
does not apply to preparation. It contains no `Parallel mode: true`, so F7's future cohort-barrier
hook, which matches that marker, will not gate preparation either. Both omissions are intentional
and must be preserved verbatim when the line is emitted.

**No edit to shared surfaces.** No edit is made to `.claude/skills/orchestrate/SKILL.md` or to
`config/orchestration-routing.json`, including the `preparation` route. The route mechanism is
marker-driven; the child contract's "commit ... to the current branch" wording is satisfied by a
branch created off `origin/main`; and `routes.preparation` declares no epic-specific required
agent, skill, or MCP tool.

**Collected per child at termination:** `plan-path`, preflight status, the promotion receipt (the
`issue_num` back-fill source), the model-routing receipt with `logical_agent: "orchestrator"`, the
topology receipt, `branch_name`, and `worktree_path`. There is no fan-in merge step: the planner
fetches and records each pushed item branch, back-fills `issue_num` into the manifest on
`parallel/<slug>-plan`, and updates the checkpoint.

## Artifact Home

**Per-item artifacts.** Each item's prepared feature folder and approved atomic plan live on that
item's own feature branch, created from `origin/main` at preparation time, committed by the
unchanged `route_id: preparation` terminal step, pushed to `origin` before its worktree is removed,
and reused unchanged as the item's execution branch. Record `branch_name` and `worktree_path` per
item in the checkpoint.

**Run-level artifacts.** `docs/features/parallel/<slug>/parallel.md` (the run manifest) and
`docs/features/parallel/<slug>/parallel-kickoff.md` (the durable kickoff copy) live on the
planner-owned branch `parallel/<slug>-plan`, created off `origin/main` and pushed. That branch is
explicitly not an integration branch: no item branch ever merges into it, it never merges into any
item branch, and it holds only `docs/features/parallel/<slug>/**`, a subtree no item's blast radius
may include. Each item opens its own pull request against `main`.

**Read access.** Readers — validators and the future `parallel-orchestrator` — reach both artifact
classes by ref: `git fetch origin <branch>` followed by `git show <ref>:<path>`, without checking
the ref out, per the technique documented in `.claude/skills/epic-run/SKILL.md`.

Three residual risks are recorded rather than eliminated:

1. **Stale execution base.** An item branch created at preparation time is based on an older `main`
   tip by the time its cohort executes. Mitigation is F5-owned (the execution-phase child merges
   `origin/main` at start, and cohort construction keeps peers non-conflicting); real overlap is
   caught by F8 drift detection. This skill records the branch-reuse contract only.
2. **Branch accumulation on withdrawal.** Withdrawn or abandoned items leave pushed preparation
   branches. F6's `--disposition abandon` path is expected to delete the branch; the cleanup
   expectation is recorded here so F6 can implement it.
3. **Per-branch git-integrity requirement — F4-owned.** The readiness gate's git-integrity check
   must verify committed plan blobs against each per-item branch ref plus the
   `parallel/<slug>-plan` ref, rather than against a single shared ref. This obligation belongs to
   the planner surface, not to the schema feature: `.claude/rules/parallel-orchestration.md` states
   that F3's `require_ready_for_execution` gate is structural only and leaves git-integrity checks,
   launch-evidence binding, and kickoff-contract cross-checks to F4.

## Radius Computation and Validation

Reach blast-radius derivation, validation, and contention through the **destination-runtime
PowerShell port** under `.claude/lib/blast-radius/`, which is published by push-down and needs no
Python interpreter:

```powershell
$repoRoot = git rev-parse --show-toplevel
Import-Module (Join-Path $repoRoot '.claude/lib/blast-radius/BlastRadius.psm1') -Force -ErrorAction Stop
```

The default PowerShell 5.1 execution policy blocks `Import-Module` of a `.psm1` file, so `pwsh` is
mandatory here.

The facade re-exports the five functions this skill needs: `Get-PlanPaths` (port of
`extract_plan_paths`), `Get-BlastRadius` (port of `derive_blast_radius`),
`Get-BlastRadiusFromObservedPaths` (port of `radius_from_observed_paths`), `Test-BlastRadius`
(port of `validate_blast_radius`), and `Test-BlastRadiusConflict` (port of `conflicts`). Wrap a
call to `Test-BlastRadius` in `@(...)`: it writes its findings to the pipeline, so a zero-element
result writes nothing and a one-element result writes a single object.

`Test-BlastRadiusConflict` reads the optional `mergeable_paths` list from that truth table and
contributes no `path_overlap` edge for a path matching it, while the path itself stays in the
declared radius and is still read by every audit.

The truth table the port reads is `config/blast-radius.json`, which push-down publishes into the
destination workspace alongside `.claude`.

The Python modules named below remain the repository authority and the parity reference; they are
cited for their contract, not invoked on the destination-runtime path. Landed contract, consumed
as-is and never reimplemented here:

- **Derivation.**
  `derive_blast_radius(plan_text, spec_text, feature_folder, config, *, source, computed_at) -> BlastRadius`
  in `scripts/dev_tools/compute_blast_radius.py`. It takes the approved atomic plan's **document
  text** and the feature `spec.md`'s **document text**, the feature folder name, and the parsed
  `config/blast-radius.json` truth table. It does not take file paths: read the documents and pass
  their text.
- **Serialized shape.** `BlastRadius.to_dict()` yields exactly the key set
  `("paths", "modules", "shared_surfaces", "contracts", "source", "computed_at")`, which is the
  shape both the manifest and the checkpoint record.
- **Validation.** `validate_blast_radius(radius, plan_text, config, *, tracked_file_count)` in
  `scripts/dev_tools/_blast_radius_validation.py` returns findings over three rules:
  `RULE_COVERAGE = "V1"`, `RULE_SHARED_SURFACE = "V2"`, and `RULE_OVER_BREADTH = "V3"`. Each finding
  carries a severity of `Blocking` or `Advisory`.
- **Contention.** `conflicts(a, b, config) -> ConflictResult`, re-exported from
  `compute_blast_radius.py`. The signature takes three arguments; the third is the parsed
  `config/blast-radius.json`. Reasons come from the fixed vocabulary
  `{path_overlap, module_overlap, shared_surface_overlap, contract_dependency}`, and the relation
  fails closed. Read the verdict from the conflict field of the returned ConflictResult.
  The result's boolean projection now agrees with that field, so `if conflicts(a, b, config):`
  yields the verdict rather than the unconditional truth a bare object test gave before issue #576.

**The F1a corrections (issue #452, merged PR #453) are load-bearing.** Derivation now reaches
separator-free repository-root shared surfaces from plan and spec text, admitting such a token only
as an exact ordinal member of the configured `shared_surfaces` list in `config/blast-radius.json`;
and the contention path comparison now honours listed-directory prefixes on both sides, aligning
with `is_path_subsumed`. Both corrections move results in the fail-closed direction — they report
more contention, not less. Do not work around either correction.

**The exclusions are configured, not improvised (issue #489).** `config/blast-radius.json` carries
an optional `mandate_reads` list naming the paths every agent is instructed to read before doing
any work: the policy rules, the tier map, and the process artifacts. A citation of one of those
paths is evidence that the author obeyed the reading order, not evidence that the change will write
the file, so `derive_blast_radius` drops it from the harvest and `validate_blast_radius` drops it
from its plan-side extraction, which keeps V1 and V2 self-consistent. The extractor likewise rejects
three token shapes that were never write claims: a wildcard-free token naming a directory rather
than a file, a `docs/features/` glob whose wildcard spans every feature folder, and a contract token
carrying no ASCII letter. These exclusions are part of the landed contract, so the prohibition now
reads: do not narrow a radius beyond the configured exclusions in order to suppress a conflict edge.

**Appending an excluded path is the planner's obligation, not an exception.** An exclusion describes
the default reading relationship, not a permanent ban. When an item's plan will genuinely WRITE a
path that the exclusions remove — amending a rule file under `.claude/rules/`, editing
`quality-tiers.yml`, or changing an instruction document under `.github/instructions/` — the planner
MUST append that exact path to the item's declared radius explicitly after normalization. Omitting
it under-reports contention and lets two items that both rewrite the same policy file run
concurrently.

### Planner procedure

1. After an item's plan is approved and preflight-clear, read the approved plan text and the
   feature `spec.md` text, derive the radius with `source: "declared"`, then call
   `normalize_declared_radius(radius, config)` to re-apply the current extraction rules and the
   configured exclusions, append any excluded path the plan's diff will genuinely write, and record
   the result on the item. The `declared` radius is the authoritative input to scheduling.
2. Validate the radius and record the findings under the item's `radius_validation` entry.
3. **V1 (coverage) or V2 (shared-surface enumeration) Blocking failure.** The item does NOT
   transition to `prepared`. Record the findings in the checkpoint and issue a follow-up
   preparation-mode delegation for that item carrying the findings as plan-revision instructions —
   the same iterate-until-clear posture the child already applies to preflight. The item is
   re-planned, not rejected. Withdrawal is a caller decision made through F6's remove operation and
   is never a planner default.
4. **V3 (over-breadth) Advisory.** Record it in the checkpoint and surface it in the completion
   report. It has no state effect.
5. **Readiness conjunction.** `prepared` requires BOTH `preflight_status == "PREFLIGHT: ALL CLEAR"`
   AND a `declared` radius that passed V1 and V2.

Derivation, the V1-V3 rules, and the contention relation are implemented upstream. This skill calls
them; it defines none of them.

## Cohort Seeding

Reach cohort computation through the **destination-runtime bash entry point** under
`.claude/lib/bash/`, which is published by push-down and needs no Python interpreter:

```bash
bash .claude/lib/bash/compute-cohorts.sh --keys "<k1> <k2> ..." --edges "<a>:<b> <a>:<b> ..."
```

`--edges` is optional; omitting it, or passing an empty string, means the conflict graph has no
edges. The entry point prints a compact JSON array of arrays on stdout, identical to Python
`json.dumps(..., separators=(",", ":"))`. On malformed input it prints the reference
implementation's exact message on stderr and exits 1; a token outside the accepted integer lexis
`-?(0|[1-9][0-9]*)` is rejected fail-closed with exit 2.

Landed contract, mirrored byte for byte by the bash entry point:

- `compute_cohorts(item_keys, conflict_edges) -> list[list[int]]` in
  `scripts/dev_tools/parallel_cohort_computation.py` is the repository authority and the parity
  reference. The signature accepts exactly two parameters, `item_keys: Iterable[int]` and
  `conflict_edges: Iterable[tuple[int, int]]`. There is no third parameter and nothing further to
  supply at seeding time.
- The return value is a plain list of lists in deterministic Welsh-Powell order: vertices are
  visited by the composite key `(-degree, item_key)` ascending — descending distinct-neighbour
  degree with ties broken by ascending item key — and each vertex takes the lowest cohort index not
  already held by one of its neighbours.
- Recoloring support belongs to F6 and F8, which is why the landed seeding signature needs no
  additional state input.

**Mapping to the recorded shape.** This planner maps the returned list of lists into the F3-owned
`cohorts[]` object shape, supplying `index` from the outer-list position and `generation: 0` itself.
The library returns the partition; the planner supplies the record fields.

### Seeding procedure

1. Invoke `compute-cohorts.sh` exactly once per plan run, over the full conflict graph, after every
   item is `prepared` and radius-validated. Derive the conflict edge set by applying
   `Test-BlastRadiusConflict` to every unordered pair of `declared` radii, then pass the pairs as
   `--edges "<a>:<b> ..."` and the item keys as `--keys "<k1> <k2> ..."`.
   Read the verdict from the conflict key of the returned hashtable.
   `Test-BlastRadiusConflict` contributes no `path_overlap` edge for a path matching the truth
   table's optional `mergeable_paths` list, and the path stays in the declared radius.
   The hashtable itself is always truthy, so a bare boolean test on the result treats every pair as
   conflicting and serializes the whole run. This is the sibling hazard to the `@(...)` warning
   above for `Test-BlastRadius`: that function writes an `IList`-shaped pipeline result whose
   emptiness is falsy, while this one returns a hashtable whose emptiness is not expressible at all.
2. Immediately after the conflict-edge set is derived and before anything consumes it, run the
   lane-assertion diagnostic:
   `bash .claude/lib/bash/report-lane-assertion.sh --manifest docs/features/parallel/<slug>/parallel.md --edges "<a>:<b> ..."`
   (a bash invocation against the published payload; it requires no Python interpreter at the
   destination runtime). It compares the manifest's
   optional `expected_conflict_components` assertion (invariant M8) against the connected
   components of the DERIVED conflict graph and prints one `ADVISORY` line per finding in four
   classes: expected-together-but-derived-apart, expected-apart-but-derived-together, a member
   naming no manifest item, and — informational only — a manifest item covered by no expected
   component.
   **The diagnostic is ADVISORY ONLY.** It never blocks the run, never modifies or suppresses a
   derived edge, never feeds `compute_cohorts`, and never influences scheduling. It always exits 0,
   including when it reports disagreements. A disagreement is a signal to re-examine the blast
   radii; it is never a licence to narrow a radius to suppress an edge, which stays prohibited.
   When the manifest carries no `expected_conflict_components` key the diagnostic still runs and
   reports every item as uncovered, which is the expected output for a run with no assertion.
   Recording the diagnostic's result in the planner checkpoint is a tolerated extra field, not a
   validated one; no validator changes for it.
3. Record `cohorts[]` at `generation: 0`, each cohort's `item_keys[]` sorted ascending.
4. Record `conflict_edges[]` as `{a, b, reason}` entries for auditability.
5. Record `recolor_generation: 0` and `current_cohort: 0`.
6. Record `max_concurrency` — default 4, bounded 1 through 32 by the F3 schema — without enforcing
   it. Enforcement is F5's, through
   `bash .claude/lib/bash/compute-concurrency-batches.sh --keys "<k1> ..." --max-concurrency <n>`
   (the bash port of `compute_concurrency_batches(cohort_item_keys, max_concurrency)`), which fills
   slots in ascending item-key order. Recoloring under add, remove, or drift mutation is F6 and F8
   scope. This skill performs seeding only.

### Recomputation parity (planner-owned check)

Planner invariant P5 in `.claude/rules/parallel-orchestration.md` is deliberately absent from the
F3 validator and states that recomputation parity against the cohort-computation module is the
planner-surface feature's check — the analogue of the epic planner's wave-number cross-check. This
skill discharges that obligation as documented procedure, not as a new module:

1. After writing `cohorts[]` into the checkpoint and **before** emitting the kickoff artifact,
   re-invoke `compute_cohorts` over exactly the `item_keys` and `conflict_edges` recorded in the
   checkpoint.
2. Assert that the recomputed list of lists maps to exactly the recorded `cohorts[]` at
   `generation: 0`: the same partition, the same `index` assignment, and the same ascending
   `item_keys[]` ordering.
3. A mismatch is a **Blocking** condition. Stop the run and report the mismatch. Do not
   auto-correct the recorded cohorts, and do not emit the kickoff artifact.

No production module is added for this check; it is a re-invocation of the landed library.

## Manifest Authoring

Write `docs/features/parallel/<slug>/parallel.md` conforming to the F3-owned frontmatter schema
recorded in `.claude/rules/parallel-orchestration.md` (manifest invariants M1-M8):

- `parallel` — the run slug, a non-empty string.
- `mode` — `closed` or `open`; defaults to `closed` when absent.
- `max_concurrency` — an integer from 1 through 32; defaults to `4` when absent.
- `created_at` — a non-empty ISO-8601 string.
- `items[]` — one entry per item, each carrying `issue_num` (a positive integer, unique across
  items), `feature_folder` (a non-empty string), `kind` (`feature` or `bug`), `state`, and
  `blast_radius` carrying `paths`, `modules`, `shared_surfaces`, `contracts`, `source: "declared"`,
  and `computed_at`.
- `expected_conflict_components[]` — OPTIONAL (invariant M8). A block sequence of objects, each
  carrying a required non-empty `members` list of positive `issue_num` integers that resolve to
  declared items, with no item in two components, plus an optional non-empty-string `name` used as
  a diagnostic label only. A flow-style value (`members: [101, 102]`) is outside the bash YAML
  subset and must not be authored. The field is an ASSERTION consumed by the advisory lane
  diagnostic in `### Seeding procedure`: it never overrides a derived edge, never feeds
  `compute_cohorts`, and never influences scheduling.

The manifest carries no `depends_on` field at any level and no top-level `integration_branch`
field; both are prohibited-key rejections in the schema. Commit it to `parallel/<slug>-plan` in
fully resolved form — every negative placeholder `issue_num` replaced by its promoted number —
before the kickoff artifact is written.

Validate the manifest with the **destination-runtime bash entry point**, which is published by
push-down and needs no Python interpreter:

```bash
bash .claude/lib/bash/validate-parallel-manifest.sh <manifest-path>
```

It prints validation errors one per line on stdout and exits 0 for a valid manifest, 1 for an
invalid one, and 2 for an unreadable file or a YAML construct outside the supported subset. The two
default-resolving accessors are subcommands of the same entry point:

```bash
bash .claude/lib/bash/validate-parallel-manifest.sh --print-mode <manifest-path>
bash .claude/lib/bash/validate-parallel-manifest.sh --print-max-concurrency <manifest-path>
```

They resolve the documented defaults `closed` and `4` when the manifest omits the key or carries a
malformed value. Consume `mode` and `max_concurrency` through these accessors rather than reading
the frontmatter directly.

`scripts/dev_tools/parallel_manifest_contract.py` (`validate_parallel_manifest_text`,
`manifest_mode`, `manifest_max_concurrency`) remains the repository authority and the parity
reference. Manifest validation is deliberately not an MCP `artifact_type`; do not attempt to
validate the manifest through `mcp__drm-copilot__validate_orchestration_artifacts`.

## Checkpoint Persistence

Write `artifacts/orchestration/parallel-planner-state.json` after every completed step.

Top-level fields: `objective`, `parallel_slug`, `parallel_manifest_path`, `mode`,
`max_concurrency`, `plan_home_branch` (`parallel/<slug>-plan`), `items[]`, `cohorts[]`,
`conflict_edges[]`, `recolor_generation`, `kickoff_prompt_path`
(`artifacts/orchestration/parallel-kickoff-<slug>.md`), `completed_steps`, `next_step`, and
`last_updated`. F3's landed required-key set (planner invariant P1) is a strict subset of this
list, and `plan_home_branch` is a permitted additional field.

Per item: `issue_num`, `feature_folder`, `kind`, `state`, `complexity_band`, `preparation_status`,
`research_path`, `plan_path`, `preflight_status`, `branch_name`, `worktree_path`, `blast_radius`
(with `source: "declared"`), `radius_validation` (the `v1`, `v2`, and `v3` results with their
severities), `model_routing_receipt`, and `topology_receipt`.

**Deliberately absent:** any `epic_worthiness` analogue, any `depends_on` field, and any `wave`
field. The parallel surface renders no worthiness verdict and expresses ordering only as
blast-radius overlap.

**Readiness contract.** Before reporting completion the checkpoint must satisfy F3's
`require_ready_for_execution` gate: at least two items (invariant P6); every item
`preparation_status: prepared` with `preflight_status` exactly `PREFLIGHT: ALL CLEAR`, non-empty
`research_path` and `plan_path`, and `blast_radius.source == "declared"` (P7); `next_step` exactly
the ready sentinel `PARALLEL_EXECUTION_READY` (P8); and `kickoff_prompt_path` exactly
`artifacts/orchestration/parallel-kickoff-<slug>.md` (P9).

**Git integrity is F4-owned.** F3's `require_ready_for_execution` gate is structural only, so
verifying that each item's committed plan blob exists on that item's pushed branch ref — and that
the manifest and durable kickoff exist on the `parallel/<slug>-plan` ref — is this planner
surface's obligation, layered behind the planner's own check without changing the F3 schema.
Perform it with `git cat-file -e <ref>:<path>` and `git show <ref>:<path>` against each per-item
branch ref plus `parallel/<slug>-plan`.

**Cache doctrine.** The checkpoint is a cache of durable state, not the source of truth. On resume,
re-derive ground truth from `git branch`, `git worktree list --porcelain`, and the pushed refs.
Where the checkpoint and the repository disagree, the repository wins and the checkpoint is
rewritten from it.

Validate the checkpoint through `mcp__drm-copilot__validate_orchestration_artifacts` with
`artifact_type: "parallel-planner-state"` before reporting completion.

## F3 Ownership Boundary

The following surfaces are owned by the schema-and-validator feature (F3). This planner writes
conforming instances and consumes the fixed enums; it never extends or redefines them:

- The manifest schema and the planner-checkpoint schema, recorded as prose invariants in
  `.claude/rules/parallel-orchestration.md`, together with all nine parallel enums.
- `scripts/dev_tools/validate_parallel_planner_state.py` and
  `scripts/dev_tools/validate_parallel_orchestrator_state.py`.
- The two state MCP `artifact_type` values `parallel-planner-state` and
  `parallel-orchestrator-state`.
- The `route_id: parallel` entry in `config/orchestration-routing.json`.

The kickoff contract is **not** F3-owned. `scripts/dev_tools/parallel_kickoff_contract.py` and the
MCP `artifact_type: "parallel-kickoff"` are F4-owned and are **delivered by this feature**, per the
epic-manifest adjudication "Planner Adjudication: the kickoff-contract boundary (F3 / F4)" and the
matching "F3 Scope Boundary — kickoff contract deferred to F4" section of
`.claude/rules/parallel-orchestration.md`. Both are landed modules, not a recommendation carrying a
pending contingency.

The single-item-run floor question is resolved by F3's landed ready-gate invariant P6, which
requires at least two items under `require_ready_for_execution`. A one-item run cannot reach a ready
checkpoint.

## Kickoff Artifact

Write the working copy to `artifacts/orchestration/parallel-kickoff-<slug>.md` (a gitignored tree)
and commit a byte-identical durable copy to `docs/features/parallel/<slug>/parallel-kickoff.md` on
`parallel/<slug>-plan`.

```markdown
# Parallel Kickoff: <slug>

Planned by parallel-planner on <iso8601>. All items are prepared: promoted, active folders created,
research complete, spec and user-story written, atomic plans approved, preflight ALL CLEAR, blast
radii declared and V1/V2-clear. Planning state:
artifacts/orchestration/parallel-planner-state.json (run branch: parallel/<slug>-plan).

## Invocation Prompt

Run `/parallel-run <slug>` to execute this run, or paste the prompt below.

Use the parallel-orchestrator subagent to execute the prepared run whose manifest is
docs/features/parallel/<slug>/parallel.md on the plan-home branch parallel/<slug>-plan. Each item
resumes at atomic execution from its committed plan-path on its own pushed feature branch rather
than re-planning, and each item opens its own pull request against main.

## Item Summary

| issue_num | feature_folder | cohort | complexity | branch | plan-path |
| --- | --- | --- | --- | --- | --- |
| ... | ... | ... | ... | ... | ... |

## Integrity

planning_commit: <hex>

| plan-path | plan-hash |
| --- | --- |
| ... | ... |
```

Structural requirements enforced by the contract module:

- The first line is exactly `# Parallel Kickoff: <slug>`, with the slug matching
  `[a-z0-9][a-z0-9-]*`.
- `## Invocation Prompt` must name `` Run `/parallel-run <slug>` ``, the manifest path
  `docs/features/parallel/<slug>/parallel.md`, the plan-home branch `parallel/<slug>-plan`, and the
  resume-boundary sentence stating that each item resumes at atomic execution from its committed
  plan-path on its own pushed feature branch.
- `## Item Summary` is a strict pipe table with the exact ordered headers
  `issue_num | feature_folder | cohort | complexity | branch | plan-path` and at least one data row.
  `issue_num` and `cohort` are integers; `complexity` is one of `C1`, `C2`, `C3`, `C4`.
- `## Integrity` is optional. When present it carries a `planning_commit:` field holding the head
  commit of `parallel/<slug>-plan` as run-level provenance and a per-item `plan-path | plan-hash`
  table whose hashes are 40 to 64 hex characters with no repeated plan path.

Validate the artifact through `mcp__drm-copilot__validate_orchestration_artifacts` with
`artifact_type: "parallel-kickoff"`. That artifact type dispatches to
`scripts/dev_tools/parallel_kickoff_contract.py`, which this feature delivers.

### Integration Commit Form (issue #539)

The parallel planner's own version-control operations — committing the run manifest
`docs/features/parallel/<slug>/parallel.md`, each item's prepared feature folder and approved
plan-path, and the durable kickoff copy `docs/features/parallel/<slug>/parallel-kickoff.md` — are
orchestration bookkeeping, not implementation. The preimplementation gate
(`.claude/hooks/enforce-orchestration-preimplementation-gate.ps1`) exempts them only when the
invocation is **pathspec-bearing**: every staging or integration invocation must name at least one
explicit path operand, and every operand must resolve inside one of the five exempt
orchestration-bookkeeping trees:

- `docs/features/epics/`
- `docs/features/parallel/`
- `docs/features/active/`
- `docs/features/potential/`
- `artifacts/orchestration/`

Use one of the two pathspec-bearing spellings:

- the message option followed by a double-dash separator and then the exempt path operands, or
- the exempt path operands named directly before the message option.

A pathless integration invocation — a message option with no path operand — is denied (D4 row 4),
as are whole-tree operands, tree-wide flags, pathspec-from-file options, history-rewriting or
content-widening options, and any operand that resolves outside the five exempt trees. Mixing one
exempt operand with one production operand denies the whole invocation. Name each exempt path
explicitly; the exemption is allow-side only and every parse ambiguity denies.

## Completion Report

The final report to the operator must include:

- The manifest path `docs/features/parallel/<slug>/parallel.md` and the run branch
  `parallel/<slug>-plan`.
- Per item: one `plan-path:` line, the branch name, the preflight status, and the
  radius-validation result, including any V3 Advisory findings.
- The cohort table at `generation 0`, together with the result of the recomputation-parity check.
- The lane-assertion diagnostic's result: the derived conflict-component count, the disagreement
  count, and every `ADVISORY` line it emitted. This line-item is REQUIRED and is reported even when
  the manifest carries no `expected_conflict_components` assertion and even when the diagnostic
  found nothing, so its silence is never ambiguous. Report it as advisory information: it does not
  gate the report, does not change the cohort table, and no finding is escalated to Blocking.
- Both kickoff artifact paths: `artifacts/orchestration/parallel-kickoff-<slug>.md` and
  `docs/features/parallel/<slug>/parallel-kickoff.md`.

End with the statement that execution has NOT started and begins only when the operator runs
`/parallel-run <slug>` or replays the kickoff prompt from the main session.

