flow-workflows 0.86.0 → 0.88.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/README.md +1 -0
- package/adapters/codex/CORE.md +11 -1
- package/adapters/codex/skills/flow-bug-investigate/SKILL.md +3 -3
- package/adapters/codex/skills/flow-bug-start/SKILL.md +1 -1
- package/adapters/codex/skills/flow-feat-design/SKILL.md +3 -3
- package/adapters/codex/skills/flow-feat-plan/SKILL.md +3 -3
- package/adapters/codex/skills/flow-feat-start/SKILL.md +1 -1
- package/adapters/codex/skills/flow-work-README/SKILL.md +1 -0
- package/adapters/codex/skills/flow-work-handoff/SKILL.md +162 -0
- package/adapters/codex/skills/flow-work-resume/SKILL.md +1 -1
- package/adapters/gemini/CORE.md +11 -1
- package/adapters/gemini/commands/flow/bug/investigate.toml +3 -3
- package/adapters/gemini/commands/flow/bug/start.toml +1 -1
- package/adapters/gemini/commands/flow/feat/design.toml +3 -3
- package/adapters/gemini/commands/flow/feat/plan.toml +3 -3
- package/adapters/gemini/commands/flow/feat/start.toml +1 -1
- package/adapters/gemini/commands/flow/work/README.toml +1 -0
- package/adapters/gemini/commands/flow/work/handoff.toml +159 -0
- package/adapters/gemini/commands/flow/work/resume.toml +1 -1
- package/adapters/hermes/CORE.md +11 -1
- package/adapters/hermes/skills/flow-bug-investigate/SKILL.md +3 -3
- package/adapters/hermes/skills/flow-bug-start/SKILL.md +1 -1
- package/adapters/hermes/skills/flow-feat-design/SKILL.md +3 -3
- package/adapters/hermes/skills/flow-feat-plan/SKILL.md +3 -3
- package/adapters/hermes/skills/flow-feat-start/SKILL.md +1 -1
- package/adapters/hermes/skills/flow-work-README/SKILL.md +1 -0
- package/adapters/hermes/skills/flow-work-handoff/SKILL.md +162 -0
- package/adapters/hermes/skills/flow-work-resume/SKILL.md +1 -1
- package/adapters/opencode/CORE.md +11 -1
- package/adapters/opencode/commands/flow-bug-investigate.md +3 -3
- package/adapters/opencode/commands/flow-bug-start.md +1 -1
- package/adapters/opencode/commands/flow-feat-design.md +3 -3
- package/adapters/opencode/commands/flow-feat-plan.md +3 -3
- package/adapters/opencode/commands/flow-feat-start.md +1 -1
- package/adapters/opencode/commands/flow-work-README.md +1 -0
- package/adapters/opencode/commands/flow-work-handoff.md +160 -0
- package/adapters/opencode/commands/flow-work-resume.md +1 -1
- package/package.json +1 -1
- package/plugins/flow/.claude-plugin/plugin.json +1 -1
- package/plugins/flow/CHANGELOG.md +29 -0
- package/plugins/flow/examples/FLOW.template.md +3 -0
package/adapters/hermes/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.88.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
|
|
@@ -218,6 +218,16 @@ sensible default and record it.
|
|
|
218
218
|
|
|
219
219
|
Asking these anyway is how a run in `guided`/`auto` degrades into a manual one.
|
|
220
220
|
|
|
221
|
+
**A study made for handoff.** `git.handoff_branch` set and the mode not `unattended` → the study is
|
|
222
|
+
built somewhere else, so wherever a study phase (`start`, `design`, `plan`, `investigate`) or
|
|
223
|
+
`resume` would hand the work to `/flow-feat-build` or `/flow-bug-fix`, it hands it to
|
|
224
|
+
`/flow-work-handoff` instead. Only the target changes: a Close that asks (`manual`) offers **Hand
|
|
225
|
+
off the study** recommended and **Build here** second; a Close that chains (`guided`/`auto`) chains
|
|
226
|
+
into handoff, whose push question is the one confirmation — no stop is added. In `unattended` the
|
|
227
|
+
rule does not apply: the repo's `FLOW.md` reaches the server too, and a run that studies a ticket
|
|
228
|
+
there goes on to build it. A repo that hands its studies off pairs the key with
|
|
229
|
+
`autonomy.mode: auto`, and the study then runs on its own up to the push.
|
|
230
|
+
|
|
221
231
|
### 2.1 `unattended` — where a person would have been asked
|
|
222
232
|
|
|
223
233
|
No person is there to answer a question in this mode. A question tool — `AskUserQuestion`, OpenCode's `question`,
|
|
@@ -36,7 +36,7 @@ Read `~/.claude/flow/CORE.hermes.md` first (shared rules: `FLOW.md` step 0, mode
|
|
|
36
36
|
**In `unattended`, before this phase's first step, read flow-core §2.1 whole — from its heading to the next section, unless it is already in your context whole; having read parts of it does not count** — the orchestrator command, the question stop and the way its answer comes back are all in it, and a phase that reads only part of it skips them.
|
|
37
37
|
|
|
38
38
|
- Read `meta.json` and `00-summary.md`; open in full only `01-context.md`. (flow-core §5)
|
|
39
|
-
- `type` is not `bug` → refuse. `size` is `XS` → suggest `/flow-bug-fix` and stop.
|
|
39
|
+
- `type` is not `bug` → refuse. `size` is `XS` → suggest `/flow-bug-fix` — `git.handoff_branch` set and not `unattended` → `/flow-work-handoff` in its place, **Hand off the study** recommended and **Build here** second where it asks (flow-core §2, a study made for handoff) — and stop.
|
|
40
40
|
- Require `context` in `phases_done`.
|
|
41
41
|
|
|
42
42
|
## 1.5 Reproduce — what is failing, before why
|
|
@@ -221,5 +221,5 @@ If `knowledge.stage` is set and the root cause reveals a **non-obvious "why"** a
|
|
|
221
221
|
|
|
222
222
|
- Update `meta.json`: `phase = "investigate"`, add to `phases_done`.
|
|
223
223
|
- Overwrite `00-summary.md` whole (≤15 lines, flow-core §5).
|
|
224
|
-
- Suggest `/flow-bug-fix
|
|
225
|
-
- **Autonomy handoff** (`autonomy.mode`, flow-core §2): `manual` → propose `/flow-bug-fix` with a single `AskUserQuestion`, invoke it only on confirmation; `guided`/`auto` → chain into it in this same turn.
|
|
224
|
+
- Suggest `/flow-bug-fix` — `git.handoff_branch` set and not `unattended` → `/flow-work-handoff` in its place, **Hand off the study** recommended and **Build here** second where it asks (flow-core §2, a study made for handoff).
|
|
225
|
+
- **Autonomy handoff** (`autonomy.mode`, flow-core §2): `manual` → propose `/flow-bug-fix` with a single `AskUserQuestion`, invoke it only on confirmation; `guided`/`auto` → chain into it in this same turn. Either way: `git.handoff_branch` set and not `unattended` → `/flow-work-handoff` in its place, **Hand off the study** recommended and **Build here** second where it asks (flow-core §2, a study made for handoff).
|
|
@@ -228,6 +228,6 @@ Run `tracker.start_cmd` with `{TICKET}` = `meta.json.ticket` and `{ASSIGNEE}` =
|
|
|
228
228
|
|
|
229
229
|
## 5. Close
|
|
230
230
|
|
|
231
|
-
- Suggest the next command by size: `/flow-bug-fix` for XS, `/flow-bug-investigate` for the rest.
|
|
231
|
+
- Suggest the next command by size: `/flow-bug-fix` for XS (`git.handoff_branch` set and not `unattended` → `/flow-work-handoff` in its place, **Hand off the study** recommended and **Build here** second where it asks (flow-core §2, a study made for handoff)), `/flow-bug-investigate` for the rest.
|
|
232
232
|
- Overwrite `00-summary.md` whole (≤15 lines, flow-core §5).
|
|
233
233
|
- Apply `autonomy.mode`: `manual` stops and recommends; `guided`/`auto` chain into that command automatically, subject to the hard gates.
|
|
@@ -40,7 +40,7 @@ after writing each challenge's Response.
|
|
|
40
40
|
|
|
41
41
|
- Load `meta.json` by current branch — in `unattended`, the work `/flow-work-run` found (flow-core §2.1). Missing → ask the user to start with `/flow-feat-start`.
|
|
42
42
|
- Read `meta.json` and `00-summary.md`; open in full only `01-context.md` (ticket, decisions, contracts received). (flow-core §5)
|
|
43
|
-
- `size` is `XS` → suggest jumping to `/flow-feat-build` and stop unless the user insists.
|
|
43
|
+
- `size` is `XS` → suggest jumping to `/flow-feat-build` — `git.handoff_branch` set and not `unattended` → `/flow-work-handoff` in its place, **Hand off the study** recommended and **Build here** second where it asks (flow-core §2, a study made for handoff) — and stop unless the user insists.
|
|
44
44
|
|
|
45
45
|
## 1.5 Approaches — open the option space first (M/L)
|
|
46
46
|
|
|
@@ -408,6 +408,6 @@ Never invoke `knowledge.save` here — the final save is in `/flow-feat-ship` af
|
|
|
408
408
|
- **Confirm the acceptance criteria** within the design review: present the enumerated list. If every criterion is unambiguous and verifiable, the review covers them — no separate prompt. `AskUserQuestion` **only** when a criterion is ambiguous, not verifiable, or one is likely missing for today's ticket (same restraint as the challenger). Apply the user's edits to `03-design.md` before advancing.
|
|
409
409
|
- **In `manual`/`guided`**, ask the user to review the design; edit the artifact on requested changes before advancing. **In `auto`, do not ask** — record it as accepted per the handoff below. Not an unconditional stop.
|
|
410
410
|
- Next step by size:
|
|
411
|
-
- **XS / S**: suggest `/flow-feat-build` (1 single MR/PR, no planned split).
|
|
411
|
+
- **XS / S**: suggest `/flow-feat-build` (1 single MR/PR, no planned split) — `git.handoff_branch` set and not `unattended` → `/flow-work-handoff` in its place, **Hand off the study** recommended and **Build here** second where it asks (flow-core §2, a study made for handoff).
|
|
412
412
|
- **M / L**: suggest `/flow-feat-plan` to split the work into independently mergeable MRs/PRs before implementing.
|
|
413
|
-
- **Autonomy handoff.** Design review is a genuine decision point: `manual` and `guided` ask for it before advancing. `auto` records the design as accepted in the artifact and **chains into the command for the size** in this same turn. `manual` proposes that command with a single `AskUserQuestion` (recommended option by default), never a written suggestion. Unresolved `high`-severity findings (§6) stop the flow in **every** mode — do not chain over them.
|
|
413
|
+
- **Autonomy handoff.** Design review is a genuine decision point: `manual` and `guided` ask for it before advancing. `auto` records the design as accepted in the artifact and **chains into the command for the size** in this same turn — `build` there: `git.handoff_branch` set and not `unattended` → `/flow-work-handoff` in its place, **Hand off the study** recommended and **Build here** second where it asks (flow-core §2, a study made for handoff). `manual` proposes that command with a single `AskUserQuestion` (recommended option by default), never a written suggestion. Unresolved `high`-severity findings (§6) stop the flow in **every** mode — do not chain over them.
|
|
@@ -31,7 +31,7 @@ Delivery planning phase. **No code is written.** Splits the feature into MRs/PRs
|
|
|
31
31
|
- Load `meta.json` by current branch — in `unattended`, the work `/flow-work-run` found (flow-core §2.1). Missing → send the user to `/flow-feat-start`.
|
|
32
32
|
- Require `design` in `phases_done`. Missing → send to `/flow-feat-design` and stop.
|
|
33
33
|
- Read `meta.json` and `00-summary.md`; open in full only `03-design.md`. (flow-core §5)
|
|
34
|
-
- **`size` `XS` or `S`** → warn that this phase does not apply (always 1 MR/PR), add `"plan:skipped"` to `phases_done`, suggest `/flow-feat-build
|
|
34
|
+
- **`size` `XS` or `S`** → warn that this phase does not apply (always 1 MR/PR), add `"plan:skipped"` to `phases_done`, suggest `/flow-feat-build` — `git.handoff_branch` set and not `unattended` → `/flow-work-handoff` in its place, **Hand off the study** recommended and **Build here** second where it asks (flow-core §2, a study made for handoff). Stop.
|
|
35
35
|
|
|
36
36
|
## 2. Work
|
|
37
37
|
|
|
@@ -165,5 +165,5 @@ A slice landing in **another repo** is not one of *this* repo's `mrs`: record it
|
|
|
165
165
|
|
|
166
166
|
`∥` = no dependency between them, can be built in parallel or as a train; `→` = the next wave waits for the previous one to merge. Then the table with `#`, wave, `depends_on`, title and estimate. One line for the split rationale, and — when any pair was merged — one line saying how many were and what they would have cost (`merged #a into #b: would have been a wave of its own`). Nothing else — risks and discarded alternatives live in the artifact.
|
|
167
167
|
- Changes requested → edit the artifact and `meta.json.mrs` before advancing.
|
|
168
|
-
- Suggest `/flow-feat-build` to start the first MR/PR.
|
|
169
|
-
- **Autonomy handoff.** Approving the split is a genuine decision point: in `manual` and `guided` ask before advancing. In `auto`, record the plan as accepted in `04-mr-plan.md` and **chain into `/flow-feat-build` automatically** in this same turn. In `manual`, propose it with a single `AskUserQuestion`.
|
|
168
|
+
- Suggest `/flow-feat-build` to start the first MR/PR — `git.handoff_branch` set and not `unattended` → `/flow-work-handoff` in its place, **Hand off the study** recommended and **Build here** second where it asks (flow-core §2, a study made for handoff).
|
|
169
|
+
- **Autonomy handoff.** Approving the split is a genuine decision point: in `manual` and `guided` ask before advancing. In `auto`, record the plan as accepted in `04-mr-plan.md` and **chain into `/flow-feat-build` automatically** in this same turn. In `manual`, propose it with a single `AskUserQuestion`. Either way: `git.handoff_branch` set and not `unattended` → `/flow-work-handoff` in its place, **Hand off the study** recommended and **Build here** second where it asks (flow-core §2, a study made for handoff).
|
|
@@ -309,5 +309,5 @@ Run `tracker.start_cmd` with `{TICKET}` = `meta.json.ticket`, `{ASSIGNEE}` = `tr
|
|
|
309
309
|
## 7. Close
|
|
310
310
|
|
|
311
311
|
- Overwrite `00-summary.md` whole (≤15 lines, flow-core §5) — first written in §6; refresh it if §6.5 changed anything.
|
|
312
|
-
- Report **following the stop header** (flow-core §3), branch added, body 2-3 lines: ticket, size, branch; recommended next command per the §4 table — in `guided`/`auto` you run it in this same turn, so there is no report and no question here: update the panel and chain (flow-core §3).
|
|
312
|
+
- Report **following the stop header** (flow-core §3), branch added, body 2-3 lines: ticket, size, branch; recommended next command per the §4 table (`build` on XS: `git.handoff_branch` set and not `unattended` → `/flow-work-handoff` in its place, **Hand off the study** recommended and **Build here** second where it asks (flow-core §2, a study made for handoff)) — in `guided`/`auto` you run it in this same turn, so there is no report and no question here: update the panel and chain (flow-core §3).
|
|
313
313
|
- Apply `autonomy.mode`: `manual` → stop and let the user invoke the recommended command; `guided`/`auto` → chain into it, subject to the hard gates (the §5 branch-base ambiguity already stops on its own).
|
|
@@ -249,6 +249,7 @@ Two signals arrive on an open MR/PR, each with its own cross-cutting loop (feat
|
|
|
249
249
|
- `/flow-work-daily [question]` — the **work assistant** (daily standup), read-only. **Local** (`.claude/work/` + git) + **forge** (open MRs/PRs, reviews awaiting you, red CI, unmergeable, unresolved threads via `git.cli`) + **tracker** (assigned tickets, priority changes via `tracker.tool`); where they cross, concrete commands (`/flow-feat-start`, `/flow-work-green`, `/flow-work-respond`). No argument → *yesterday · today · blockers*; a question → just that. External sources best-effort, never blocking; the only write is a "last seen" marker, like `/flow-news`.
|
|
250
250
|
- `/flow-work-status` — all works in `.claude/work/`, current phase, divergence with git.
|
|
251
251
|
- `/flow-work-run [ticket]` — the **entry an orchestrator launches** for an `unattended` run: asks it `why` (start · answer · merged · review · pipeline), routes to the phase that answers it, and makes no decision of its own. Refuses to run in any other mode.
|
|
252
|
+
- `/flow-work-handoff` — hands a **finished study** to whoever builds it elsewhere (an unattended server, another machine): refuses an unfinished one, pushes only this work's folder as a parentless commit to `git.handoff_branch` without touching the checkout, asks before the push, then offers a marked summary for the ticket body (`tracker.edit_cmd`).
|
|
252
253
|
- `/flow-work-resume` — detects the branch, reads `meta.json`, recaps, suggests the next step. **Re-reads the ticket's comment thread** (§2.5), reports only what is new since `01-context.md`, appends it to the artifact; never amends the design on its own.
|
|
253
254
|
- `/flow-work-try <branch>` / `/flow-work-try --back` — point the main checkout at a branch to test it here, then return; re-syncs per `git.worktree_resync`. Resolves the work behind the branch and, once the environment is up, **prints the manual test plan already written** (the `needs-manual`/`unproven` criteria, or the acceptance criteria when `validate` has not run) and offers to collect Pass/Fail/Blocked into the validation artifact. No work behind the branch → the plain switch, as before.
|
|
254
255
|
- `/flow-work-clean [--dry-run]` — **periodic sweep** of finished work's residue: worktree, local branch, `.claude/work/` folder (intermediate train MR/PRs never set `phase: done`, so the `ship`/`abandon` prompt never fires). Verdict per branch from **the forge** with local fallbacks, including patch-equivalence for **squash merges** (`git branch --merged` misses them); acts only on `merged`/`empty`. `unknown` never a candidate; dirty and unpushed protected; work folders **archived, never deleted**; remote never touched. **`autonomy.mode` does not authorize deletion**: `auto` confirms the list too. `--purge-archive <N>d`: separate opt-in pass over `_archive/`, only folders already committed to git.
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: flow-work-handoff
|
|
3
|
+
description: "Hand a finished study to whoever builds it elsewhere — push only this work's folder to a branch of its own, then offer a summary for the ticket"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
<!-- GENERATED by script/adapter-build.py from plugins/flow/commands/work/handoff.md — do not edit; change the plugin file and run the script. -->
|
|
7
|
+
|
|
8
|
+
# `/flow-work-handoff`
|
|
9
|
+
|
|
10
|
+
> **hermes adapter — how the Claude Code primitives named below map here.**
|
|
11
|
+
> - `AskUserQuestion` → ask in plain text with numbered options and wait for the reply.
|
|
12
|
+
> - `Agent <role>` / `Agent general-purpose` / subagents → `delegate_task`, which creates the child on the spot from the `goal` and `context` you give it: the name in `agents.<role>` in `FLOW.md` is a role to state in that prompt, not an agent to declare anywhere; empty → do it in this context.
|
|
13
|
+
> - Parallel fan-out → several `delegate_task` calls in one response, capped at `agents.fanout_max` (empty → 4) and, above that, by `delegation.max_concurrent_children` in `config.yaml` (default 10); `agents.fanout_tool` is Claude Code-only, ignore it.
|
|
14
|
+
> - `ScheduleWakeup` / `Monitor` / `/loop` → Hermes schedules itself: the `cronjob` tool, or `/cron add "every 30m" "<this command>"`. Each firing is a fresh session with no history, so the state still lives in `monitor.md` and the scheduled text must stand alone; retiring the job from inside a firing needs `cron.allow_agent_scheduling: true`.
|
|
15
|
+
> - `TaskCreate` → a markdown checklist in the phase artifact.
|
|
16
|
+
> - `Skill commit-commands:commit-push-pr` → `git add` · `git commit` · `git push -u origin HEAD` · the `git.cli` CLI (`gh pr create` / `glab mr create`).
|
|
17
|
+
> - `/model <value>` → Hermes's own `/model`; a subagent's model is `delegation.model` in `config.yaml`, one value for every child of a round.
|
|
18
|
+
> - `knowledge.*` roles → whatever tools `FLOW.md` names there; an MCP tool keeps its name, its server is declared under `mcp_servers` in `~/.hermes/config.yaml` (see `config.snippet.yaml`).
|
|
19
|
+
> - `$ARGUMENTS` → what the user typed after the skill name: Hermes takes everything from the first non-skill token on as the instruction.
|
|
20
|
+
|
|
21
|
+
Read `~/.claude/flow/CORE.hermes.md` first (shared rules: `FLOW.md` step 0, autonomy, how a stop reads, the live panel, `00-summary.md`) — skip if you already read it in this session.
|
|
22
|
+
|
|
23
|
+
**Panel words are closed** — `mark`: `done` · `current` · `pending` · `wait` · `block` · `info`; `style`: `normal` · `dim` · `title` · `accent` · `ok` · `warn` · `error`. Anything else is dropped by the reader in silence: the panel still paints, and nobody is told.
|
|
24
|
+
|
|
25
|
+
For a study made here and built somewhere else — a server running `/flow-work-run` unattended,
|
|
26
|
+
another person, another machine. The builder gets the work folder at the same path and nothing
|
|
27
|
+
else: no conversation, maybe a less capable model. So this command checks the study can stand on
|
|
28
|
+
its own, pushes the folder to a branch that holds only it, and offers to put a summary in the
|
|
29
|
+
ticket for the people who review it.
|
|
30
|
+
|
|
31
|
+
## 1. Pre-flight
|
|
32
|
+
|
|
33
|
+
- Effective FLOW configuration per flow-core §0. `autonomy.mode: unattended` → refuse in one line:
|
|
34
|
+
an unattended run never pushes (flow-core §2.1); handing off is a person's step.
|
|
35
|
+
- Locate the active `meta.json` by current branch; not found → ask the user for the ticket.
|
|
36
|
+
- `git.handoff_branch` empty → refuse, naming the key and an example
|
|
37
|
+
(`git.handoff_branch: study/{TICKET}`): where the study goes is the receiver's convention, never
|
|
38
|
+
flow's guess. `{TICKET}` = `meta.json.ticket`.
|
|
39
|
+
- `origin` not configured → refuse; there is nowhere to push.
|
|
40
|
+
|
|
41
|
+
## 2. Is the study finished?
|
|
42
|
+
|
|
43
|
+
The builder can ask no one. Refuse — pushing nothing, naming every reason that holds — when:
|
|
44
|
+
|
|
45
|
+
- **The study's last phase is not in `phases_done`.** A feature: `context` on XS (what `start`
|
|
46
|
+
records), `design` on S, `plan` on M/L. A bug: `context` on XS, `investigate` otherwise.
|
|
47
|
+
- **`meta.json.pending` is set** — a question is still waiting for its answer.
|
|
48
|
+
- **A clarification is still open** — a question under "Decisions clarified at start" in
|
|
49
|
+
`01-context.md` recorded without its answer.
|
|
50
|
+
|
|
51
|
+
Any refusal names the way on: finish what is missing, or **build here** (`/flow-feat-build` /
|
|
52
|
+
`/flow-bug-fix`) — a run that chained here (flow-core §2, a study made for handoff) must not end on
|
|
53
|
+
a dead end.
|
|
54
|
+
|
|
55
|
+
Then, **before anything is pushed**: decisions this session settled while talking that no artifact
|
|
56
|
+
records yet → write them where they belong now (`01-context.md` "Decisions clarified at start",
|
|
57
|
+
`03-design.md` "Decisions (ADR-light)", `04-mr-plan.md`), and say which. What was decided only in
|
|
58
|
+
the chat does not travel.
|
|
59
|
+
|
|
60
|
+
## 3. Build the commit, without touching the checkout
|
|
61
|
+
|
|
62
|
+
A throwaway index holds only this folder, so the current branch, `HEAD`, the real index (staged
|
|
63
|
+
work included) and the working tree are never read into it nor written to, and a git-ignored
|
|
64
|
+
`.claude/work/` is no obstacle.
|
|
65
|
+
|
|
66
|
+
**Each block below is one shell call, run as it stands** — a harness need not keep variables from
|
|
67
|
+
one call to the next, so every block sets its own. Replace each `@…@` with its literal value first:
|
|
68
|
+
`@WORK@` the work folder relative to the repository root (`.claude/work/<work-dir>`, no trailing
|
|
69
|
+
slash), `@BRANCH@` the resolved `git.handoff_branch`, `@TICKET@` `meta.json.ticket`, `@COMMIT@` the
|
|
70
|
+
sha the build block printed. A value holding a `'` cannot go inside those quotes: refuse it and
|
|
71
|
+
say which.
|
|
72
|
+
|
|
73
|
+
First, the branch must not exist on `origin` — a second push would need a force, and this command
|
|
74
|
+
never forces:
|
|
75
|
+
|
|
76
|
+
```bash handoff-check
|
|
77
|
+
BRANCH='@BRANCH@'
|
|
78
|
+
git ls-remote --exit-code origin "refs/heads/$BRANCH" >/dev/null; echo "rc=$?"
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
`rc=0` → the branch exists: refuse, and say what the person does next — delete it on the forge
|
|
82
|
+
(only when nothing still reads it), or set another name in `git.handoff_branch`. `rc=2` → absent,
|
|
83
|
+
continue. Anything else → stop and say why (no network, no access). Then build the commit:
|
|
84
|
+
|
|
85
|
+
```bash handoff-build
|
|
86
|
+
WORK='@WORK@'; TICKET='@TICKET@'
|
|
87
|
+
cd "$(git rev-parse --show-toplevel)" || exit 1
|
|
88
|
+
[ -d "$WORK" ] || { echo "no such folder: $WORK"; exit 1; }
|
|
89
|
+
tmp=$(mktemp -d) || exit 1
|
|
90
|
+
idx="$tmp/index" # does not exist yet: git starts an empty index there
|
|
91
|
+
GIT_INDEX_FILE="$idx" git add -f -- "$WORK" ":(exclude)$WORK/panel.json" &&
|
|
92
|
+
files=$(GIT_INDEX_FILE="$idx" git ls-files) &&
|
|
93
|
+
tree=$(GIT_INDEX_FILE="$idx" git write-tree) &&
|
|
94
|
+
commit=$(git commit-tree "$tree" -m "study $TICKET")
|
|
95
|
+
rc=$?; rm -rf "$tmp"
|
|
96
|
+
[ "$rc" -eq 0 ] && [ -n "$files" ] || { echo "nothing was built (rc=$rc)"; exit 1; }
|
|
97
|
+
printf '%s\n' "$files"; echo "commit=$commit"
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
`GIT_INDEX_FILE` is set per command, never exported, so a failure leaves no session pointing git
|
|
101
|
+
at a deleted index. `panel.json` stays out: it is this machine's terminal panel, and on another one
|
|
102
|
+
it would show this session's last state as if it were theirs. The block failing → its message, and
|
|
103
|
+
stop: `commit-tree` with no git identity configured is the usual cause.
|
|
104
|
+
|
|
105
|
+
## 4. Confirm, then push
|
|
106
|
+
|
|
107
|
+
Show the branch, the remote and the file list the build printed — every path, one per line — then
|
|
108
|
+
ask with `AskUserQuestion` **in every `autonomy.mode`**: a push leaves the machine (flow-core §2).
|
|
109
|
+
**Push** · **Cancel**. Nothing between the list and the question (flow-core §3).
|
|
110
|
+
|
|
111
|
+
```bash handoff-push
|
|
112
|
+
BRANCH='@BRANCH@'; COMMIT='@COMMIT@'
|
|
113
|
+
case "$COMMIT" in ''|*[!0-9a-f]*) echo "not a commit sha: $COMMIT"; exit 1 ;; esac
|
|
114
|
+
git cat-file -e "$COMMIT^{commit}" || exit 1
|
|
115
|
+
git push origin "$COMMIT:refs/heads/$BRANCH"
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
The sha is checked first because `git push origin :refs/heads/<branch>` — an empty source —
|
|
119
|
+
deletes the branch on the remote. A rejected push → its message, verbatim, and stop. Never retried
|
|
120
|
+
with `--force`.
|
|
121
|
+
|
|
122
|
+
## 5. The ticket summary (offer)
|
|
123
|
+
|
|
124
|
+
Only after a successful push, and only in ticket mode with a real tracker id. The people reviewing
|
|
125
|
+
the ticket read it there, never in the work folder; the receiver does not parse it.
|
|
126
|
+
|
|
127
|
+
- **Read the body back** — the summary goes inside the ticket's own text, never over it:
|
|
128
|
+
`gh` → `gh issue view {TICKET} --json body -q .body` · `glab` → `glab issue view {TICKET} -F json`,
|
|
129
|
+
its `description`. Any other tracker → the offer is not made: one line saying flow cannot read that
|
|
130
|
+
body back to keep its text.
|
|
131
|
+
- **The write command** — `tracker.edit_cmd`, `{TICKET}` and `{BODY_FILE}` substituted. Empty →
|
|
132
|
+
`gh issue edit {TICKET} --body-file {BODY_FILE}` for `gh`,
|
|
133
|
+
`glab issue update {TICKET} --description-file {BODY_FILE}` for `glab`.
|
|
134
|
+
- **The block**, short, in the language of the ticket. A feature takes it from `01-context.md`,
|
|
135
|
+
`03-design.md` and `04-mr-plan.md`; a bug from `01-context.md` and `03-investigation.md` — the
|
|
136
|
+
expected behaviour as its criteria, the root cause and the intended fix as its design, "Areas with
|
|
137
|
+
similar risk" as its risks. A section whose source does not exist (an XS study, an S feature with
|
|
138
|
+
no plan) says `none`:
|
|
139
|
+
|
|
140
|
+
```
|
|
141
|
+
<!-- flow-study -->
|
|
142
|
+
## What and why
|
|
143
|
+
## Acceptance criteria
|
|
144
|
+
## Design in brief
|
|
145
|
+
## MR/PR plan
|
|
146
|
+
## Risks
|
|
147
|
+
<!-- end flow-study -->
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
Criteria keep their literal values. The plan lists each MR/PR with what it covers and what it
|
|
151
|
+
waits on; a bug's plan is its single fix.
|
|
152
|
+
- **The new body**: the body read above with the block between the two markers replaced — or, with
|
|
153
|
+
no markers yet, the block appended after a blank line. Written to a `mktemp` file outside the
|
|
154
|
+
tree — `{BODY_FILE}` — so nothing of it can be committed later.
|
|
155
|
+
- **Show the block and ask** with `AskUserQuestion`, **in every mode**: it writes to a shared
|
|
156
|
+
tracker. **Write it** · **Skip**. Failure → its message in one line; the push stands.
|
|
157
|
+
|
|
158
|
+
## 6. Close
|
|
159
|
+
|
|
160
|
+
Report following flow-core §3: the branch and the commit, the file count, whether the ticket
|
|
161
|
+
summary was written. Refresh the panel: a `Now` line saying the study was handed off, no `Decision`
|
|
162
|
+
line. `meta.json` gains nothing — the branch on `origin` is the record, and the §3 refusal reads it.
|
|
@@ -92,7 +92,7 @@ With what is new (there is already work on disk):
|
|
|
92
92
|
## 4. Next step
|
|
93
93
|
|
|
94
94
|
- An `Unattended:` line → the question is the orchestrator's to bring back: say so, and that answering it here instead means clearing `meta.json.pending` and continuing in this session's mode; the next step itself comes from `phase` as always.
|
|
95
|
-
- Suggest the concrete command from `phase` and `size
|
|
95
|
+
- Suggest the concrete command from `phase` and `size` — a study whose next step is `build` or `fix`: `git.handoff_branch` set and not `unattended` → `/flow-work-handoff` in its place, **Hand off the study** recommended and **Build here** second where it asks (flow-core §2, a study made for handoff). Interrupted phase (e.g. `build` with an empty artifact) → suggest rerunning it with `/flow-feat-build` or `/flow-bug-fix`.
|
|
96
96
|
- §2.5 found something new → let it inform the suggestion (a contract that arrived after you left may mean rerunning `design` rather than continuing `build`); say why in one line.
|
|
97
97
|
- `meta.json.related_repos` entries not `done` → remind that a **sibling repo still has a pending part** (`<repo>: <scope>`, plus `contract not handed over` when its `contract_handoff` is `pending`); suggest `/flow-feat-start <TICKET>` there. flow only reminds; it never scans or touches the other repo.
|
|
98
98
|
- Do not advance on your own. The user decides.
|
|
@@ -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.88.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
|
|
@@ -217,6 +217,16 @@ sensible default and record it.
|
|
|
217
217
|
|
|
218
218
|
Asking these anyway is how a run in `guided`/`auto` degrades into a manual one.
|
|
219
219
|
|
|
220
|
+
**A study made for handoff.** `git.handoff_branch` set and the mode not `unattended` → the study is
|
|
221
|
+
built somewhere else, so wherever a study phase (`start`, `design`, `plan`, `investigate`) or
|
|
222
|
+
`resume` would hand the work to `/flow-feat-build` or `/flow-bug-fix`, it hands it to
|
|
223
|
+
`/flow-work-handoff` instead. Only the target changes: a Close that asks (`manual`) offers **Hand
|
|
224
|
+
off the study** recommended and **Build here** second; a Close that chains (`guided`/`auto`) chains
|
|
225
|
+
into handoff, whose push question is the one confirmation — no stop is added. In `unattended` the
|
|
226
|
+
rule does not apply: the repo's `FLOW.md` reaches the server too, and a run that studies a ticket
|
|
227
|
+
there goes on to build it. A repo that hands its studies off pairs the key with
|
|
228
|
+
`autonomy.mode: auto`, and the study then runs on its own up to the push.
|
|
229
|
+
|
|
220
230
|
### 2.1 `unattended` — where a person would have been asked
|
|
221
231
|
|
|
222
232
|
No person is there to answer a question in this mode. A question tool — `AskUserQuestion`, OpenCode's `question`,
|
|
@@ -34,7 +34,7 @@ Read `~/.claude/flow/CORE.opencode.md` first (shared rules: `FLOW.md` step 0, mo
|
|
|
34
34
|
**In `unattended`, before this phase's first step, read flow-core §2.1 whole — from its heading to the next section, unless it is already in your context whole; having read parts of it does not count** — the orchestrator command, the question stop and the way its answer comes back are all in it, and a phase that reads only part of it skips them.
|
|
35
35
|
|
|
36
36
|
- Read `meta.json` and `00-summary.md`; open in full only `01-context.md`. (flow-core §5)
|
|
37
|
-
- `type` is not `bug` → refuse. `size` is `XS` → suggest `/flow-bug-fix` and stop.
|
|
37
|
+
- `type` is not `bug` → refuse. `size` is `XS` → suggest `/flow-bug-fix` — `git.handoff_branch` set and not `unattended` → `/flow-work-handoff` in its place, **Hand off the study** recommended and **Build here** second where it asks (flow-core §2, a study made for handoff) — and stop.
|
|
38
38
|
- Require `context` in `phases_done`.
|
|
39
39
|
|
|
40
40
|
## 1.5 Reproduce — what is failing, before why
|
|
@@ -219,5 +219,5 @@ If `knowledge.stage` is set and the root cause reveals a **non-obvious "why"** a
|
|
|
219
219
|
|
|
220
220
|
- Update `meta.json`: `phase = "investigate"`, add to `phases_done`.
|
|
221
221
|
- Overwrite `00-summary.md` whole (≤15 lines, flow-core §5).
|
|
222
|
-
- Suggest `/flow-bug-fix
|
|
223
|
-
- **Autonomy handoff** (`autonomy.mode`, flow-core §2): `manual` → propose `/flow-bug-fix` with a single `AskUserQuestion`, invoke it only on confirmation; `guided`/`auto` → chain into it in this same turn.
|
|
222
|
+
- Suggest `/flow-bug-fix` — `git.handoff_branch` set and not `unattended` → `/flow-work-handoff` in its place, **Hand off the study** recommended and **Build here** second where it asks (flow-core §2, a study made for handoff).
|
|
223
|
+
- **Autonomy handoff** (`autonomy.mode`, flow-core §2): `manual` → propose `/flow-bug-fix` with a single `AskUserQuestion`, invoke it only on confirmation; `guided`/`auto` → chain into it in this same turn. Either way: `git.handoff_branch` set and not `unattended` → `/flow-work-handoff` in its place, **Hand off the study** recommended and **Build here** second where it asks (flow-core §2, a study made for handoff).
|
|
@@ -226,6 +226,6 @@ Run `tracker.start_cmd` with `{TICKET}` = `meta.json.ticket` and `{ASSIGNEE}` =
|
|
|
226
226
|
|
|
227
227
|
## 5. Close
|
|
228
228
|
|
|
229
|
-
- Suggest the next command by size: `/flow-bug-fix` for XS, `/flow-bug-investigate` for the rest.
|
|
229
|
+
- Suggest the next command by size: `/flow-bug-fix` for XS (`git.handoff_branch` set and not `unattended` → `/flow-work-handoff` in its place, **Hand off the study** recommended and **Build here** second where it asks (flow-core §2, a study made for handoff)), `/flow-bug-investigate` for the rest.
|
|
230
230
|
- Overwrite `00-summary.md` whole (≤15 lines, flow-core §5).
|
|
231
231
|
- Apply `autonomy.mode`: `manual` stops and recommends; `guided`/`auto` chain into that command automatically, subject to the hard gates.
|
|
@@ -38,7 +38,7 @@ after writing each challenge's Response.
|
|
|
38
38
|
|
|
39
39
|
- Load `meta.json` by current branch — in `unattended`, the work `/flow-work-run` found (flow-core §2.1). Missing → ask the user to start with `/flow-feat-start`.
|
|
40
40
|
- Read `meta.json` and `00-summary.md`; open in full only `01-context.md` (ticket, decisions, contracts received). (flow-core §5)
|
|
41
|
-
- `size` is `XS` → suggest jumping to `/flow-feat-build` and stop unless the user insists.
|
|
41
|
+
- `size` is `XS` → suggest jumping to `/flow-feat-build` — `git.handoff_branch` set and not `unattended` → `/flow-work-handoff` in its place, **Hand off the study** recommended and **Build here** second where it asks (flow-core §2, a study made for handoff) — and stop unless the user insists.
|
|
42
42
|
|
|
43
43
|
## 1.5 Approaches — open the option space first (M/L)
|
|
44
44
|
|
|
@@ -406,6 +406,6 @@ Never invoke `knowledge.save` here — the final save is in `/flow-feat-ship` af
|
|
|
406
406
|
- **Confirm the acceptance criteria** within the design review: present the enumerated list. If every criterion is unambiguous and verifiable, the review covers them — no separate prompt. `AskUserQuestion` **only** when a criterion is ambiguous, not verifiable, or one is likely missing for today's ticket (same restraint as the challenger). Apply the user's edits to `03-design.md` before advancing.
|
|
407
407
|
- **In `manual`/`guided`**, ask the user to review the design; edit the artifact on requested changes before advancing. **In `auto`, do not ask** — record it as accepted per the handoff below. Not an unconditional stop.
|
|
408
408
|
- Next step by size:
|
|
409
|
-
- **XS / S**: suggest `/flow-feat-build` (1 single MR/PR, no planned split).
|
|
409
|
+
- **XS / S**: suggest `/flow-feat-build` (1 single MR/PR, no planned split) — `git.handoff_branch` set and not `unattended` → `/flow-work-handoff` in its place, **Hand off the study** recommended and **Build here** second where it asks (flow-core §2, a study made for handoff).
|
|
410
410
|
- **M / L**: suggest `/flow-feat-plan` to split the work into independently mergeable MRs/PRs before implementing.
|
|
411
|
-
- **Autonomy handoff.** Design review is a genuine decision point: `manual` and `guided` ask for it before advancing. `auto` records the design as accepted in the artifact and **chains into the command for the size** in this same turn. `manual` proposes that command with a single `AskUserQuestion` (recommended option by default), never a written suggestion. Unresolved `high`-severity findings (§6) stop the flow in **every** mode — do not chain over them.
|
|
411
|
+
- **Autonomy handoff.** Design review is a genuine decision point: `manual` and `guided` ask for it before advancing. `auto` records the design as accepted in the artifact and **chains into the command for the size** in this same turn — `build` there: `git.handoff_branch` set and not `unattended` → `/flow-work-handoff` in its place, **Hand off the study** recommended and **Build here** second where it asks (flow-core §2, a study made for handoff). `manual` proposes that command with a single `AskUserQuestion` (recommended option by default), never a written suggestion. Unresolved `high`-severity findings (§6) stop the flow in **every** mode — do not chain over them.
|
|
@@ -29,7 +29,7 @@ Delivery planning phase. **No code is written.** Splits the feature into MRs/PRs
|
|
|
29
29
|
- Load `meta.json` by current branch — in `unattended`, the work `/flow-work-run` found (flow-core §2.1). Missing → send the user to `/flow-feat-start`.
|
|
30
30
|
- Require `design` in `phases_done`. Missing → send to `/flow-feat-design` and stop.
|
|
31
31
|
- Read `meta.json` and `00-summary.md`; open in full only `03-design.md`. (flow-core §5)
|
|
32
|
-
- **`size` `XS` or `S`** → warn that this phase does not apply (always 1 MR/PR), add `"plan:skipped"` to `phases_done`, suggest `/flow-feat-build
|
|
32
|
+
- **`size` `XS` or `S`** → warn that this phase does not apply (always 1 MR/PR), add `"plan:skipped"` to `phases_done`, suggest `/flow-feat-build` — `git.handoff_branch` set and not `unattended` → `/flow-work-handoff` in its place, **Hand off the study** recommended and **Build here** second where it asks (flow-core §2, a study made for handoff). Stop.
|
|
33
33
|
|
|
34
34
|
## 2. Work
|
|
35
35
|
|
|
@@ -163,5 +163,5 @@ A slice landing in **another repo** is not one of *this* repo's `mrs`: record it
|
|
|
163
163
|
|
|
164
164
|
`∥` = no dependency between them, can be built in parallel or as a train; `→` = the next wave waits for the previous one to merge. Then the table with `#`, wave, `depends_on`, title and estimate. One line for the split rationale, and — when any pair was merged — one line saying how many were and what they would have cost (`merged #a into #b: would have been a wave of its own`). Nothing else — risks and discarded alternatives live in the artifact.
|
|
165
165
|
- Changes requested → edit the artifact and `meta.json.mrs` before advancing.
|
|
166
|
-
- Suggest `/flow-feat-build` to start the first MR/PR.
|
|
167
|
-
- **Autonomy handoff.** Approving the split is a genuine decision point: in `manual` and `guided` ask before advancing. In `auto`, record the plan as accepted in `04-mr-plan.md` and **chain into `/flow-feat-build` automatically** in this same turn. In `manual`, propose it with a single `AskUserQuestion`.
|
|
166
|
+
- Suggest `/flow-feat-build` to start the first MR/PR — `git.handoff_branch` set and not `unattended` → `/flow-work-handoff` in its place, **Hand off the study** recommended and **Build here** second where it asks (flow-core §2, a study made for handoff).
|
|
167
|
+
- **Autonomy handoff.** Approving the split is a genuine decision point: in `manual` and `guided` ask before advancing. In `auto`, record the plan as accepted in `04-mr-plan.md` and **chain into `/flow-feat-build` automatically** in this same turn. In `manual`, propose it with a single `AskUserQuestion`. Either way: `git.handoff_branch` set and not `unattended` → `/flow-work-handoff` in its place, **Hand off the study** recommended and **Build here** second where it asks (flow-core §2, a study made for handoff).
|
|
@@ -307,5 +307,5 @@ Run `tracker.start_cmd` with `{TICKET}` = `meta.json.ticket`, `{ASSIGNEE}` = `tr
|
|
|
307
307
|
## 7. Close
|
|
308
308
|
|
|
309
309
|
- Overwrite `00-summary.md` whole (≤15 lines, flow-core §5) — first written in §6; refresh it if §6.5 changed anything.
|
|
310
|
-
- Report **following the stop header** (flow-core §3), branch added, body 2-3 lines: ticket, size, branch; recommended next command per the §4 table — in `guided`/`auto` you run it in this same turn, so there is no report and no question here: update the panel and chain (flow-core §3).
|
|
310
|
+
- Report **following the stop header** (flow-core §3), branch added, body 2-3 lines: ticket, size, branch; recommended next command per the §4 table (`build` on XS: `git.handoff_branch` set and not `unattended` → `/flow-work-handoff` in its place, **Hand off the study** recommended and **Build here** second where it asks (flow-core §2, a study made for handoff)) — in `guided`/`auto` you run it in this same turn, so there is no report and no question here: update the panel and chain (flow-core §3).
|
|
311
311
|
- Apply `autonomy.mode`: `manual` → stop and let the user invoke the recommended command; `guided`/`auto` → chain into it, subject to the hard gates (the §5 branch-base ambiguity already stops on its own).
|
|
@@ -247,6 +247,7 @@ Two signals arrive on an open MR/PR, each with its own cross-cutting loop (feat
|
|
|
247
247
|
- `/flow-work-daily [question]` — the **work assistant** (daily standup), read-only. **Local** (`.claude/work/` + git) + **forge** (open MRs/PRs, reviews awaiting you, red CI, unmergeable, unresolved threads via `git.cli`) + **tracker** (assigned tickets, priority changes via `tracker.tool`); where they cross, concrete commands (`/flow-feat-start`, `/flow-work-green`, `/flow-work-respond`). No argument → *yesterday · today · blockers*; a question → just that. External sources best-effort, never blocking; the only write is a "last seen" marker, like `/flow-news`.
|
|
248
248
|
- `/flow-work-status` — all works in `.claude/work/`, current phase, divergence with git.
|
|
249
249
|
- `/flow-work-run [ticket]` — the **entry an orchestrator launches** for an `unattended` run: asks it `why` (start · answer · merged · review · pipeline), routes to the phase that answers it, and makes no decision of its own. Refuses to run in any other mode.
|
|
250
|
+
- `/flow-work-handoff` — hands a **finished study** to whoever builds it elsewhere (an unattended server, another machine): refuses an unfinished one, pushes only this work's folder as a parentless commit to `git.handoff_branch` without touching the checkout, asks before the push, then offers a marked summary for the ticket body (`tracker.edit_cmd`).
|
|
250
251
|
- `/flow-work-resume` — detects the branch, reads `meta.json`, recaps, suggests the next step. **Re-reads the ticket's comment thread** (§2.5), reports only what is new since `01-context.md`, appends it to the artifact; never amends the design on its own.
|
|
251
252
|
- `/flow-work-try <branch>` / `/flow-work-try --back` — point the main checkout at a branch to test it here, then return; re-syncs per `git.worktree_resync`. Resolves the work behind the branch and, once the environment is up, **prints the manual test plan already written** (the `needs-manual`/`unproven` criteria, or the acceptance criteria when `validate` has not run) and offers to collect Pass/Fail/Blocked into the validation artifact. No work behind the branch → the plain switch, as before.
|
|
252
253
|
- `/flow-work-clean [--dry-run]` — **periodic sweep** of finished work's residue: worktree, local branch, `.claude/work/` folder (intermediate train MR/PRs never set `phase: done`, so the `ship`/`abandon` prompt never fires). Verdict per branch from **the forge** with local fallbacks, including patch-equivalence for **squash merges** (`git branch --merged` misses them); acts only on `merged`/`empty`. `unknown` never a candidate; dirty and unpushed protected; work folders **archived, never deleted**; remote never touched. **`autonomy.mode` does not authorize deletion**: `auto` confirms the list too. `--purge-archive <N>d`: separate opt-in pass over `_archive/`, only folders already committed to git.
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Hand a finished study to whoever builds it elsewhere — push only this work's folder to a branch of its own, then offer a summary for the ticket
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
<!-- GENERATED by script/adapter-build.py from plugins/flow/commands/work/handoff.md — do not edit; change the plugin file and run the script. -->
|
|
6
|
+
|
|
7
|
+
# `/flow-work-handoff`
|
|
8
|
+
|
|
9
|
+
> **opencode adapter — how the Claude Code primitives named below map here.**
|
|
10
|
+
> - `AskUserQuestion` → ask in plain text with numbered options and wait for the reply.
|
|
11
|
+
> - `Agent <role>` / `Agent general-purpose` / subagents → `@<name>` declared in `agents/<name>.md` (`mode: subagent`); the name comes from `agents.<role>` in `FLOW.md`, empty → do it in this context.
|
|
12
|
+
> - Parallel fan-out → several `@name` in one prompt, capped at `agents.fanout_max` (empty → 4); `agents.fanout_tool` is Claude Code-only, ignore it.
|
|
13
|
+
> - `ScheduleWakeup` / `Monitor` / `/loop` → not available in-session: run one cycle, persist state in `monitor.md`, let the user schedule `opencode run -p "<command>"` with cron.
|
|
14
|
+
> - `TaskCreate` → a markdown checklist in the phase artifact.
|
|
15
|
+
> - `Skill commit-commands:commit-push-pr` → `git add` · `git commit` · `git push -u origin HEAD` · the `git.cli` CLI (`gh pr create` / `glab mr create`).
|
|
16
|
+
> - `/model <value>` → opencode's model picker (`/models`).
|
|
17
|
+
> - `knowledge.*` roles → whatever tools `FLOW.md` names there; an MCP tool keeps its name, its server is declared in `opencode.json` (see this adapter's `opencode.json` for the domain-memory example).
|
|
18
|
+
|
|
19
|
+
Read `~/.claude/flow/CORE.opencode.md` first (shared rules: `FLOW.md` step 0, autonomy, how a stop reads, the live panel, `00-summary.md`) — skip if you already read it in this session.
|
|
20
|
+
|
|
21
|
+
**Panel words are closed** — `mark`: `done` · `current` · `pending` · `wait` · `block` · `info`; `style`: `normal` · `dim` · `title` · `accent` · `ok` · `warn` · `error`. Anything else is dropped by the reader in silence: the panel still paints, and nobody is told.
|
|
22
|
+
|
|
23
|
+
For a study made here and built somewhere else — a server running `/flow-work-run` unattended,
|
|
24
|
+
another person, another machine. The builder gets the work folder at the same path and nothing
|
|
25
|
+
else: no conversation, maybe a less capable model. So this command checks the study can stand on
|
|
26
|
+
its own, pushes the folder to a branch that holds only it, and offers to put a summary in the
|
|
27
|
+
ticket for the people who review it.
|
|
28
|
+
|
|
29
|
+
## 1. Pre-flight
|
|
30
|
+
|
|
31
|
+
- Effective FLOW configuration per flow-core §0. `autonomy.mode: unattended` → refuse in one line:
|
|
32
|
+
an unattended run never pushes (flow-core §2.1); handing off is a person's step.
|
|
33
|
+
- Locate the active `meta.json` by current branch; not found → ask the user for the ticket.
|
|
34
|
+
- `git.handoff_branch` empty → refuse, naming the key and an example
|
|
35
|
+
(`git.handoff_branch: study/{TICKET}`): where the study goes is the receiver's convention, never
|
|
36
|
+
flow's guess. `{TICKET}` = `meta.json.ticket`.
|
|
37
|
+
- `origin` not configured → refuse; there is nowhere to push.
|
|
38
|
+
|
|
39
|
+
## 2. Is the study finished?
|
|
40
|
+
|
|
41
|
+
The builder can ask no one. Refuse — pushing nothing, naming every reason that holds — when:
|
|
42
|
+
|
|
43
|
+
- **The study's last phase is not in `phases_done`.** A feature: `context` on XS (what `start`
|
|
44
|
+
records), `design` on S, `plan` on M/L. A bug: `context` on XS, `investigate` otherwise.
|
|
45
|
+
- **`meta.json.pending` is set** — a question is still waiting for its answer.
|
|
46
|
+
- **A clarification is still open** — a question under "Decisions clarified at start" in
|
|
47
|
+
`01-context.md` recorded without its answer.
|
|
48
|
+
|
|
49
|
+
Any refusal names the way on: finish what is missing, or **build here** (`/flow-feat-build` /
|
|
50
|
+
`/flow-bug-fix`) — a run that chained here (flow-core §2, a study made for handoff) must not end on
|
|
51
|
+
a dead end.
|
|
52
|
+
|
|
53
|
+
Then, **before anything is pushed**: decisions this session settled while talking that no artifact
|
|
54
|
+
records yet → write them where they belong now (`01-context.md` "Decisions clarified at start",
|
|
55
|
+
`03-design.md` "Decisions (ADR-light)", `04-mr-plan.md`), and say which. What was decided only in
|
|
56
|
+
the chat does not travel.
|
|
57
|
+
|
|
58
|
+
## 3. Build the commit, without touching the checkout
|
|
59
|
+
|
|
60
|
+
A throwaway index holds only this folder, so the current branch, `HEAD`, the real index (staged
|
|
61
|
+
work included) and the working tree are never read into it nor written to, and a git-ignored
|
|
62
|
+
`.claude/work/` is no obstacle.
|
|
63
|
+
|
|
64
|
+
**Each block below is one shell call, run as it stands** — a harness need not keep variables from
|
|
65
|
+
one call to the next, so every block sets its own. Replace each `@…@` with its literal value first:
|
|
66
|
+
`@WORK@` the work folder relative to the repository root (`.claude/work/<work-dir>`, no trailing
|
|
67
|
+
slash), `@BRANCH@` the resolved `git.handoff_branch`, `@TICKET@` `meta.json.ticket`, `@COMMIT@` the
|
|
68
|
+
sha the build block printed. A value holding a `'` cannot go inside those quotes: refuse it and
|
|
69
|
+
say which.
|
|
70
|
+
|
|
71
|
+
First, the branch must not exist on `origin` — a second push would need a force, and this command
|
|
72
|
+
never forces:
|
|
73
|
+
|
|
74
|
+
```bash handoff-check
|
|
75
|
+
BRANCH='@BRANCH@'
|
|
76
|
+
git ls-remote --exit-code origin "refs/heads/$BRANCH" >/dev/null; echo "rc=$?"
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
`rc=0` → the branch exists: refuse, and say what the person does next — delete it on the forge
|
|
80
|
+
(only when nothing still reads it), or set another name in `git.handoff_branch`. `rc=2` → absent,
|
|
81
|
+
continue. Anything else → stop and say why (no network, no access). Then build the commit:
|
|
82
|
+
|
|
83
|
+
```bash handoff-build
|
|
84
|
+
WORK='@WORK@'; TICKET='@TICKET@'
|
|
85
|
+
cd "$(git rev-parse --show-toplevel)" || exit 1
|
|
86
|
+
[ -d "$WORK" ] || { echo "no such folder: $WORK"; exit 1; }
|
|
87
|
+
tmp=$(mktemp -d) || exit 1
|
|
88
|
+
idx="$tmp/index" # does not exist yet: git starts an empty index there
|
|
89
|
+
GIT_INDEX_FILE="$idx" git add -f -- "$WORK" ":(exclude)$WORK/panel.json" &&
|
|
90
|
+
files=$(GIT_INDEX_FILE="$idx" git ls-files) &&
|
|
91
|
+
tree=$(GIT_INDEX_FILE="$idx" git write-tree) &&
|
|
92
|
+
commit=$(git commit-tree "$tree" -m "study $TICKET")
|
|
93
|
+
rc=$?; rm -rf "$tmp"
|
|
94
|
+
[ "$rc" -eq 0 ] && [ -n "$files" ] || { echo "nothing was built (rc=$rc)"; exit 1; }
|
|
95
|
+
printf '%s\n' "$files"; echo "commit=$commit"
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
`GIT_INDEX_FILE` is set per command, never exported, so a failure leaves no session pointing git
|
|
99
|
+
at a deleted index. `panel.json` stays out: it is this machine's terminal panel, and on another one
|
|
100
|
+
it would show this session's last state as if it were theirs. The block failing → its message, and
|
|
101
|
+
stop: `commit-tree` with no git identity configured is the usual cause.
|
|
102
|
+
|
|
103
|
+
## 4. Confirm, then push
|
|
104
|
+
|
|
105
|
+
Show the branch, the remote and the file list the build printed — every path, one per line — then
|
|
106
|
+
ask with `AskUserQuestion` **in every `autonomy.mode`**: a push leaves the machine (flow-core §2).
|
|
107
|
+
**Push** · **Cancel**. Nothing between the list and the question (flow-core §3).
|
|
108
|
+
|
|
109
|
+
```bash handoff-push
|
|
110
|
+
BRANCH='@BRANCH@'; COMMIT='@COMMIT@'
|
|
111
|
+
case "$COMMIT" in ''|*[!0-9a-f]*) echo "not a commit sha: $COMMIT"; exit 1 ;; esac
|
|
112
|
+
git cat-file -e "$COMMIT^{commit}" || exit 1
|
|
113
|
+
git push origin "$COMMIT:refs/heads/$BRANCH"
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
The sha is checked first because `git push origin :refs/heads/<branch>` — an empty source —
|
|
117
|
+
deletes the branch on the remote. A rejected push → its message, verbatim, and stop. Never retried
|
|
118
|
+
with `--force`.
|
|
119
|
+
|
|
120
|
+
## 5. The ticket summary (offer)
|
|
121
|
+
|
|
122
|
+
Only after a successful push, and only in ticket mode with a real tracker id. The people reviewing
|
|
123
|
+
the ticket read it there, never in the work folder; the receiver does not parse it.
|
|
124
|
+
|
|
125
|
+
- **Read the body back** — the summary goes inside the ticket's own text, never over it:
|
|
126
|
+
`gh` → `gh issue view {TICKET} --json body -q .body` · `glab` → `glab issue view {TICKET} -F json`,
|
|
127
|
+
its `description`. Any other tracker → the offer is not made: one line saying flow cannot read that
|
|
128
|
+
body back to keep its text.
|
|
129
|
+
- **The write command** — `tracker.edit_cmd`, `{TICKET}` and `{BODY_FILE}` substituted. Empty →
|
|
130
|
+
`gh issue edit {TICKET} --body-file {BODY_FILE}` for `gh`,
|
|
131
|
+
`glab issue update {TICKET} --description-file {BODY_FILE}` for `glab`.
|
|
132
|
+
- **The block**, short, in the language of the ticket. A feature takes it from `01-context.md`,
|
|
133
|
+
`03-design.md` and `04-mr-plan.md`; a bug from `01-context.md` and `03-investigation.md` — the
|
|
134
|
+
expected behaviour as its criteria, the root cause and the intended fix as its design, "Areas with
|
|
135
|
+
similar risk" as its risks. A section whose source does not exist (an XS study, an S feature with
|
|
136
|
+
no plan) says `none`:
|
|
137
|
+
|
|
138
|
+
```
|
|
139
|
+
<!-- flow-study -->
|
|
140
|
+
## What and why
|
|
141
|
+
## Acceptance criteria
|
|
142
|
+
## Design in brief
|
|
143
|
+
## MR/PR plan
|
|
144
|
+
## Risks
|
|
145
|
+
<!-- end flow-study -->
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
Criteria keep their literal values. The plan lists each MR/PR with what it covers and what it
|
|
149
|
+
waits on; a bug's plan is its single fix.
|
|
150
|
+
- **The new body**: the body read above with the block between the two markers replaced — or, with
|
|
151
|
+
no markers yet, the block appended after a blank line. Written to a `mktemp` file outside the
|
|
152
|
+
tree — `{BODY_FILE}` — so nothing of it can be committed later.
|
|
153
|
+
- **Show the block and ask** with `AskUserQuestion`, **in every mode**: it writes to a shared
|
|
154
|
+
tracker. **Write it** · **Skip**. Failure → its message in one line; the push stands.
|
|
155
|
+
|
|
156
|
+
## 6. Close
|
|
157
|
+
|
|
158
|
+
Report following flow-core §3: the branch and the commit, the file count, whether the ticket
|
|
159
|
+
summary was written. Refresh the panel: a `Now` line saying the study was handed off, no `Decision`
|
|
160
|
+
line. `meta.json` gains nothing — the branch on `origin` is the record, and the §3 refusal reads it.
|
|
@@ -90,7 +90,7 @@ With what is new (there is already work on disk):
|
|
|
90
90
|
## 4. Next step
|
|
91
91
|
|
|
92
92
|
- An `Unattended:` line → the question is the orchestrator's to bring back: say so, and that answering it here instead means clearing `meta.json.pending` and continuing in this session's mode; the next step itself comes from `phase` as always.
|
|
93
|
-
- Suggest the concrete command from `phase` and `size
|
|
93
|
+
- Suggest the concrete command from `phase` and `size` — a study whose next step is `build` or `fix`: `git.handoff_branch` set and not `unattended` → `/flow-work-handoff` in its place, **Hand off the study** recommended and **Build here** second where it asks (flow-core §2, a study made for handoff). Interrupted phase (e.g. `build` with an empty artifact) → suggest rerunning it with `/flow-feat-build` or `/flow-bug-fix`.
|
|
94
94
|
- §2.5 found something new → let it inform the suggestion (a contract that arrived after you left may mean rerunning `design` rather than continuing `build`); say why in one line.
|
|
95
95
|
- `meta.json.related_repos` entries not `done` → remind that a **sibling repo still has a pending part** (`<repo>: <scope>`, plus `contract not handed over` when its `contract_handoff` is `pending`); suggest `/flow-feat-start <TICKET>` there. flow only reminds; it never scans or touches the other repo.
|
|
96
96
|
- Do not advance on your own. The user decides.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "flow-workflows",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.88.0",
|
|
4
4
|
"description": "Guided feat/bug workflows (start→ship), post-deploy monitoring and multi-agent code review, stack-agnostic. Each repo uses a shared FLOW.md plus optional harness overlay.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"name": "flow",
|
|
4
4
|
"displayName": "Flow — guided dev workflows",
|
|
5
5
|
"description": "Guided feat/bug workflows (start→ship), post-deploy monitoring and multi-agent code review, stack-agnostic. Each repo uses a shared FLOW.md plus optional harness overlay.",
|
|
6
|
-
"version": "0.
|
|
6
|
+
"version": "0.88.0",
|
|
7
7
|
"author": {
|
|
8
8
|
"name": "Alberto Vioque",
|
|
9
9
|
"email": "mashware@gmail.com"
|