Dependent-spec chains: branch from the parent, link GitHub stacks - #433
Conversation
PR SummaryMedium Risk Overview flowctl adds make-pr gains merged-parent rebase --onto on create-only runs (leased push), §4.6c stack REST linking with one-line degrade on failure, chained layers ready under autonomy when nothing is open, and Tests: Reviewed by Cursor Bugbot for commit 2d70e23. Configure here. |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit 2d70e23. Configure here.
…dge for the chained dogfood run Claude-Session: https://claude.ai/code/session_01JFYv5JtK8HAsshpWRAT5YL
…tHub stacks - flowctl: `spec chain <id>` read-only predicate (one ls-remote at most), the single owner of chain eligibility; the spec-level admission gate in `ready --spec`, `next`, and `ready --all` treats the chain parent as satisfied - work: Phase 2 branches a chained spec from the parent's fetched remote tip, records that merge-base as the spec base, and blocks on an ineligible answer - flow --auto / backlog / attended ladder: selection calls the predicate, parks with its reason (no strike), and prefixes the verdict reason `chained on <id>; ` - make-pr: chain rung in the base cascade detected from history (merge-base with the parent's branch tip or merged-PR head), merged-parent rewrite on a create run only with a leased push, stack link via the stacks REST API with integer payloads and one-line degrade, chained layers with nothing open born ready - docs: flowctl.md `spec chain`, orchestration and teams pages, glossary terms chain/stack/layer/frontier, changelog, 2026-08-27 memory entry superseded - tests: flowctl chain states and admission gates; fence fixtures for every consumer against a bare origin and a stubbed gh Task: fn-152-make-pr-opt-in-stack-linking-via-github.1 Claude-Session: https://claude.ai/code/session_01JFYv5JtK8HAsshpWRAT5YL
… chained draft rule - make-pr: a merged parent whose chain base cannot be refreshed is unresolved (exit 2) instead of rebasing onto a stale ref; a failed ls-remote read refuses the rewrite instead of skipping the lease - make-pr: stack-link POST failures are captured inside conditionals so the one-line degrade runs under the workflow's `set -e`; repo lookup guarded - make-pr: open items force draft on a chained layer even over --ready (R8) - flowctl: `pilot-log append --reason` (optional, stored only when given) so the backlog decision-log row carries `chained on <parent>; ` (R9) - tests: consumer runner executes fences under `set -e`; new cases for the failed base refresh, the failed remote read, and the reason row Task: fn-152-make-pr-opt-in-stack-linking-via-github.1 Claude-Session: https://claude.ai/code/session_01JFYv5JtK8HAsshpWRAT5YL
…ason, carry spec files onto the chain branch - make-pr: fetch the chain base before the ancestry test so a parent merged from another clone is seen as landed (no spurious rewrite); clear the chain parent after a merged-parent rewrite so the standalone bottom layer keeps the standard draft matrix and no link is attempted; `--update` re-inserts the Stack line from the PR payload's stack object (read-only) - flow --auto: `pilot-log append --reason` only on a chained dispatch, so non-chained rows keep the frozen shape - work: `--branch=new` on a chained spec carries the spec's own tracked .flow spec/task files from the pre-checkout commit when the parent tip lacks them (one bookkeeping commit), so admission sees them - tests: fixtures for the other-clone merge, the carry-over, the rewritten standalone layer, and the update-time stack line refresh Task: fn-152-make-pr-opt-in-stack-linking-via-github.1 Claude-Session: https://claude.ai/code/session_01JFYv5JtK8HAsshpWRAT5YL
…en its title to fit the PR title rule Claude-Session: https://claude.ai/code/session_01JFYv5JtK8HAsshpWRAT5YL
2d70e23 to
265c08b
Compare
A fixed POSIX PATH left no git.exe for the ls-remote read, so every chain state reported a failed remote query on the Windows unit job. Claude-Session: https://claude.ai/code/session_01JFYv5JtK8HAsshpWRAT5YL
…ck #434) Claude-Session: https://claude.ai/code/session_01JFYv5JtK8HAsshpWRAT5YL

