You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat(flow): Stacks on — auto-stacked dependency chains in one run (#123)
A single `flow` over a merge-gated, file-overlapping chain couldn't complete in one
run: `Depends on (merged)` waits for a real merge, and a flow run only opens draft PRs,
so the chain advanced only across separate invocations interleaved with human merges.
Add an opt-in `Stacks on: <id>…` field: a dependent builds on its prerequisite's
*just-produced branch* within the SAME run and publishes a separate stacked PR
(`gh pr create --base <prereq-branch>`), so a planned refactor sequence (203 → 207 → 204)
completes as a reviewable PR stack in one `flow` invocation. Keep `Depends on (merged)`
(the conservative multi-run mode) intact.
- brief.stacks_on() parses the field (shared _id_list, like depends_on_merged).
- flow: _declared_deps includes stacks_on (DAG validation); _stacked_snapshot gates a
dependent on its prereq being COMPLETE-with-a-published-branch (not merged), once per
pass like _merged_snapshot; _deps_met honours it. A stack prerequisite publishes
in-loop (the moment it's COMPLETE) — not the deferred end-publish — so a dependent's
next pass can base on its branch; _publish_bundle dedups against the end-publish.
- worktree: a stacked dependent's Do worktree bases off origin/<prereq-branch> (resolved
from the prereq's publish.json), not the target base, so Do builds on the prereq's diff.
- publish: a `Stacks on` bundle cuts its branch off the parent branch and targets the PR
at it (`--base <parent-branch>`); publish.json records mode "stacked-pr" + stacks_on.
Warns (best-effort, gh) if the repo disallows merge commits (a stack must merge-commit
bottom-up, not squash).
- docs 09 + brief field document Stacks on vs Depends on (merged), the bottom-up
merge-commit rule, and the cross-run rebuild after a parent's branch changes.
Scope note (settled with the maintainer): under publish-on-accept + eligibility waiting
for COMPLETE, a prereq finishes ALL its iterations before its dependent ever builds, so
the in-run "auto-rebuild the stack above on iterate" can't fire — the dependent always
builds on the prereq's final branch. The cross-run rebuild (e.g. after a squash-merge)
is the documented mechanism; no in-run cascade code is needed.
Tests: stacks_on parsing; stacked-PR publish targets the parent branch + errors before
the parent publishes; worktree bases off the parent branch (real git); a stacked chain
completes in one flow_ids run (parent built+published before the dependent). 200 OK;
lint_docs OK.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Eduard Ralph <15236434+eduralph@users.noreply.github.com>
Copy file name to clipboardExpand all lines: template/PCDA/quality-cycle/09-parallel-lanes.md
+1Lines changed: 1 addition & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -58,6 +58,7 @@ Manual wave-splitting (run a prerequisite batch to COMPLETE, *then* the next) en
58
58
59
59
-**`- **Depends on:** <id>[, <id>…]`** — a topological gate. The in-driver pool dispatches a bundle only once every declared prerequisite is **COMPLETE** (signed off, not merely built). Because a prereq reaches COMPLETE only after its sign-off in an earlier pass, a dependent waits across passes — exactly the manual wave plan, now machine-enforced.
60
60
-**`- **Depends on (merged):** <id>[, <id>…]`** — a *stricter* gate for a dependent that **edits files a prerequisite also edits**. `Depends on` waits only for COMPLETE — which means "a draft PR was opened", **not merged**. A dependent's Do runs in a worktree off the target base (`origin/<base>`), so a prereq whose PR is still open is *absent* from that base: the dependent is built without the predecessor's diff and its PR conflicts at merge. This field holds the dependent until the prereq's PR is **merged into the base** (read from the prereq's recorded PR via `gh pr view`), so Do genuinely builds on the merged result. Because the flow only *opens* draft PRs (it never merges), a merge-gated dependent is **held across `pdca flow` runs**: its prereq is published in one run, a human merges that PR, and a later `pdca flow <dependent>` finds it merged and proceeds. Best-effort and fail-closed: anything not confirmable as merged keeps the dependent safely blocked.
61
+
- **`- **Stacks on:** <id>[, <id>…]`** — auto-stacked chains (issue #123). Like `Depends on (merged)` it is for a dependent that **edits files a prerequisite also edits**, but instead of *waiting for the prereq to merge* (multi-run), it builds the dependent on the prereq's **just-produced branch** within the **same `flow` run** and publishes a **separate stacked PR** (`gh pr create --base <prereq-branch>`, one PR per item showing only that item's increment). A stacked dependent is eligible once its prereq is **COMPLETE with a published branch**; its Do worktree bases off `origin/<prereq-branch>` and its PR targets that branch — the base is **derived from the prereq's `publish.json`**, never written in the brief (it doesn't exist at Plan time). So a planned, file-overlapping refactor sequence (`203 → 207 → 204`) completes as a reviewable PR stack in **one invocation** — independents still run in parallel; a `Stacks on` chain is sequential within itself. **Merge the stack bottom-up with merge-commit / rebase-merge, not squash** (a squash drops the parent's commits, so a child retargeted to the base re-shows the parent's diff until rebased); publish warns if the target repo disallows merge commits. If a parent's branch later changes (e.g. a squash-merge between runs), rebuild the items stacked above it (`signoff --iterate-do`) on the new foundation. Use `Depends on (merged)` instead when you'd rather wait for each PR to merge before the next builds.
61
62
-**`- **Conflicts with:** <id>[, <id>…]`** — a same-wave exclusion. Two bundles that touch a shared resource (e.g. both edit one `ci.yml`) are **never in flight in the same concurrent wave**; the pool serializes them across lanes while still parallelizing everything else.
62
63
63
64
The fields are **additive and backwards-compatible**: with none declared, every bundle is always eligible and dispatch is byte-for-byte the prior **sort-by-name pool**. An unschedulable graph — a cycle, or a dependency that is neither in the batch nor an already-COMPLETE bundle — is a **hard error rejected before any build** (`pdca flow` aborts up front). `pdca status` shows a `[blocked-by: <ids>]` flag so the queue reads as a DAG, not a flat list. Declared ordering complements lane planning: planning *avoids* integration tangling by code locality; `depends_on` / `conflicts_with`*enforce* the residual ordering that locality cannot express.
0 commit comments