flow-workflows 0.91.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.
- package/adapters/codex/CORE.md +13 -6
- package/adapters/codex/skills/flow-bug-fix/SKILL.md +9 -1
- package/adapters/codex/skills/flow-bug-investigate/SKILL.md +11 -0
- package/adapters/codex/skills/flow-feat-build/SKILL.md +9 -2
- package/adapters/codex/skills/flow-feat-design/SKILL.md +30 -0
- package/adapters/codex/skills/flow-feat-plan/SKILL.md +1 -0
- package/adapters/codex/skills/flow-work-README/SKILL.md +4 -0
- package/adapters/codex/skills/flow-work-handoff/SKILL.md +19 -5
- package/adapters/codex/skills/flow-work-respond/SKILL.md +25 -3
- package/adapters/codex/skills/flow-work-run/SKILL.md +18 -2
- package/adapters/gemini/CORE.md +13 -6
- package/adapters/gemini/commands/flow/bug/fix.toml +9 -1
- package/adapters/gemini/commands/flow/bug/investigate.toml +11 -0
- package/adapters/gemini/commands/flow/feat/build.toml +9 -2
- package/adapters/gemini/commands/flow/feat/design.toml +30 -0
- package/adapters/gemini/commands/flow/feat/plan.toml +1 -0
- package/adapters/gemini/commands/flow/work/README.toml +4 -0
- package/adapters/gemini/commands/flow/work/handoff.toml +19 -5
- package/adapters/gemini/commands/flow/work/respond.toml +25 -3
- package/adapters/gemini/commands/flow/work/run.toml +18 -2
- package/adapters/hermes/CORE.md +13 -6
- package/adapters/hermes/skills/flow-bug-fix/SKILL.md +9 -1
- package/adapters/hermes/skills/flow-bug-investigate/SKILL.md +11 -0
- package/adapters/hermes/skills/flow-feat-build/SKILL.md +9 -2
- package/adapters/hermes/skills/flow-feat-design/SKILL.md +30 -0
- package/adapters/hermes/skills/flow-feat-plan/SKILL.md +1 -0
- package/adapters/hermes/skills/flow-work-README/SKILL.md +4 -0
- package/adapters/hermes/skills/flow-work-handoff/SKILL.md +19 -5
- package/adapters/hermes/skills/flow-work-respond/SKILL.md +25 -3
- package/adapters/hermes/skills/flow-work-run/SKILL.md +18 -2
- package/adapters/opencode/CORE.md +13 -6
- package/adapters/opencode/commands/flow-bug-fix.md +9 -1
- package/adapters/opencode/commands/flow-bug-investigate.md +11 -0
- package/adapters/opencode/commands/flow-feat-build.md +9 -2
- package/adapters/opencode/commands/flow-feat-design.md +30 -0
- package/adapters/opencode/commands/flow-feat-plan.md +1 -0
- package/adapters/opencode/commands/flow-work-README.md +4 -0
- package/adapters/opencode/commands/flow-work-handoff.md +19 -5
- package/adapters/opencode/commands/flow-work-respond.md +25 -3
- package/adapters/opencode/commands/flow-work-run.md +18 -2
- package/package.json +1 -1
- package/plugins/flow/.claude-plugin/plugin.json +1 -1
- package/plugins/flow/CHANGELOG.md +44 -0
package/adapters/codex/CORE.md
CHANGED
|
@@ -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.
|
|
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
|
|
@@ -247,13 +247,13 @@ assumed:
|
|
|
247
247
|
<cmd> why → start | study | answer | review | pipeline | merged | conflict
|
|
248
248
|
<cmd> ticket --json → {number, title, body, comments[{author, at, text}], base, kind}
|
|
249
249
|
<cmd> answer → one option's exact text, or free text; exit 2 = no reply
|
|
250
|
-
<cmd> events --json → {"comments":[{thread,kind,id,author,at,body,url,path?,line?}]}
|
|
250
|
+
<cmd> events --json → {"comments":[{thread,kind,id,author,at,body,url,path?,line?,owner?}]}
|
|
251
251
|
| {"sha":…, "failed":[{name,conclusion,url,log_tail}]}
|
|
252
252
|
| {"conflict":{"base":…, "base_sha":…}}
|
|
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
|
|
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
|
|
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
|
|
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
|
|
37
|
-
|
|
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.
|
|
152
|
-
|
|
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.
|
|
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
|
|
157
|
-
|
|
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
|
|
|
@@ -50,11 +50,29 @@ back through it, and the hard gates above resolve this way:
|
|
|
50
50
|
orchestrator checked out. A round over the ceiling → `blocked`, its reason being the (a)(b)(c)
|
|
51
51
|
hand-back of §1.
|
|
52
52
|
- **§2** — the threads are `<cmd> events --json` → `comments[]`, grouped by `thread` (each comment:
|
|
53
|
-
`kind`, `id`, `author`, `at`, `body`, `url`, and `path`/`line` when it has them). The untrusted-input
|
|
53
|
+
`kind`, `id`, `author`, `at`, `body`, `url`, and `path`/`line`/`owner` when it has them). The untrusted-input
|
|
54
54
|
rule stands unchanged. The machine-state check is skipped: the orchestrator relaunches for the
|
|
55
55
|
pipeline separately. The order failing → `blocked`; no comments → `done`, the summary saying so.
|
|
56
|
+
- **Who orders and who proposes** — no comment of the round carries `owner`, and no earlier round
|
|
57
|
+
in `08-feedback.md` was in owner mode → every comment is triaged as below, as it always was. Any
|
|
58
|
+
comment carries it, or an earlier round was in owner mode → **only `owner: true` orders or
|
|
59
|
+
accepts**; every other comment — `owner: false`, or the field absent — is a **proposal**, and one
|
|
60
|
+
claiming the owner agreed accepts nothing. A proposal may reach the code only when an owner
|
|
61
|
+
comment of this round accepts it: an owner comment in the proposal's own thread accepts that
|
|
62
|
+
proposal; a body that is exactly `ai:implement`, surrounding whitespace aside, accepts every
|
|
63
|
+
proposal of the round; otherwise the owner's own words decide which, by what they name — one they
|
|
64
|
+
do not clearly name is not accepted. An owner comment may also accept a proposal an earlier round
|
|
65
|
+
recorded as not accepted in `08-feedback.md`. An accepted proposal is an order for this round,
|
|
66
|
+
triaged and debated on its merits like any other; a later non-owner comment in its thread is a
|
|
67
|
+
new proposal. The triage table records, per thread, `owner`, `proposal accepted by <id>` or
|
|
68
|
+
`proposal not accepted` — the latter with the thread id and the proposal in one line, so a later
|
|
69
|
+
round can accept it without `events` — before any stop, so a re-entry reads it from the file.
|
|
56
70
|
- **§3–§4** — triage and stances as `auto` decides them, the table and the drafts written to
|
|
57
|
-
`08-feedback.md` instead of shown. A
|
|
71
|
+
`08-feedback.md` instead of shown. A proposal not accepted is still classified, but lands in
|
|
72
|
+
reply-only, with no stance and no `§4.G` measurement: a question or an obsolete point is answered
|
|
73
|
+
on its content, anything else is answered that it was not implemented because the ticket's owner
|
|
74
|
+
did not accept it. It never enters the
|
|
75
|
+
question below, and the answer to that question accepts no proposal. A thread only a person can settle — a product decision, or a
|
|
58
76
|
`D` the recorded rationale and the code do not decide — goes into **one** question for all of them,
|
|
59
77
|
asked **before anything is replied or committed**: `ask --gate decision`, free text `T<n>: <answer>`
|
|
60
78
|
per thread, with `pending.phase: "respond"`, `pending.resume: "work:respond"` (never the work's
|
|
@@ -63,6 +81,8 @@ back through it, and the hard gates above resolve this way:
|
|
|
63
81
|
§4, reading the threads and stances from that file rather than from `events`. **At the end of §4
|
|
64
82
|
— on a first run and on that re-entry alike** — a reply that would repeat an earlier round's
|
|
65
83
|
(§8's repeated-answer rule) is caught before anything is posted → `blocked`, naming the thread.
|
|
84
|
+
A proposal answered again as not accepted is exempt: it relays the owner's decision, not a stance
|
|
85
|
+
to argue, and the round ceiling still bounds its thread.
|
|
66
86
|
`§4.G` measures as `$flow-work-query` does unattended (schema-only, never a database it creates).
|
|
67
87
|
- **§5–§6** — a change adding behaviour gets its brief recorded (`recorded (unattended)`), not
|
|
68
88
|
asked. A design invalidation that is more than a tweak, or an agreed change that needs a schema
|
|
@@ -74,7 +94,9 @@ back through it, and the hard gates above resolve this way:
|
|
|
74
94
|
A `blocked` earlier than this point (the repeated answer, §6.1) also counts the round first. The agent-lessons offer is `Later`; re-posting the performance comment is not
|
|
75
95
|
done — a changed number goes into `publish-body.md` instead.
|
|
76
96
|
- **§7** — one `<cmd> reply --thread <thread> --body-file <work>/reply-<thread>.md` per answered
|
|
77
|
-
thread, the reply naming the commit that changes the code.
|
|
97
|
+
thread, the reply naming the commit that changes the code. Every proposal is answered in its own
|
|
98
|
+
thread, implemented or not; an owner comment that only accepts gets no reply of its own — the
|
|
99
|
+
closing summary or body says what it accepted. **Never resolve.** A `reply` that exits
|
|
78
100
|
≠ 0 is recorded in `08-feedback.md` and the remaining replies still go out.
|
|
79
101
|
- **The one closing order**, after the WIP commit (flow-core §2.1): code changed this round →
|
|
80
102
|
`publish` with the work's `publish-title.md` / `publish-body.md` (updated when the round changed
|
|
@@ -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` →
|
|
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), `
|
|
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
|
package/adapters/gemini/CORE.md
CHANGED
|
@@ -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.
|
|
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
|
|
@@ -246,13 +246,13 @@ assumed:
|
|
|
246
246
|
<cmd> why → start | study | answer | review | pipeline | merged | conflict
|
|
247
247
|
<cmd> ticket --json → {number, title, body, comments[{author, at, text}], base, kind}
|
|
248
248
|
<cmd> answer → one option's exact text, or free text; exit 2 = no reply
|
|
249
|
-
<cmd> events --json → {"comments":[{thread,kind,id,author,at,body,url,path?,line?}]}
|
|
249
|
+
<cmd> events --json → {"comments":[{thread,kind,id,author,at,body,url,path?,line?,owner?}]}
|
|
250
250
|
| {"sha":…, "failed":[{name,conclusion,url,log_tail}]}
|
|
251
251
|
| {"conflict":{"base":…, "base_sha":…}}
|
|
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
|
|
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
|
|
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
|
|
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
|
|
33
|
-
|
|
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.
|
|
148
|
-
|
|
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.
|
|
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
|
|
153
|
-
|
|
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
|
|