flow-workflows 0.92.0 → 0.93.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/adapters/codex/CORE.md +12 -5
  2. package/adapters/codex/skills/flow-bug-fix/SKILL.md +9 -1
  3. package/adapters/codex/skills/flow-bug-investigate/SKILL.md +11 -0
  4. package/adapters/codex/skills/flow-feat-build/SKILL.md +9 -2
  5. package/adapters/codex/skills/flow-feat-design/SKILL.md +30 -0
  6. package/adapters/codex/skills/flow-feat-plan/SKILL.md +1 -0
  7. package/adapters/codex/skills/flow-work-README/SKILL.md +4 -0
  8. package/adapters/codex/skills/flow-work-handoff/SKILL.md +19 -5
  9. package/adapters/codex/skills/flow-work-run/SKILL.md +18 -2
  10. package/adapters/gemini/CORE.md +12 -5
  11. package/adapters/gemini/commands/flow/bug/fix.toml +9 -1
  12. package/adapters/gemini/commands/flow/bug/investigate.toml +11 -0
  13. package/adapters/gemini/commands/flow/feat/build.toml +9 -2
  14. package/adapters/gemini/commands/flow/feat/design.toml +30 -0
  15. package/adapters/gemini/commands/flow/feat/plan.toml +1 -0
  16. package/adapters/gemini/commands/flow/work/README.toml +4 -0
  17. package/adapters/gemini/commands/flow/work/handoff.toml +19 -5
  18. package/adapters/gemini/commands/flow/work/run.toml +18 -2
  19. package/adapters/hermes/CORE.md +12 -5
  20. package/adapters/hermes/skills/flow-bug-fix/SKILL.md +9 -1
  21. package/adapters/hermes/skills/flow-bug-investigate/SKILL.md +11 -0
  22. package/adapters/hermes/skills/flow-feat-build/SKILL.md +9 -2
  23. package/adapters/hermes/skills/flow-feat-design/SKILL.md +30 -0
  24. package/adapters/hermes/skills/flow-feat-plan/SKILL.md +1 -0
  25. package/adapters/hermes/skills/flow-work-README/SKILL.md +4 -0
  26. package/adapters/hermes/skills/flow-work-handoff/SKILL.md +19 -5
  27. package/adapters/hermes/skills/flow-work-run/SKILL.md +18 -2
  28. package/adapters/opencode/CORE.md +12 -5
  29. package/adapters/opencode/commands/flow-bug-fix.md +9 -1
  30. package/adapters/opencode/commands/flow-bug-investigate.md +11 -0
  31. package/adapters/opencode/commands/flow-feat-build.md +9 -2
  32. package/adapters/opencode/commands/flow-feat-design.md +30 -0
  33. package/adapters/opencode/commands/flow-feat-plan.md +1 -0
  34. package/adapters/opencode/commands/flow-work-README.md +4 -0
  35. package/adapters/opencode/commands/flow-work-handoff.md +19 -5
  36. package/adapters/opencode/commands/flow-work-run.md +18 -2
  37. package/package.json +1 -1
  38. package/plugins/flow/.claude-plugin/plugin.json +1 -1
  39. package/plugins/flow/CHANGELOG.md +28 -0
@@ -17,7 +17,7 @@ Every `$flow-*` command assumes these rules. They are stated once, here, so a co
17
17
  carries what is specific to its phase. Read this once per session; a command that says "load
18
18
  `flow-core`" means this file.
19
19
 
20
- **This file belongs to flow `0.92.0`.** Compare it once, at the start of the session, against
20
+ **This file belongs to flow `0.93.0`.** Compare it once, at the start of the session, against
21
21
  `version` in `~/.claude/flow/.claude-plugin/plugin.json`. The two differing means the session
22
22
  is running a **mixture** — the commands from one copy of the plugin, these shared rules from another
23
23
  — which is exactly what happens when a branch or a release candidate is loaded over an installed
@@ -253,7 +253,7 @@ assumed:
253
253
  <cmd> ask --question-file F --options-file F [--recommended N] [--free-text] [--gate NAME]
254
254
  <cmd> publish --title-file F --body-file F [--part N --of M]
255
255
  <cmd> blocked --reason-file F
256
- <cmd> done [--summary-file F]
256
+ <cmd> done [--summary-file F] [--depends-on N]…
257
257
  <cmd> reply --thread ID --body-file F
258
258
  ```
259
259
 
@@ -314,12 +314,18 @@ runs the study phases as any run does, and three rules make it stop where it sho
314
314
  study to code crosses that door, an answered question that resumes straight into `build`
315
315
  included, so the phases before it carry no clause. The run kind is read there, never stored: the
316
316
  approval reuses the same work folder with `why = start`.
317
- - **At the door**, a study run first writes the statement of the change when the study holds none
317
+ - **At the door**, a study run first checks each `meta.json.ticket_deps` entry: `N` = its `ticket`
318
+ without a leading `#`, the form `ticket --json` prints `number`, and it must match
319
+ `[A-Za-z0-9][A-Za-z0-9._-]*` — an `N` that does not (a space, a shell character, a leading `-`)
320
+ is never put on a command line: `blocked`, naming the entry, before anything below is written.
321
+ Then it writes the statement of the change when the study holds none
318
322
  (the command says which), then `done-summary.md` — the `flow-study` block of `$flow-work-handoff`
319
323
  §5, the same sections from the same sources, and, when `01-context.md` lists `## Study revisions`,
320
324
  the line `Revised at the reviewer's request: <n> reply(ies)` first under `## What and why`. It
321
325
  overwrites `00-summary.md`, sets the panel's `attention` to `done` and closes with
322
- `done --summary-file`. Nothing else moves — no `mrs[]` entry, no branch, no brief — so
326
+ `done --summary-file`, plus one `--depends-on N` per `meta.json.ticket_deps` entry, in its order —
327
+ so the orchestrator can hold the ticket until those land; what it does with them is its call.
328
+ Nothing else moves — no `mrs[]` entry, no branch, no brief — so
323
329
  `meta.json.phase` stays at the last study phase and approval is an ordinary `start`.
324
330
  - **A person's changes rewind the study.** `$flow-work-run` §2 adds the reply to the ticket in
325
331
  `01-context.md` and sends the work back to `context`; the study phases then run again whole, the