Dependent-spec chains: branch from the parent, link GitHub stacks
A spec that depends on another spec waits today until the parent's pull request has merged. This PR removes that wait: a dependent spec branches from its parent, targets the parent's PR, and on GitHub joins its stack.
TL;DR
Not in this PR (by design)
The change, top to bottom
Dependent specs chain on their parent's branch instead of waiting for its merge: one flowctl predicate owns eligibility, work forks from the parent tip, make-pr targets the parent PR and links a GitHub stack, and flow --auto parks with the predicate's reason; specs with no dependency edges are untouched.
fn-152-chain-authoring-2d70e23bdf8cb118f5cac40f1d9e9982dc35b4a3dd12699ddafa2d70e23bdf8ce3fe33697b0767ec796eafbbfdf2Legend:
WHYPRINCIPLESTEPKEPTVERIFY·NEWMODIFIEDDELETEDRENAMEDCOPIED·CANONICALGENERATEDMECHANICALWHY1. Problem — A dependent spec waits for its parent's PR to merge: flow --auto parks it, backlog mode logs it blocked, and a direct work run builds it off the default branch with no dependency check.Evidence: source:spec, source:rid-R1, source:rid-R3, R-ID:R1, R-ID:R3
PRINCIPLE2. Principle — One predicate owns chain eligibility in flowctl and every consumer calls it; chain identity is derived from git history, never from a scratch file or a sidecar field; no config key.Evidence: source:spec, source:rid-R2, source:rid-R10, R-ID:R2, R-ID:R10
STEP3. The chain predicate and the admission gate — flowctl spec chain evaluates eligibility (parent open, all tasks done, branch on origin, linear only, one ls-remote) and the spec-level admission gate treats the chain parent as satisfied.Evidence: source:spec, source:rid-R1, source:rid-R2, source:rid-R2a, source:rid-R10, source:rid-R11, source:task, R-ID:R1, R-ID:R2, R-ID:R2a, R-ID:R10, R-ID:R11, task:fn-152-make-pr-opt-in-stack-linking-via-github.1
MODIFIEDCANONICALplugins/flow-next/scripts/flowctl.pyGenerated/mechanical files (1)
MODIFIEDMECHANICALplugins/flow-next/scripts/flowctl_tracker/MANIFEST.jsonSTEP4. Selection parks with the predicate's reason — flow --auto ready and backlog selection call the predicate, park without a strike, and prefix the verdict and decision-log reason with the chain parent; the attended ladder applies the same command.Evidence: source:spec, source:rid-R3, source:rid-R9, source:task, R-ID:R3, R-ID:R9, task:fn-152-make-pr-opt-in-stack-linking-via-github.1
MODIFIEDCANONICALplugins/flow-next/skills/flow-next-flow/auto.mdMODIFIEDCANONICALplugins/flow-next/skills/flow-next-flow/references/backlog-mode.mdMODIFIEDCANONICALplugins/flow-next/skills/flow-next-flow/workflow.mdGenerated/mechanical files (3)
MODIFIEDGENERATEDplugins/flow-next/codex/skills/flow-next-flow/auto.mdMODIFIEDGENERATEDplugins/flow-next/codex/skills/flow-next-flow/references/backlog-mode.mdMODIFIEDGENERATEDplugins/flow-next/codex/skills/flow-next-flow/workflow.mdSTEP5. Work branches from the parent's remote tip — The spec branch forks from origin/<parent>, the spec base is that merge-base, --branch=current checks ancestry, and spec files are carried across when the parent tip lacks them.Evidence: source:spec, source:rid-R4, source:task, R-ID:R4, task:fn-152-make-pr-opt-in-stack-linking-via-github.1
MODIFIEDCANONICALplugins/flow-next/skills/flow-next-work/phases.mdMODIFIEDCANONICALplugins/flow-next/skills/flow-next-work/SKILL.mdGenerated/mechanical files (2)
MODIFIEDGENERATEDplugins/flow-next/codex/skills/flow-next-work/phases.mdMODIFIEDGENERATEDplugins/flow-next/codex/skills/flow-next-work/SKILL.mdSTEP6. Make-pr detects the chain from history and links the stack — The chain rung sets the base to the parent's remote-tracking ref when its PR is open, rewrites a merged parent from the merge-base boundary on a create run only, links the stack with integer-typed REST payloads, degrades in one line, and creates chained layers ready.Evidence: source:spec, source:rid-R5, source:rid-R6, source:rid-R7, source:rid-R8, source:task, R-ID:R5, R-ID:R6, R-ID:R7, R-ID:R8, task:fn-152-make-pr-opt-in-stack-linking-via-github.1
MODIFIEDCANONICALplugins/flow-next/skills/flow-next-make-pr/workflow.mdMODIFIEDCANONICALplugins/flow-next/skills/flow-next-make-pr/create-and-finalize.mdMODIFIEDCANONICALplugins/flow-next/skills/flow-next-make-pr/SKILL.mdGenerated/mechanical files (3)
MODIFIEDGENERATEDplugins/flow-next/codex/skills/flow-next-make-pr/workflow.mdMODIFIEDGENERATEDplugins/flow-next/codex/skills/flow-next-make-pr/create-and-finalize.mdMODIFIEDGENERATEDplugins/flow-next/codex/skills/flow-next-make-pr/SKILL.mdSTEP7. Fixtures over a bare origin and a stubbed gh — flowctl chain states and admission gates, plus fence fixtures for every consumer under set -e with a recording gh stub.Evidence: source:spec, source:rid-R13, source:task, R-ID:R13, task:fn-152-make-pr-opt-in-stack-linking-via-github.1
NEWCANONICALplugins/flow-next/tests/test_spec_chain.pyNEWCANONICALplugins/flow-next/tests/test_chain_consumer_fixtures.pyMODIFIEDCANONICALplugins/flow-next/tests/fixtures/land_chain_gh_stub.pyMODIFIEDCANONICALplugins/flow-next/tests/test_flowctl_surface.pyMODIFIEDCANONICALplugins/flow-next/tests/test_review_prompt_constraints.pySTEP8. Documentation, glossary, changelog, and spec state — flowctl reference rows, orchestration and teams pages, four glossary terms, the changelog entry, the 2026-08-27 memory entry superseded by the chain rules, and the spec state this branch carries.Evidence: source:spec, source:rid-R12, source:task, R-ID:R12, task:fn-152-make-pr-opt-in-stack-linking-via-github.1
MODIFIEDCANONICALplugins/flow-next/docs/flowctl.mdMODIFIEDCANONICALplugins/flow-next/docs/orchestration.mdMODIFIEDCANONICALplugins/flow-next/docs/teams.mdMODIFIEDCANONICALGLOSSARY.mdMODIFIEDCANONICALCHANGELOG.mdGenerated/mechanical files (9)
MODIFIEDGENERATEDplugins/flow-next/codex/docs/flow-next/flowctl.mdMODIFIEDGENERATEDplugins/flow-next/codex/docs/flow-next/orchestration.mdMODIFIEDGENERATEDplugins/flow-next/codex/docs/flow-next/teams.mdMODIFIEDMECHANICAL.flow/memory/knowledge/workflow/stacked-pr-squash-close-recovery-2026-08-27.mdNEWMECHANICAL.flow/memory/bug/runtime-errors/skill-fences-that-degrade-only-without-2026-09-13.mdMODIFIEDMECHANICAL.flow/specs/fn-152-make-pr-opt-in-stack-linking-via-github.mdMODIFIEDMECHANICAL.flow/specs/fn-152-make-pr-opt-in-stack-linking-via-github.jsonNEWMECHANICAL.flow/tasks/fn-152-make-pr-opt-in-stack-linking-via-github.1.mdNEWMECHANICAL.flow/tasks/fn-152-make-pr-opt-in-stack-linking-via-github.1.jsonKEPT9. Kept — No configuration key; the gh-stack extension is never required; a spec with no dependency edges takes the byte-identical cascade, draft matrix, and verdict output.Evidence: source:spec, source:rid-R10, source:rid-R11, R-ID:R10, R-ID:R11
VERIFY10. Verify — Full suite 220 files, 4991 tests, 0 failures on the committed tree; ruff clean; codex impl-review NEEDS_WORK with 5 findings then SHIP.Evidence: source:task, task:fn-152-make-pr-opt-in-stack-linking-via-github.1
Critical changes
plugins/flow-next/tests/test_chain_consumer_fixtures.py(+524/-0 lines)plugins/flow-next/scripts/flowctl.py(+235/-44 lines)plugins/flow-next/tests/test_spec_chain.py(+225/-0 lines).flow/specs/fn-152-make-pr-opt-in-stack-linking-via-github.json(+190/-9 lines)GLOSSARY.md(+168/-0 lines)How to review this PR
The pipeline already verified this — you don't re-check it from scratch:
python3 scripts/run_tests_parallel.pyon the committed tree, 220 files, 4991 tests, 0 failures; the three focused chain and land suites green;uvx ruff@0.16.0 check .green; the Codex mirror regenerated twice.Your job — the calls the pipeline can't make:
Review plan
Must review (~25%)
plugins/flow-next/scripts/flowctl.py— second highest-churn file (+235/-44) and the only Python in the change — Does the chain predicate refuse every non-linear shape and treat a remote failure as failure rather than as an absent branch, and does the admission gate admit only the chain parent? — openevaluate_spec_chainandspec_blocked_by_depsplugins/flow-next/skills/flow-next-make-pr/workflow.md— the base cascade changes (+137/-9) in the fence make-pr runs on every create — Is the merge-base test the only chain detector, and can the rewrite run under--dry-runor--update? — open thechain-detectandchain-rewritefencesplugins/flow-next/skills/flow-next-make-pr/create-and-finalize.md— the stack link and the draft exception (+85) — Are the stack payloads integer-typed and does every non-2xx degrade to a plain chain layer without failing the create? — open thestack-linkanddraft-matrixfencesplugins/flow-next/skills/flow-next-work/phases.md— work's branch step now forks from a parent (+46/-13) — Does a--branch=currentrun without the parent tip in its ancestry stop before any task starts? — open thework-branchfenceSpot-check
plugins/flow-next/tests/test_chain_consumer_fixtures.py(+524) andplugins/flow-next/tests/test_spec_chain.py(+225) — the fixtures are the proof for R13; sample one scenario per consumer against the spec's error cases.plugins/flow-next/tests/fixtures/land_chain_gh_stub.py,plugins/flow-next/tests/test_flowctl_surface.py,plugins/flow-next/tests/test_review_prompt_constraints.py— stub extension and surface pins.plugins/flow-next/skills/flow-next-flow/auto.md,plugins/flow-next/skills/flow-next-flow/references/backlog-mode.md,plugins/flow-next/skills/flow-next-flow/workflow.md— the dependency conjunct is replaced by the predicate call and the reason prefix; confirm no jq copy of the rule remains.plugins/flow-next/skills/flow-next-make-pr/SKILL.mdandplugins/flow-next/skills/flow-next-work/SKILL.md— pointer edits.GLOSSARY.md,CHANGELOG.md,plugins/flow-next/docs/flowctl.md,plugins/flow-next/docs/orchestration.md,plugins/flow-next/docs/teams.md) — glossary terms, flowctl rows, changelog; prose only.Safe to skim (~30%)
plugins/flow-next/codex/— regenerated bysync-codex.sh, guard-verified — skim..flow/plus the trackerMANIFEST.jsonhash — spec, task, and memory state, not hand-written code — skim.Structural changes
The change adds no new module and no new import edge; every changed area already existed.
plugins/flow-next/scripts/flowctl.pygains the chain predicate that the skill prose underplugins/flow-next/skills/flow-next-flow/,flow-next-work/, andflow-next-make-pr/now calls, and the tests underplugins/flow-next/tests/gaintest_spec_chain.pyandtest_chain_consumer_fixtures.py. The Codex mirror underplugins/flow-next/codex/is regenerated for each changed skill and docs file, andGLOSSARY.md, the docs pages, and the.flow/state round it out.Decisions made
tracker.readyStateis a deterministic CLI verb (flowctl pilot strikes clear <spec-id>), NOT board-native transition detection.Memory left behind
Bugs captured during this spec:
bug/build-errors/canonical-3c-edits-vanish-from-codex-2026-08-28— fn-208.2 added FORBIDDEN and TIMEBOX lines to the canonical work phases.md 3c worker dispatch template.bug/build-errors/changelog-entry-landed-in-a-released-2026-08-01— A worker appended an## UnreleasedCHANGELOG entry that actually landed inside the most recent RELEASED version section (## [flow-next 3.13.1]), retroactively attributing unreleased work to a shipped release.bug/build-errors/codex-home-rewrite-both-spellings-2026-08-02— Replacing a hardcoded$HOME/.codexwith${CODEX_HOME:-$HOME/.codex}across a generated artifact tree looks like a blanket substitution.bug/build-errors/concurrent-gating-draws-soft-terms-2026-08-21— The fn-203 rolling-frontier eval pre-registration froze "all three arms launched same-second on one machine (contention hits all arms equally)" as its batch plan, plus soft terms ("~3x", "with margin", "borderline") and an unbounded batch-…bug/build-errors/concurrent-loop-skill-prose-linear-2026-08-22— The fn-203.4 beta scheduler reference (rolling-frontier work variant) shipped its Phase-3 loop as "handle all of 3d (integrate + review + fix loop + done), then recompute admission" - which silently serializes admission behind each task's…bug/build-errors/delegating-cli-wrapper-inherits-2026-08-30— The first cut offlowctl memory upsert(fn-212) drew four review findings, all the same shape: a delegating wrapper inherits the delegate's assumptions unless each one is re-checked against the wrapper's own contract.bug/build-errors/prose-tick-lock-claim-before-read-2026-08-28— A prose-level tick lock added to the land conductor (fn-208.1) drew four review findings in one round: the ledger snapshot was read before the claim was taken (stale-snapshot gating), age-only stale clearing could evict a live long tick an…bug/build-errors/skill-flag-gating-a-durable-write-needs-2026-08-31— Capture's new--no-planflag (fn-214 R5) was parsed with the same substring idiom as the file's other flags ([[ "$RAW_ARGS" == *"--no-plan"* ]]).bug/test-failures/test-asserted-a-public-envelope-that-2026-08-01— A spec criterion said per-briefingstatusinchart show --json"remains the single source of truth for capture-readiness".bug/test-failures/test-runner-timeout-must-kill-a-process-2026-08-04— A file-level test runner that shards withsubprocess.run(cmd, timeout=N)has TWO unbounded hang paths, and both hide behind a passing local suite:bug/test-failures/two-independent-resolve-calls-faked-a-2026-08-04—leaf_is_safe(flowctl_tracker/lifecycle/helpers.py) proved containment by resolving base and leaf INDEPENDENTLY and comparing (base_real not in target_real.parents).bug/test-failures/windows-83-path-test-failures-were-2026-08-04— Two Windows-only test failures were recorded in the fn-120 characterization as "CLI--fileargs fail from 8.3 short paths".bug/runtime-errors/land-chain-fences-a-failed-read-is-2026-09-13— fn-149.1 added chain/stack landing to the land skill as prose fences.bug/runtime-errors/one-shot-keyed-to-an-earlier-captured-2026-08-19— fn-200.2 added a one-shot-per-head Phase 3 action to the land workflow (request-reviewers): atomicmkdirclaim keyed to(PR, HEAD_OID), then remote mutations, then a ledger write.bug/runtime-errors/same-owner-alias-re-registration-must-2026-08-02—validate_and_build_initial_mapruns twice perchart create --initial-map-file: a provisional pass under a sentinel chart id (fn-999999999) and a real pass after allocation.bug/runtime-errors/skill-fences-that-degrade-only-without-2026-09-13— The make-pr stack-link fence captured a failinggh apiPOST asLINK_OUT=$(gh api ...); LINK_RC=$?and then branched onLINK_RCto print the documented one-line degrade.bug/security/managed-review-transport-must-bound-2026-09-08— A valid local review provider failure could echo its scoped credential through ordinary CLI diagnostics.bug/integration/backend-special-case-in-a-shared-helper-2026-09-05— Adding theflowctl claudereview subcommands (fn-221.2), the primary commands rejected a foreign--specthrough_resolve_claude_review_spec, butvalidateanddeep-passroute through the shared_resolve_session_pass_spec, whose…bug/integration/ci-path-classification-must-include-2026-09-05— Git diff --name-only can report only the destination of a detected rename.bug/integration/cross-family-review-claims-key-on-the-2026-09-05— Documenting theclaudereview backend (fn-221.4), every overview passage called the review "cross-family" from Codex, Cursor, Grok Build, Droid and OpenCode and "same-family" from Claude Code - keyed on the host name.bug/integration/headless-review-backend-error-envelope-2026-09-05— Adding theclaude -preview backend (fn-221.1), the runner shared cursor's lenient result parser and returned an error envelope'sresulttext as reviewer output with only the exit code flipped to 1.bug/integration/installer-must-own-what-it-deletes-2026-08-21— PR Route-aware next-step guidance in capture and plan closers (fn-202) #363's docs-install stage (round-3 fix) recursively deleted$CODEX_HOME/docs/reachand copied loose generic names (README.md) into the shared$CODEX_HOME/docs/— destroying non-flow-next user data on every install.bug/integration/land-evidence-field-defaulted-to-off-on-2026-08-19— The land workflow initialized the per-PR evidence fieldREVIEWERS_STATE=offunconditionally and only overwrote it inside the §2.6b predicate branch.bug/integration/scheduler-prose-asserted-wrong-config-2026-08-22— Review (fn-203.6, codex) caught two P1s in the rolling-scheduler beta prose: (1) the plan-sync gate claimedplanSync.enabledoff "(null/false/missing - the default)", but the SHIPPED default istrue(get_default_config()in flowctl.p…bug/data/adding-a-key-to-a-content-hash-orphans-2026-08-01—chart briefingis idempotent on a content fingerprint.bug/data/docs-for-a-hash-identity-fix-inherit-2026-08-01— The docs task for a content-hash fix (fn-154: reopen epoch folded into_briefing_fingerprint) shipped prose that was true in spirit and wrong in scope, twice in a row.Glossary / strategy notes
Glossary: added
Chain,Frontier,Layer,Stack.Generated by
/flow-next:make-prfrom fn-152-make-pr-opt-in-stack-linking-via-github againstfn-149-land-hardening-survive-stacked-pr-autoon 2026-09-13.