@@ -332,7 +338,7 @@ person asking for changes, a ticket that already has code — is `$flow-work-run
332
338
  | Where | In `unattended` |
333
339
  |---|---|
334
340
  | Business brief before code (`build`, `fix`) | record — the brief is written to the phase artifact, marked `recorded (unattended)`, and travels in the MR/PR body |
335
- | The door to code in a study run (`build`, `fix` pre-flight) | done — `done-summary.md`, the study as `$flow-work-handoff` §5 writes it for the ticket |
341
+ | The door to code in a study run (`build`, `fix` pre-flight) | done — `done-summary.md`, the study as `$flow-work-handoff` §5 writes it for the ticket, and one `--depends-on` per `meta.json.ticket_deps` entry |
336
342
  | Push and MR/PR creation (`ship`) | `publish` — the run commits on its own branch, writes title and body, and hands them over; the orchestrator pushes and opens the draft |
337
343
  | Writing a DB schema change, index or migration — in any phase, a review's own fixes included — or applying one (`validate`) | question `migration`, before the first line of it is written or run — except in `respond` and `green`, where it is blocked: a schema change agreed after the MR/PR exists goes back through `build` |
338
344
  | `review` with blockers or high-severity findings | question `high_findings` |
@@ -352,6 +358,7 @@ person asking for changes, a ticket that already has code — is `$flow-work-run
352
358
  | The next MR/PR of a train (`ship` close) | record — not started: one MR/PR per unattended run |
353
359
  | `build` with no startable MR/PR (its `depends_on` not `merged` in `meta.json`) | blocked — the reason names what must merge; an unattended `build` never stacks a branch on another, and never creates a remote branch |
354
360
  | The parent of a stacked MR/PR merged — rebase and force-push | blocked — never force-push unattended |
361
+ | An approved study's branch (`run`, `why = start`, no build or fix begun) | record — fast-forwarded to its base when it holds no commit of its own (`$flow-work-run` §2); the base unreadable or the fast-forward failing → blocked. Not integrating: nothing of the run's is merged |
355
362
  | Integrating the base into the MR/PR's branch (`green`) | on `why = conflict`, record — `git merge origin/<base>` as the orchestrator left it, never rebase, each conflict resolved on its merits (`$flow-work-green` §5.C); on any other `why`, blocked — the orchestrator brings the base in |
356
363
  | Any offer about the work's own bookkeeping — a tracker issue, the first `KNOWLEDGE.md` or a knowledge save, a FLOW convention, an agent lesson or a saved specialist, the follow-up survey, carrying files over from the main checkout, a contract handoff, archiving the work | record — `Later`; nothing written outside the work folder |
357
364
  | A step the command cannot complete (a red suite it cannot fix, a tool missing, auth failing, the same check failing a second time) | blocked |
@@ -36,7 +36,15 @@ Read `~/.claude/flow/CORE.codex.md` first (shared rules: `FLOW.md` step 0, model
36
36
  would make — from the root cause and the constraints for the fix when the investigation has them,
37
37
  the reproduction and the ticket otherwise, `## Study revisions` included — the person approves a
38
38
  stated change, never `none`; then the summary, and
39
- `<cmd> done --summary-file <work>/done-summary.md`. Nothing else in this command runs.
39
+ `<cmd> done --summary-file <work>/done-summary.md`, plus `--depends-on N` per
40
+ `meta.json.ticket_deps` entry, `N` its `ticket` with the leading `#` dropped — a bare `#` starts a
41
+ shell comment — and checked against `[A-Za-z0-9][A-Za-z0-9._-]*` before anything is written, else
42
+ `blocked` (flow-core §2.1). Nothing else in this command runs.
43
+ - **`meta.json.ticket_deps` not empty, and not `unattended`** → the study rests on other tickets
44
+ (`$flow-feat-design` §5.1): name each — `<ticket> — <why>` — in the brief's stop (§2), one line,
45
+ so whoever builds confirms the piece is on **this work's branch**, not only on its base — the
46
+ branch was cut when the study began, and a piece that merged since needs the base brought in
47
+ before writing against it. Nothing is checked; waiting is the builder's call.
40
48
  - `size` `XS`: may start without `investigate`, but require a 2-3 line description of the fix — an `## Intended fix` in `03-investigation.md` is that description.
41
49
  - **An `## Intended fix` in `03-investigation.md` binds §2's brief at every size**: it is what a person approved at the end of a study (flow-core §2.1).
42
50
  - `size` ≥ S: require `investigate` in `phases_done` (on S it will have run its reproduction half only).
@@ -174,10 +174,21 @@ If `agents.fanout_tool` is set in `FLOW.md`, run the sweep through that tool; br
174
174
  - Do not touch X because…
175
175
  - Consider Y because…
176
176
 
177
+ ## Depends on other tickets
178
+ <§4.1: `- <ticket> — <what it brings that the base branch lacks>`, then "Not dependencies (either order works)", or "none".>
179
+
177
180
  ## Investigation challenges
178
181
  <filled in by §5 with the challenger table>
179
182
  ```
180
183
 
184
+ ### 4.1 Tickets this fix depends on
185
+
186
+ A fix can rest on another ticket the same way a design can — a corrected contract, a migration, a
187
+ decision that lands only when that ticket merges. Detect and record it before the challenger, exactly
188
+ as `$flow-feat-design` §5.1 says: the same rule, the same "Not dependencies (either order works)"
189
+ line, the same `meta.json.ticket_deps` entries, written under `## Depends on other tickets` in
190
+ `03-investigation.md`.
191
+
181
192
  ## 5. Root cause challenge (challenger)
182
193
 
183
194
  Before closing, launch a `Agent general-purpose` challenger with this task:
@@ -33,8 +33,15 @@ Implementation phase. Code is written here.
33
33
  and act as flow-core §2.1 "A study run" says. On `study` → this run writes no code: `size` XS and
34
34
  no `03-design.md` → first write the 2–3 line note below as that file, from `01-context.md` (its
35
35
  `## Study revisions` included) — the person approves a stated change, never `none`; then the
36
- summary, and `<cmd> done --summary-file <work>/done-summary.md`. Nothing else in this command
37
- runs.
36
+ summary, and `<cmd> done --summary-file <work>/done-summary.md`, plus `--depends-on N` per
37
+ `meta.json.ticket_deps` entry, `N` its `ticket` with the leading `#` dropped — a bare `#` starts a
38
+ shell comment — and checked against `[A-Za-z0-9][A-Za-z0-9._-]*` before anything is written, else
39
+ `blocked` (flow-core §2.1). Nothing else in this command runs.
40
+ - **`meta.json.ticket_deps` not empty, and not `unattended`** → the study rests on other tickets
41
+ (`$flow-feat-design` §5.1): name each — `<ticket> — <why>` — in the brief's stop (§2), one line,
42
+ so whoever builds confirms the piece is on **this work's branch**, not only on its base — the
43
+ branch was cut when the study began, and a piece that merged since needs the base brought in
44
+ before writing against it. Nothing is checked; waiting is the builder's call.
38
45
  - `size` M/L: require both `03-design.md` **and** `04-mr-plan.md`. Plan missing → send to `$flow-feat-plan`; design missing → send to `$flow-feat-design`.
39
46
  - `size` XS/S: may start without a design — ask the user for a 2-3 line note on what will be done and save it as a minimal `03-design.md`. No MR/PR plan (always 1 MR/PR).
40
47
  - **If `meta.json.mrs` has more than one entry**: pick the **startable** MR/PR — the `pending` one with the **lowest `n` whose `depends_on` are all `merged`**. Dependencies still `pending`/`in_progress` → not startable, even with a low `n`. (No `wave`/`depends_on` — an older plan — → "first pending by `n`".)
@@ -290,6 +290,9 @@ Consolidate outputs into `.claude/work/<TICKET>/03-design.md`:
290
290
  - **Invariant**: <e.g. "after assignTo(user), ticket.assignee == user AND ticket.status != 'unassigned'">
291
291
  - **Edge / boundary**: <e.g. "empty input list → returns 0, does not throw">
292
292
 
293
+ ## Depends on other tickets
294
+ <§5.1: one line per ticket this design cannot be built without — `- <ticket> — <what it brings that the base branch lacks>` — then, under "Not dependencies (either order works)", each premise on another ticket that holds whichever merges first, with one line why. Neither → "none".>
295
+
293
296
  ## Defensive mechanisms and their justification
294
297
  <One row per validation, guard, retry, lock, fallback, cache, idempotency, queue, or flag the design introduces. If you cannot name a REAL and PRESENT scenario that justifies it, the piece is unnecessary — remove it from the design.>
295
298
 
@@ -318,6 +321,33 @@ Consolidate outputs into `.claude/work/<TICKET>/03-design.md`:
318
321
 
319
322
  **"Acceptance criteria"**: start from the provisional list in `01-context.md` (pinned from the ticket in `$flow-feat-start`), fold in the clarifications and the internal/external contracts decided here, and promote it to the canonical enumerated list `$flow-feat-validate` gates against — observable, verifiable, proportional to size.
320
323
 
324
+ ### 5.1 Tickets this design depends on
325
+
326
+ Fill `## Depends on other tickets` now, before the challenger of §6 reads the design. The study is
327
+ often built by someone who never saw it made — another person, an unattended server — and who
328
+ starts from the base. A design that rests on what another ticket has not delivered yet leaves that
329
+ builder to find the piece missing and either ask or redo it.
330
+
331
+ - **A dependency** is a piece, contract or decision the chosen design needs, that is **not on the
332
+ base this work builds from** (`meta.json.base`, empty → `git.default_base`) — checked there, after
333
+ a best-effort `git fetch origin <base>`, never assumed from a local ref — and that **another
334
+ ticket** of this tracker brings. Missing from the base with no ticket bringing it → it is scope
335
+ or an open question, not a dependency. A ticket in another repo is not a `ticket_deps` entry —
336
+ the orchestrator only resolves this tracker's numbers — so write it in the section as a
337
+ cross-repo dependency, for a person, and leave it out of the list.
338
+ - **Not a dependency**: a premise that holds whichever ticket merges first. Say so under "Not
339
+ dependencies (either order works)", so nobody waits for it.
340
+ - **Record each one** in that section and in `meta.json.ticket_deps` —
341
+ `{ "ticket": "<the reference as a ticket body writes it: #253, MT-123>", "why": "<one line>" }`,
342
+ never one twice. `ticket_deps` is the record the exits read (`$flow-work-handoff` §5, the door to
343
+ code of an unattended study run, flow-core §2.1); the section is the study's prose. Each run of
344
+ this phase writes the list whole from the design it closes; `plan`, which runs after it, appends
345
+ its own again. A §6 response or a §9 edit that changes what the design needs changes
346
+ the section and the list together. None → `[]` and "none".
347
+
348
+ `ticket_deps` is not `mrs[].depends_on`: that one orders this work's own MRs/PRs, this one names
349
+ other tickets. Flow never checks whether a dependency has closed — whoever builds waits for it.
350
+
321
351
  ## 6. Design challenge (challenger)
322
352
 
323
353
  Before closing, **challenge the design** with an `Agent general-purpose` and this self-contained brief:
@@ -126,6 +126,7 @@ Add the `mrs` array with the agreed plan:
126
126
  ```
127
127
 
128
128
  - `n` = execution order (topological): `depends_on` only ever references a **lower** `n`; `wave` groups MRs/PRs that can run in parallel. `$flow-feat-build §1` reads `depends_on` to pick the next **startable** MR/PR and to say which can go in parallel — keep both accurate whenever the plan is edited or renumbered.
129
+ - **Another ticket is not a `depends_on`.** An MR/PR that turns out, while splitting, to need something another ticket brings is a dependency of the study: append it to `meta.json.ticket_deps` as `$flow-feat-design` §5.1 says, name it in that MR/PR's entry of `04-mr-plan.md` beside what it waits on, and leave `03-design.md` as it closed. The design's own entries stand as they are.
129
130
  - **`phases_done`** starts `[]` in every seeded entry: it tracks `build`/`review`/`validate` **for that MR/PR specifically**, so the gates in `$flow-feat-review §1`, `$flow-feat-validate §1` and `$flow-feat-ship §1` judge *this* MR/PR — a sibling's review never satisfies a new MR/PR's gate.
130
131
  - Estimates are **indicative**, not contractual. `$flow-feat-build` uses them as a thermometer: real work over `lines_est` +50% or `files_est + 2` triggers the "cut or continue" question (see §2.3 in build). **A thermometer is not a target** (flow-core §9): an estimate is never met by deleting comments, tests or blank lines, by compressing readable code, or by cutting where the piece is not coherent — the overrun is recorded instead, and a diff shaped to fit a number is the one case where these estimates lie to the phases downstream.
131
132
 
@@ -112,6 +112,9 @@ This system **orchestrates** the project's existing sub-agents and skills (it do
112
112
  "note": "reason if closed/superseded; empty otherwise"
113
113
  }
114
114
  ],
115
+ "ticket_deps": [
116
+ { "ticket": "#253", "why": "one line, what that ticket brings that the base branch lacks" }
117
+ ],
115
118
  "related_repos": [
116
119
  { "repo": "sibling-project", "scope": "what's needed there", "status": "pending" | "in_progress" | "done", "contract_handoff": "none" | "pending" | "published → <location>" }
117
120
  ],
@@ -160,6 +163,7 @@ This system **orchestrates** the project's existing sub-agents and skills (it do
160
163
  Two fields for the review because they are two facts. `reviewed_sha` used to be `git rev-parse HEAD` at the end of the phase, which by then held the round's own fixes — so `ship` compared a sha against itself and reported a match over code no reviewer had read. Read by `ship` at pre-flight against `HEAD`: a delta of test files and this work's own folder passes with a note in `06-review.md`; anything else stops and asks in every autonomy mode. Empty is not a mismatch; `fixed_sha` set with `reviewed_sha == HEAD` is one. `review` also reads `reviewed_sha` back as **its own base** on a re-review, so the second round reads the delta instead of the whole branch.
161
164
  - **`respond_rounds`**: rounds of `$flow-work-respond` on an MR/PR. `$flow-work-respond §1` reads it against `quality.respond_max_rounds` (empty = 3) and refuses the round that would exceed it, handing the open threads back to the user.
162
165
  - **`related_repos`**: the **other repos a task touches**. Captured at `$flow-feat-start` / `$flow-bug-start` §cross-repo, refined at `design`/`plan`, reminded at `ship`, shown by `daily`/`resume`/`status`. flow only notes and reminds — never touches the other repo. `[]` for single-repo.
166
+ - **`ticket_deps`**: the **other tickets this study cannot be built without** — something the design needs that is not on the base branch and that another ticket brings (`$flow-feat-design` §5.1, `$flow-bug-investigate` §4.1). Read at the two exits of a study: `$flow-work-handoff` §5 writes a `Depends on <ticket>` line per entry into the ticket body, and an unattended study run passes `--depends-on N` per entry on its `done` (flow-core §2.1). Not `mrs[].depends_on`, which orders this work's own MRs/PRs. Written whole each time `design` (or `investigate`) runs, and removed when an unattended study is rewound. `[]` or absent when the study depends on nothing.
163
167
  - **`contract_handoff`**: whether the sibling knows **what shape to build against** (`scope` is prose; distinct from `status`). `none` — consumes no contract declared here. `pending` — consumes one, not handed over yet. `published → <location>` — published where that side reads it (normally the tracker ticket; `$flow-feat-ship` §6.3), picked up by the sibling's `$flow-feat-start` §3.6.
164
168
  - **`followups`**: work a phase deliberately did **not** do here — a neighbouring defect found while building, a piece put out of scope while planning, a risk recorded but not mitigated, an edge case left unchecked, a prevention action from a postmortem. Written by the phase that parks it (asks nothing) when it clears the bar of flow-core §7, triaged **once** at `ship`'s Close behind a skeptic and a ceiling on how many may become questions, published in the MR/PR description while still open, and surfaced by `status`/`daily`/`next` — including from `_archive/` — until `declined` or `done`. Shape and rules: flow-core §7. `[]` when the work deferred nothing.
165
169
  - **`defaults_used`**: an empty `FLOW.md` key a phase resolved with its default instead of asking — written in `guided`/`auto`, where asking would breach the never-a-question contract (flow-core §0). Not an error and not deferred work: a record, aggregated by `$flow-doctor` across every work including `_archive/`, so a key that keeps defaulting can be pinned once instead of decided every time. `[]` when nothing defaulted.
@@ -148,13 +148,27 @@ the ticket read it there, never in the work folder; the receiver does not parse
148
148
  <!-- end flow-study -->
149
149
  ```
150
150
 
151
- Criteria keep their literal values. The plan lists each MR/PR with what it covers and what it
152
- waits on; a bug's plan is its single fix.
151
+ Criteria keep their literal values. `## Design in brief` ends with each `meta.json.ticket_deps`
152
+ entry and its why — `Depends on #253: <why>` — so whoever approves the study sees what it waits
153
+ for; an unattended study's `done-summary.md` is this block, so it carries them too. The plan
154
+ lists each MR/PR with what it covers and what it waits on; a bug's plan is its single fix.
153
155
  - **The new body**: the body read above with the block between the two markers replaced — or, with
154
- no markers yet, the block appended after a blank line. Written to a `mktemp` file outside the
156
+ no markers yet, the block appended after a blank line. Then, for each `meta.json.ticket_deps`
157
+ entry (`$flow-feat-design` §5.1) whose line is not already a line of that body — same text,
158
+ ignoring case and surrounding whitespace — one line after the end marker, in `ticket_deps` order:
159
+
160
+ ```
161
+ Depends on #253
162
+ ```
163
+
164
+ `#253` being the entry's `ticket` as it stands. Outside the markers so a later rewrite of the
165
+ block leaves it alone, and never removed: a line a person wrote by hand looks exactly like one
166
+ flow wrote, and the convention is theirs too. Written to a `mktemp` file outside the
155
167
  tree — `{BODY_FILE}` — so nothing of it can be committed later.
156
- - **Show the block and ask** with `AskUserQuestion`, **in every mode**: it writes to a shared
157
- tracker. **Write it** · **Skip**. Failure → its message in one line; the push stands.
168
+ - **Show the block, and the `Depends on` lines it adds, and ask** with `AskUserQuestion`, **in
169
+ every mode**: it writes to a shared tracker. A `Depends on` line already in the body, outside the
170
+ markers, that no entry names — a dependency a revised study dropped, or one a person added — is named in the same
171
+ question and left in place: whether it still holds is theirs to say. **Write it** · **Skip**. Failure → its message in one line; the push stands.
158
172
 
159
173
  ## 6. Close
160
174
 
@@ -67,7 +67,7 @@ of truth, and an argument that disagrees with it is ignored and named in the log
67
67
  | `conflict` | exists | `$flow-work-green` — the MR/PR conflicts with its base; which base, and where it stands, come from `<cmd> events --json` |
68
68
  | `review` · `pipeline` · `conflict` | none | `blocked` — there is no work of this ticket to answer for |
69
69
 
70
- **The work's branch, on `start` and `study`.** The current branch is `meta.json.branch` → nothing to do. It is
70
+ **The work's branch, on `start` and `study`.** The current branch is `meta.json.branch` → no switch. It is
71
71
  not — a study made on another machine arrives on the ticket's `base`, a run that died before its
72
72
  closing order is relaunched there — then, in this order:
73
73
 
@@ -90,6 +90,21 @@ closing order is relaunched there — then, in this order:
90
90
  `meta.json.branch` = it, `meta.json.base` = that base, `worktree` and `stacked_on` = `null` — paths and parents from the
91
91
  other machine mean nothing here — and record `{ "key": "unattended:rehomed", "default": "<old branch> → <name> from <base>", "phase": "run" }`.
92
92
 
93
+ **On `start`, a study's branch catches up with its base** — once on the work's branch, the "no
94
+ switch" case included, and only while no build or fix has begun (neither `05-implementation.md` nor
95
+ `04-fix.md` exists). Every command below runs in the work's own checkout — step 2's worktree when
96
+ that is where it lives. A study writes no code, so its branch still sits where the base was when the
97
+ study began; an approval that waited for the tickets the study depends on (`meta.json.ticket_deps`)
98
+ would otherwise build on a base that lacks them. `<base>` = `meta.json.base`, empty →
99
+ `git.default_base`: `git fetch origin <base>`, then `<ref>` = `origin/<base>`; the fetch failing
100
+ with a remote configured → `blocked`, since a stale ref would leave the build on the old base
101
+ unnoticed. No remote → the local `<base>`. `git merge-base --is-ancestor HEAD <ref>` and `HEAD` is not
102
+ `<ref>` → the branch holds no commit of its own and the base moved: `git merge --ff-only <ref>`, and
103
+ record `{ "key": "unattended:base-caught-up", "default": "<old short sha> → <new short sha> on
104
+ <base>", "phase": "run" }`. The fast-forward fails → `blocked`, its message as the reason. Not an
105
+ ancestor → it holds commits a previous run made: leave it as it is — bringing the base into work in
106
+ progress is the orchestrator's (flow-core §2.1).
107
+
93
108
  **A person's changes to a study, on `study`** (flow-core §2.1: they rewind it). The work's branch
94
109
  first, as above. `<work>/study-revision.md` holds the reply, as the row above wrote it. Each step
95
110
  can be run again: a run that dies part-way is finished by the next relaunch, whatever it serves.
@@ -113,7 +128,8 @@ can be run again: a run that dies part-way is finished by the next relaunch, wha
113
128
  `study-revision.md` and earlier `before-*/` folders into `<work>/before-<id7>/` — never
114
129
  overwriting a name already there — so no artifact, summary or agent answer of the discarded study
115
130
  leaks into the next. Then `meta.json`: `phase` = `context`, `phases_done` = `["context"]`, `mrs`
116
- removed (the row that blocks a work past the study guarantees nothing was built), `notes`
131
+ removed (the row that blocks a work past the study guarantees nothing was built), `ticket_deps`
132
+ removed (the redone study finds its dependencies again), `notes`
117
133
  emptied, and every `followups[]` entry a study phase wrote (`source` `design`, `plan` or
118
134
  `investigate`) removed.
119
135
  5. **Re-size**: estimate the size again with `$flow-feat-start` §4's table (`$flow-bug-start`'s for
@@ -16,7 +16,7 @@ Every `/flow:*` command assumes these rules. They are stated once, here, so a co
16
16
  carries what is specific to its phase. Read this once per session; a command that says "load
17
17
  `flow-core`" means this file.
18
18
 
19
- **This file belongs to flow `0.92.0`.** Compare it once, at the start of the session, against
19
+ **This file belongs to flow `0.93.0`.** Compare it once, at the start of the session, against
20
20
  `version` in `~/.claude/flow/.claude-plugin/plugin.json`. The two differing means the session
21
21
  is running a **mixture** — the commands from one copy of the plugin, these shared rules from another
22
22
  — which is exactly what happens when a branch or a release candidate is loaded over an installed
@@ -252,7 +252,7 @@ assumed:
252
252
  <cmd> ask --question-file F --options-file F [--recommended N] [--free-text] [--gate NAME]
253
253
  <cmd> publish --title-file F --body-file F [--part N --of M]
254
254
  <cmd> blocked --reason-file F
255
- <cmd> done [--summary-file F]
255
+ <cmd> done [--summary-file F] [--depends-on N]…
256
256
  <cmd> reply --thread ID --body-file F
257
257
  ```
258
258
 
@@ -313,12 +313,18 @@ runs the study phases as any run does, and three rules make it stop where it sho
313
313
  study to code crosses that door, an answered question that resumes straight into `build`
314
314
  included, so the phases before it carry no clause. The run kind is read there, never stored: the
315
315
  approval reuses the same work folder with `why = start`.
316
- - **At the door**, a study run first writes the statement of the change when the study holds none
316
+ - **At the door**, a study run first checks each `meta.json.ticket_deps` entry: `N` = its `ticket`
317
+ without a leading `#`, the form `ticket --json` prints `number`, and it must match
318
+ `[A-Za-z0-9][A-Za-z0-9._-]*` — an `N` that does not (a space, a shell character, a leading `-`)
319
+ is never put on a command line: `blocked`, naming the entry, before anything below is written.
320
+ Then it writes the statement of the change when the study holds none
317
321
  (the command says which), then `done-summary.md` — the `flow-study` block of `/flow:work:handoff`
318
322
  §5, the same sections from the same sources, and, when `01-context.md` lists `## Study revisions`,
319
323
  the line `Revised at the reviewer's request: <n> reply(ies)` first under `## What and why`. It
320
324
  overwrites `00-summary.md`, sets the panel's `attention` to `done` and closes with
321
- `done --summary-file`. Nothing else moves — no `mrs[]` entry, no branch, no brief — so
325
+ `done --summary-file`, plus one `--depends-on N` per `meta.json.ticket_deps` entry, in its order —
326
+ so the orchestrator can hold the ticket until those land; what it does with them is its call.
327
+ Nothing else moves — no `mrs[]` entry, no branch, no brief — so
322
328
  `meta.json.phase` stays at the last study phase and approval is an ordinary `start`.
323
329
  - **A person's changes rewind the study.** `/flow:work:run` §2 adds the reply to the ticket in
324
330
  `01-context.md` and sends the work back to `context`; the study phases then run again whole, the
@@ -331,7 +337,7 @@ person asking for changes, a ticket that already has code — is `/flow:work:run
331
337
  | Where | In `unattended` |
332
338
  |---|---|
333
339
  | Business brief before code (`build`, `fix`) | record — the brief is written to the phase artifact, marked `recorded (unattended)`, and travels in the MR/PR body |
334
- | The door to code in a study run (`build`, `fix` pre-flight) | done — `done-summary.md`, the study as `/flow:work:handoff` §5 writes it for the ticket |
340
+ | The door to code in a study run (`build`, `fix` pre-flight) | done — `done-summary.md`, the study as `/flow:work:handoff` §5 writes it for the ticket, and one `--depends-on` per `meta.json.ticket_deps` entry |
335
341
  | Push and MR/PR creation (`ship`) | `publish` — the run commits on its own branch, writes title and body, and hands them over; the orchestrator pushes and opens the draft |
336
342
  | Writing a DB schema change, index or migration — in any phase, a review's own fixes included — or applying one (`validate`) | question `migration`, before the first line of it is written or run — except in `respond` and `green`, where it is blocked: a schema change agreed after the MR/PR exists goes back through `build` |
337
343
  | `review` with blockers or high-severity findings | question `high_findings` |
@@ -351,6 +357,7 @@ person asking for changes, a ticket that already has code — is `/flow:work:run
351
357
  | The next MR/PR of a train (`ship` close) | record — not started: one MR/PR per unattended run |
352
358
  | `build` with no startable MR/PR (its `depends_on` not `merged` in `meta.json`) | blocked — the reason names what must merge; an unattended `build` never stacks a branch on another, and never creates a remote branch |
353
359
  | The parent of a stacked MR/PR merged — rebase and force-push | blocked — never force-push unattended |
360
+ | An approved study's branch (`run`, `why = start`, no build or fix begun) | record — fast-forwarded to its base when it holds no commit of its own (`/flow:work:run` §2); the base unreadable or the fast-forward failing → blocked. Not integrating: nothing of the run's is merged |
354
361
  | Integrating the base into the MR/PR's branch (`green`) | on `why = conflict`, record — `git merge origin/<base>` as the orchestrator left it, never rebase, each conflict resolved on its merits (`/flow:work:green` §5.C); on any other `why`, blocked — the orchestrator brings the base in |
355
362
  | Any offer about the work's own bookkeeping — a tracker issue, the first `KNOWLEDGE.md` or a knowledge save, a FLOW convention, an agent lesson or a saved specialist, the follow-up survey, carrying files over from the main checkout, a contract handoff, archiving the work | record — `Later`; nothing written outside the work folder |
356
363
  | A step the command cannot complete (a red suite it cannot fix, a tool missing, auth failing, the same check failing a second time) | blocked |
@@ -32,7 +32,15 @@ Read `~/.claude/flow/CORE.gemini.md` first (shared rules: `FLOW.md` step 0, mode
32
32
  would make — from the root cause and the constraints for the fix when the investigation has them,
33
33
  the reproduction and the ticket otherwise, `## Study revisions` included — the person approves a
34
34
  stated change, never `none`; then the summary, and
35
- `<cmd> done --summary-file <work>/done-summary.md`. Nothing else in this command runs.
35
+ `<cmd> done --summary-file <work>/done-summary.md`, plus `--depends-on N` per
36
+ `meta.json.ticket_deps` entry, `N` its `ticket` with the leading `#` dropped — a bare `#` starts a
37
+ shell comment — and checked against `[A-Za-z0-9][A-Za-z0-9._-]*` before anything is written, else
38
+ `blocked` (flow-core §2.1). Nothing else in this command runs.
39
+ - **`meta.json.ticket_deps` not empty, and not `unattended`** → the study rests on other tickets
40
+ (`/flow:feat:design` §5.1): name each — `<ticket> — <why>` — in the brief's stop (§2), one line,
41
+ so whoever builds confirms the piece is on **this work's branch**, not only on its base — the
42
+ branch was cut when the study began, and a piece that merged since needs the base brought in
43
+ before writing against it. Nothing is checked; waiting is the builder's call.
36
44
  - `size` `XS`: may start without `investigate`, but require a 2-3 line description of the fix — an `## Intended fix` in `03-investigation.md` is that description.
37
45
  - **An `## Intended fix` in `03-investigation.md` binds §2's brief at every size**: it is what a person approved at the end of a study (flow-core §2.1).
38
46
  - `size` ≥ S: require `investigate` in `phases_done` (on S it will have run its reproduction half only).
@@ -170,10 +170,21 @@ If `agents.fanout_tool` is set in `FLOW.md`, run the sweep through that tool; br
170
170
  - Do not touch X because…
171
171
  - Consider Y because…
172
172
 
173
+ ## Depends on other tickets
174
+ <§4.1: `- <ticket> — <what it brings that the base branch lacks>`, then "Not dependencies (either order works)", or "none".>
175
+
173
176
  ## Investigation challenges
174
177
  <filled in by §5 with the challenger table>
175
178
  ```
176
179
 
180
+ ### 4.1 Tickets this fix depends on
181
+
182
+ A fix can rest on another ticket the same way a design can — a corrected contract, a migration, a
183
+ decision that lands only when that ticket merges. Detect and record it before the challenger, exactly
184
+ as `/flow:feat:design` §5.1 says: the same rule, the same "Not dependencies (either order works)"
185
+ line, the same `meta.json.ticket_deps` entries, written under `## Depends on other tickets` in
186
+ `03-investigation.md`.
187
+
177
188
  ## 5. Root cause challenge (challenger)
178
189
 
179
190
  Before closing, launch a `Agent general-purpose` challenger with this task:
@@ -29,8 +29,15 @@ Implementation phase. Code is written here.
29
29
  and act as flow-core §2.1 "A study run" says. On `study` → this run writes no code: `size` XS and
30
30
  no `03-design.md` → first write the 2–3 line note below as that file, from `01-context.md` (its
31
31
  `## Study revisions` included) — the person approves a stated change, never `none`; then the
32
- summary, and `<cmd> done --summary-file <work>/done-summary.md`. Nothing else in this command
33
- runs.
32
+ summary, and `<cmd> done --summary-file <work>/done-summary.md`, plus `--depends-on N` per
33
+ `meta.json.ticket_deps` entry, `N` its `ticket` with the leading `#` dropped — a bare `#` starts a
34
+ shell comment — and checked against `[A-Za-z0-9][A-Za-z0-9._-]*` before anything is written, else
35
+ `blocked` (flow-core §2.1). Nothing else in this command runs.
36
+ - **`meta.json.ticket_deps` not empty, and not `unattended`** → the study rests on other tickets
37
+ (`/flow:feat:design` §5.1): name each — `<ticket> — <why>` — in the brief's stop (§2), one line,
38
+ so whoever builds confirms the piece is on **this work's branch**, not only on its base — the
39
+ branch was cut when the study began, and a piece that merged since needs the base brought in
40
+ before writing against it. Nothing is checked; waiting is the builder's call.
34
41
  - `size` M/L: require both `03-design.md` **and** `04-mr-plan.md`. Plan missing → send to `/flow:feat:plan`; design missing → send to `/flow:feat:design`.
35
42
  - `size` XS/S: may start without a design — ask the user for a 2-3 line note on what will be done and save it as a minimal `03-design.md`. No MR/PR plan (always 1 MR/PR).
36
43
  - **If `meta.json.mrs` has more than one entry**: pick the **startable** MR/PR — the `pending` one with the **lowest `n` whose `depends_on` are all `merged`**. Dependencies still `pending`/`in_progress` → not startable, even with a low `n`. (No `wave`/`depends_on` — an older plan — → "first pending by `n`".)
@@ -286,6 +286,9 @@ Consolidate outputs into `.claude/work/<TICKET>/03-design.md`:
286
286
  - **Invariant**: <e.g. "after assignTo(user), ticket.assignee == user AND ticket.status != 'unassigned'">
287
287
  - **Edge / boundary**: <e.g. "empty input list → returns 0, does not throw">
288
288
 
289
+ ## Depends on other tickets
290
+ <§5.1: one line per ticket this design cannot be built without — `- <ticket> — <what it brings that the base branch lacks>` — then, under "Not dependencies (either order works)", each premise on another ticket that holds whichever merges first, with one line why. Neither → "none".>
291
+
289
292
  ## Defensive mechanisms and their justification
290
293
  <One row per validation, guard, retry, lock, fallback, cache, idempotency, queue, or flag the design introduces. If you cannot name a REAL and PRESENT scenario that justifies it, the piece is unnecessary — remove it from the design.>
291
294
 
@@ -314,6 +317,33 @@ Consolidate outputs into `.claude/work/<TICKET>/03-design.md`:
314
317
 
315
318
  **"Acceptance criteria"**: start from the provisional list in `01-context.md` (pinned from the ticket in `/flow:feat:start`), fold in the clarifications and the internal/external contracts decided here, and promote it to the canonical enumerated list `/flow:feat:validate` gates against — observable, verifiable, proportional to size.
316
319
 
320
+ ### 5.1 Tickets this design depends on
321
+
322
+ Fill `## Depends on other tickets` now, before the challenger of §6 reads the design. The study is
323
+ often built by someone who never saw it made — another person, an unattended server — and who
324
+ starts from the base. A design that rests on what another ticket has not delivered yet leaves that
325
+ builder to find the piece missing and either ask or redo it.
326
+
327
+ - **A dependency** is a piece, contract or decision the chosen design needs, that is **not on the
328
+ base this work builds from** (`meta.json.base`, empty → `git.default_base`) — checked there, after
329
+ a best-effort `git fetch origin <base>`, never assumed from a local ref — and that **another
330
+ ticket** of this tracker brings. Missing from the base with no ticket bringing it → it is scope
331
+ or an open question, not a dependency. A ticket in another repo is not a `ticket_deps` entry —
332
+ the orchestrator only resolves this tracker's numbers — so write it in the section as a
333
+ cross-repo dependency, for a person, and leave it out of the list.
334
+ - **Not a dependency**: a premise that holds whichever ticket merges first. Say so under "Not
335
+ dependencies (either order works)", so nobody waits for it.
336
+ - **Record each one** in that section and in `meta.json.ticket_deps` —
337
+ `{ "ticket": "<the reference as a ticket body writes it: #253, MT-123>", "why": "<one line>" }`,
338
+ never one twice. `ticket_deps` is the record the exits read (`/flow:work:handoff` §5, the door to
339
+ code of an unattended study run, flow-core §2.1); the section is the study's prose. Each run of
340
+ this phase writes the list whole from the design it closes; `plan`, which runs after it, appends
341
+ its own again. A §6 response or a §9 edit that changes what the design needs changes
342
+ the section and the list together. None → `[]` and "none".
343
+
344
+ `ticket_deps` is not `mrs[].depends_on`: that one orders this work's own MRs/PRs, this one names
345
+ other tickets. Flow never checks whether a dependency has closed — whoever builds waits for it.
346
+
317
347
  ## 6. Design challenge (challenger)
318
348
 
319
349
  Before closing, **challenge the design** with an `Agent general-purpose` and this self-contained brief:
@@ -122,6 +122,7 @@ Add the `mrs` array with the agreed plan:
122
122
  ```
123
123
 
124
124
  - `n` = execution order (topological): `depends_on` only ever references a **lower** `n`; `wave` groups MRs/PRs that can run in parallel. `/flow:feat:build §1` reads `depends_on` to pick the next **startable** MR/PR and to say which can go in parallel — keep both accurate whenever the plan is edited or renumbered.
125
+ - **Another ticket is not a `depends_on`.** An MR/PR that turns out, while splitting, to need something another ticket brings is a dependency of the study: append it to `meta.json.ticket_deps` as `/flow:feat:design` §5.1 says, name it in that MR/PR's entry of `04-mr-plan.md` beside what it waits on, and leave `03-design.md` as it closed. The design's own entries stand as they are.
125
126
  - **`phases_done`** starts `[]` in every seeded entry: it tracks `build`/`review`/`validate` **for that MR/PR specifically**, so the gates in `/flow:feat:review §1`, `/flow:feat:validate §1` and `/flow:feat:ship §1` judge *this* MR/PR — a sibling's review never satisfies a new MR/PR's gate.
126
127
  - Estimates are **indicative**, not contractual. `/flow:feat:build` uses them as a thermometer: real work over `lines_est` +50% or `files_est + 2` triggers the "cut or continue" question (see §2.3 in build). **A thermometer is not a target** (flow-core §9): an estimate is never met by deleting comments, tests or blank lines, by compressing readable code, or by cutting where the piece is not coherent — the overrun is recorded instead, and a diff shaped to fit a number is the one case where these estimates lie to the phases downstream.
127
128
 
@@ -108,6 +108,9 @@ This system **orchestrates** the project's existing sub-agents and skills (it do
108
108
  "note": "reason if closed/superseded; empty otherwise"
109
109
  }
110
110
  ],
111
+ "ticket_deps": [
112
+ { "ticket": "#253", "why": "one line, what that ticket brings that the base branch lacks" }
113
+ ],
111
114
  "related_repos": [
112
115
  { "repo": "sibling-project", "scope": "what's needed there", "status": "pending" | "in_progress" | "done", "contract_handoff": "none" | "pending" | "published → <location>" }
113
116
  ],
@@ -156,6 +159,7 @@ This system **orchestrates** the project's existing sub-agents and skills (it do
156
159
  Two fields for the review because they are two facts. `reviewed_sha` used to be `git rev-parse HEAD` at the end of the phase, which by then held the round's own fixes — so `ship` compared a sha against itself and reported a match over code no reviewer had read. Read by `ship` at pre-flight against `HEAD`: a delta of test files and this work's own folder passes with a note in `06-review.md`; anything else stops and asks in every autonomy mode. Empty is not a mismatch; `fixed_sha` set with `reviewed_sha == HEAD` is one. `review` also reads `reviewed_sha` back as **its own base** on a re-review, so the second round reads the delta instead of the whole branch.
157
160
  - **`respond_rounds`**: rounds of `/flow:work:respond` on an MR/PR. `/flow:work:respond §1` reads it against `quality.respond_max_rounds` (empty = 3) and refuses the round that would exceed it, handing the open threads back to the user.
158
161
  - **`related_repos`**: the **other repos a task touches**. Captured at `/flow:feat:start` / `/flow:bug:start` §cross-repo, refined at `design`/`plan`, reminded at `ship`, shown by `daily`/`resume`/`status`. flow only notes and reminds — never touches the other repo. `[]` for single-repo.
162
+ - **`ticket_deps`**: the **other tickets this study cannot be built without** — something the design needs that is not on the base branch and that another ticket brings (`/flow:feat:design` §5.1, `/flow:bug:investigate` §4.1). Read at the two exits of a study: `/flow:work:handoff` §5 writes a `Depends on <ticket>` line per entry into the ticket body, and an unattended study run passes `--depends-on N` per entry on its `done` (flow-core §2.1). Not `mrs[].depends_on`, which orders this work's own MRs/PRs. Written whole each time `design` (or `investigate`) runs, and removed when an unattended study is rewound. `[]` or absent when the study depends on nothing.
159
163
  - **`contract_handoff`**: whether the sibling knows **what shape to build against** (`scope` is prose; distinct from `status`). `none` — consumes no contract declared here. `pending` — consumes one, not handed over yet. `published → <location>` — published where that side reads it (normally the tracker ticket; `/flow:feat:ship` §6.3), picked up by the sibling's `/flow:feat:start` §3.6.
160
164
  - **`followups`**: work a phase deliberately did **not** do here — a neighbouring defect found while building, a piece put out of scope while planning, a risk recorded but not mitigated, an edge case left unchecked, a prevention action from a postmortem. Written by the phase that parks it (asks nothing) when it clears the bar of flow-core §7, triaged **once** at `ship`'s Close behind a skeptic and a ceiling on how many may become questions, published in the MR/PR description while still open, and surfaced by `status`/`daily`/`next` — including from `_archive/` — until `declined` or `done`. Shape and rules: flow-core §7. `[]` when the work deferred nothing.
161
165
  - **`defaults_used`**: an empty `FLOW.md` key a phase resolved with its default instead of asking — written in `guided`/`auto`, where asking would breach the never-a-question contract (flow-core §0). Not an error and not deferred work: a record, aggregated by `/flow:doctor` across every work including `_archive/`, so a key that keeps defaulting can be pinned once instead of decided every time. `[]` when nothing defaulted.
@@ -144,13 +144,27 @@ the ticket read it there, never in the work folder; the receiver does not parse
144
144
  <!-- end flow-study -->
145
145
  ```
146
146
 
147
- Criteria keep their literal values. The plan lists each MR/PR with what it covers and what it
148
- waits on; a bug's plan is its single fix.
147
+ Criteria keep their literal values. `## Design in brief` ends with each `meta.json.ticket_deps`
148
+ entry and its why — `Depends on #253: <why>` — so whoever approves the study sees what it waits
149
+ for; an unattended study's `done-summary.md` is this block, so it carries them too. The plan
150
+ lists each MR/PR with what it covers and what it waits on; a bug's plan is its single fix.
149
151
  - **The new body**: the body read above with the block between the two markers replaced — or, with
150
- no markers yet, the block appended after a blank line. Written to a `mktemp` file outside the
152
+ no markers yet, the block appended after a blank line. Then, for each `meta.json.ticket_deps`
153
+ entry (`/flow:feat:design` §5.1) whose line is not already a line of that body — same text,
154
+ ignoring case and surrounding whitespace — one line after the end marker, in `ticket_deps` order:
155
+
156
+ ```
157
+ Depends on #253
158
+ ```
159
+
160
+ `#253` being the entry's `ticket` as it stands. Outside the markers so a later rewrite of the
161
+ block leaves it alone, and never removed: a line a person wrote by hand looks exactly like one
162
+ flow wrote, and the convention is theirs too. Written to a `mktemp` file outside the
151
163
  tree — `{BODY_FILE}` — so nothing of it can be committed later.
152
- - **Show the block and ask** with `AskUserQuestion`, **in every mode**: it writes to a shared
153
- tracker. **Write it** · **Skip**. Failure → its message in one line; the push stands.
164
+ - **Show the block, and the `Depends on` lines it adds, and ask** with `AskUserQuestion`, **in
165
+ every mode**: it writes to a shared tracker. A `Depends on` line already in the body, outside the
166
+ markers, that no entry names — a dependency a revised study dropped, or one a person added — is named in the same
167
+ question and left in place: whether it still holds is theirs to say. **Write it** · **Skip**. Failure → its message in one line; the push stands.
154
168
 
155
169
  ## 6. Close
156
170
 
@@ -63,7 +63,7 @@ of truth, and an argument that disagrees with it is ignored and named in the log
63
63
  | `conflict` | exists | `/flow:work:green` — the MR/PR conflicts with its base; which base, and where it stands, come from `<cmd> events --json` |
64
64
  | `review` · `pipeline` · `conflict` | none | `blocked` — there is no work of this ticket to answer for |
65
65
 
66
- **The work's branch, on `start` and `study`.** The current branch is `meta.json.branch` → nothing to do. It is
66
+ **The work's branch, on `start` and `study`.** The current branch is `meta.json.branch` → no switch. It is
67
67
  not — a study made on another machine arrives on the ticket's `base`, a run that died before its
68
68
  closing order is relaunched there — then, in this order:
69
69
 
@@ -86,6 +86,21 @@ closing order is relaunched there — then, in this order:
86
86
  `meta.json.branch` = it, `meta.json.base` = that base, `worktree` and `stacked_on` = `null` — paths and parents from the
87
87
  other machine mean nothing here — and record `{ "key": "unattended:rehomed", "default": "<old branch> → <name> from <base>", "phase": "run" }`.
88
88
 
89
+ **On `start`, a study's branch catches up with its base** — once on the work's branch, the "no
90
+ switch" case included, and only while no build or fix has begun (neither `05-implementation.md` nor
91
+ `04-fix.md` exists). Every command below runs in the work's own checkout — step 2's worktree when
92
+ that is where it lives. A study writes no code, so its branch still sits where the base was when the
93
+ study began; an approval that waited for the tickets the study depends on (`meta.json.ticket_deps`)
94
+ would otherwise build on a base that lacks them. `<base>` = `meta.json.base`, empty →
95
+ `git.default_base`: `git fetch origin <base>`, then `<ref>` = `origin/<base>`; the fetch failing
96
+ with a remote configured → `blocked`, since a stale ref would leave the build on the old base
97
+ unnoticed. No remote → the local `<base>`. `git merge-base --is-ancestor HEAD <ref>` and `HEAD` is not
98
+ `<ref>` → the branch holds no commit of its own and the base moved: `git merge --ff-only <ref>`, and
99
+ record `{ "key": "unattended:base-caught-up", "default": "<old short sha> → <new short sha> on
100
+ <base>", "phase": "run" }`. The fast-forward fails → `blocked`, its message as the reason. Not an
101
+ ancestor → it holds commits a previous run made: leave it as it is — bringing the base into work in
102
+ progress is the orchestrator's (flow-core §2.1).
103
+
89
104
  **A person's changes to a study, on `study`** (flow-core §2.1: they rewind it). The work's branch
90
105
  first, as above. `<work>/study-revision.md` holds the reply, as the row above wrote it. Each step
91
106
  can be run again: a run that dies part-way is finished by the next relaunch, whatever it serves.
@@ -109,7 +124,8 @@ can be run again: a run that dies part-way is finished by the next relaunch, wha
109
124
  `study-revision.md` and earlier `before-*/` folders into `<work>/before-<id7>/` — never
110
125
  overwriting a name already there — so no artifact, summary or agent answer of the discarded study
111
126
  leaks into the next. Then `meta.json`: `phase` = `context`, `phases_done` = `["context"]`, `mrs`
112
- removed (the row that blocks a work past the study guarantees nothing was built), `notes`
127
+ removed (the row that blocks a work past the study guarantees nothing was built), `ticket_deps`
128
+ removed (the redone study finds its dependencies again), `notes`
113
129
  emptied, and every `followups[]` entry a study phase wrote (`source` `design`, `plan` or
114
130
  `investigate`) removed.
115
131
  5. **Re-size**: estimate the size again with `/flow:feat:start` §4's table (`/flow:bug:start`'s for