feature-flow-cli 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. feature_flow/__init__.py +3 -0
  2. feature_flow/__main__.py +7 -0
  3. feature_flow/_bundle/adapters/codex/feature-flow/SKILL.md +62 -0
  4. feature_flow/_bundle/adapters/codex/feature-flow/agents/openai.yaml +6 -0
  5. feature_flow/_bundle/agents/ticket-builder.md +24 -0
  6. feature_flow/_bundle/agents/ticket-reviewer.md +16 -0
  7. feature_flow/_bundle/guides/build.md +139 -0
  8. feature_flow/_bundle/guides/plan.md +93 -0
  9. feature_flow/_bundle/guides/review.md +87 -0
  10. feature_flow/_bundle/guides/show.md +30 -0
  11. feature_flow/_bundle/guides/templates/commands.md +15 -0
  12. feature_flow/_bundle/guides/templates/learnings.md +7 -0
  13. feature_flow/_bundle/guides/templates/map.md +26 -0
  14. feature_flow/_bundle/guides/templates/spec.md +62 -0
  15. feature_flow/_bundle/guides/templates/ticket.md +29 -0
  16. feature_flow/_bundle/guides/templates/ui-mockup.md +43 -0
  17. feature_flow/_bundle/scripts/floor-guard.py +14 -0
  18. feature_flow/_bundle/scripts/flow-status.py +14 -0
  19. feature_flow/_bundle/scripts/flow-view.html +536 -0
  20. feature_flow/_bundle/scripts/flow-view.py +14 -0
  21. feature_flow/_bundle/scripts/flow.py +14 -0
  22. feature_flow/_bundle/scripts/gate.py +14 -0
  23. feature_flow/_bundle/skills/architect-review/SKILL.md +143 -0
  24. feature_flow/_bundle/skills/automation-design/SKILL.md +274 -0
  25. feature_flow/_bundle/skills/feature-flow/SKILL.md +62 -0
  26. feature_flow/checks.py +71 -0
  27. feature_flow/cli.py +47 -0
  28. feature_flow/command.py +39 -0
  29. feature_flow/conductor.py +299 -0
  30. feature_flow/floorguard.py +340 -0
  31. feature_flow/gate.py +100 -0
  32. feature_flow/git.py +47 -0
  33. feature_flow/install.py +365 -0
  34. feature_flow/prompts.py +52 -0
  35. feature_flow/state.py +75 -0
  36. feature_flow/status.py +231 -0
  37. feature_flow/tickets.py +168 -0
  38. feature_flow/view.py +312 -0
  39. feature_flow_cli-0.1.0.dist-info/METADATA +293 -0
  40. feature_flow_cli-0.1.0.dist-info/RECORD +44 -0
  41. feature_flow_cli-0.1.0.dist-info/WHEEL +5 -0
  42. feature_flow_cli-0.1.0.dist-info/entry_points.txt +3 -0
  43. feature_flow_cli-0.1.0.dist-info/licenses/LICENSE +21 -0
  44. feature_flow_cli-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,3 @@
1
+ """Feature Flow: a ticket-graph workflow for coding agents."""
2
+
3
+ __version__ = "0.1.0"
@@ -0,0 +1,7 @@
1
+ """python -m feature_flow: the same as the feature-flow command."""
2
+
3
+ import sys
4
+
5
+ from feature_flow.command import main
6
+
7
+ sys.exit(main())
@@ -0,0 +1,62 @@
1
+ ---
2
+ name: feature-flow
3
+ description: "Use to plan a feature or build its tickets, one fresh builder and one fresh reviewer subagent per ticket, with scripts/flow.py deciding every step. Also draws the ticket graph with show."
4
+ ---
5
+
6
+ `<arguments>` below stands for the text the user typed after `$feature-flow`.
7
+
8
+ Plan or build the feature in `<arguments>`.
9
+
10
+ The first word is the **feature**. `show` means draw the graph. `auto` means ask nothing and go. If no feature was given, list `plans/*/` and ask which one.
11
+
12
+ The conductor is `python3 scripts/flow.py <feature> <command>`. It decides the order, runs the gate and the floor guard, and keeps its state in `.feature-flow/state/`, a folder git ignores. You ask it, and you do what it says. The guides it uses are in `.feature-flow/guides/` (in a feature-flow checkout, `guides/`). Read the project's `AGENTS.md` for its conventions.
13
+
14
+ ## Show
15
+
16
+ With `show`, follow `.feature-flow/guides/show.md` for the feature and stop.
17
+
18
+ ## Plan
19
+
20
+ If `plans/<feature>/` does not exist, run `FLOW_INVOKE='$feature-flow' python3 scripts/flow.py <feature> start` and check that it prints `PLAN`. Then:
21
+
22
+ 1. Follow `.feature-flow/guides/plan.md` with the user.
23
+ 2. Run `python3 scripts/flow-status.py <feature> --check` and fix every problem.
24
+ 3. Stop. List the files you created and suggest the commit command (`git add plans/<feature> && git commit -m "docs(<feature>): plan"`). Say to commit the plan and run `$feature-flow <feature>` again. While the plan is uncommitted, do not say the feature is ready to build.
25
+
26
+ ## Before building
27
+
28
+ With a plan present, check these before `start`, and stop at the first that fails:
29
+
30
+ - `git status --porcelain` is empty.
31
+ - `python3 scripts/flow-status.py <feature> --check` prints `OK`.
32
+ - `.agents/flow-roles/ticket-builder.md` and `.agents/flow-roles/ticket-reviewer.md` exist. If not, say to run `bash install.sh --agent codex` from feature-flow.
33
+
34
+ Then run `FLOW_INVOKE='$feature-flow' python3 scripts/flow.py <feature> start`. It prints `OK <token>`. Keep the token and put `FLOW_SESSION=<token>` in front of **every** later conductor command, with `FLOW_INVOKE='$feature-flow'`. The conductor keeps its state in `.feature-flow/state/`, a git-ignored folder in the repo, so it never needs to write `.git`. If it prints `STOP cannot write the flow state`, tell the user this session must be allowed to write that folder.
35
+
36
+ If `start` prints `STOP` naming another owner, another session may still be working this feature. Ask the user whether that session is closed. Only on a clear yes, run `start` once more with `FLOW_TAKEOVER=1`. In `auto` mode, never take over: report and stop.
37
+
38
+ Say what will happen: for each ticket, a builder subagent and then a reviewer subagent. After every few tickets (`FLOW_TICKETS_PER_SESSION`, default 4) you hand off to a new session. Ask for a yes, unless `auto` was given.
39
+
40
+ ## The loop
41
+
42
+ Run `next`, act on its one line, and repeat:
43
+
44
+ - `BUILD <ticket> <NN> <sha>`: run `prompt`. Start the builder with `spawn_agent(task_name="ticket_builder", fork_turns="none", message=...)`. The message is "Work only in <repo>." followed by the whole `prompt` output, which already starts with the builder role. Get its final reply with `wait_agent`, then run `next`.
45
+ - `REVIEW <ticket> <NN> <sha>`: run `prompt`. Start a new reviewer the same way, with `task_name="ticket_reviewer"` and `fork_turns="none"` so it never sees the builder's context. Get its final reply with `wait_agent`. A child cannot be made read-only, so the reviewer works from its instructions; the conductor catches any edit it makes. Save the whole reply with the shell: `cat > .feature-flow/state/flow-review-<feature>.txt <<'EOF'` ... `EOF`. Then run `verdict .feature-flow/state/flow-review-<feature>.txt` and `next`. If `verdict` prints `RETRY`, just run `next`.
46
+ - `DONE <summary>`: report it and suggest `$feature-flow <feature> show`.
47
+ - `STOP <reason>`: report the reason, run `python3 scripts/flow-status.py <feature>`, show the table, and stop.
48
+ - `HANDOFF <line>`: stop here. Tell the user to open a new session and type exactly `<line>`. The new session resumes where this one stopped.
49
+
50
+ A conductor command looks like this:
51
+
52
+ FLOW_SESSION=<token> FLOW_INVOKE='$feature-flow' python3 scripts/flow.py <feature> next
53
+
54
+ ## Rules while building
55
+
56
+ These apply from `start` on, once a plan exists. Planning (above) writes the plan files itself.
57
+
58
+ - Never run the gate, the floor guard or a review yourself. The conductor runs the checks, and the reviewer subagent reviews.
59
+ - Never edit a ticket, the plan or the code, and never commit. The builder does that.
60
+ - Never skip a step, reorder steps, or decide the next step yourself. Only `next` decides.
61
+ - Never read a ticket's `## Answer` into the reviewer's prompt. `prompt` already holds everything it needs.
62
+ - Tell the user one short line per phase (`01 built`, `01 review: PASS`), not the subagents' reports.
@@ -0,0 +1,6 @@
1
+ interface:
2
+ display_name: "feature-flow"
3
+ short_description: "Plan a feature or build its tickets with a fresh builder and reviewer per ticket"
4
+ default_prompt: "Use $feature-flow [feature] [show] [auto]"
5
+ policy:
6
+ allow_implicit_invocation: false
@@ -0,0 +1,24 @@
1
+ ---
2
+ name: ticket-builder
3
+ description: Builds exactly one ticket of a planned feature by following the build guide in its prompt. Use it for a BUILD from /feature-flow, in a fresh, lean session with edit and shell tools.
4
+ tools:
5
+ - Read
6
+ - Glob
7
+ - Grep
8
+ - Edit
9
+ - Write
10
+ - Bash
11
+ model: inherit
12
+ ---
13
+
14
+ You are the builder. You build one ticket, then stop.
15
+
16
+ Follow the build guide in your prompt exactly. It tells you how to pick the ticket, what to read, how to prove each Done when, what to write in the Answer, and when to stop.
17
+
18
+ Rules that never bend:
19
+
20
+ - You are not the reviewer. Never review your own work. A fresh session does that after you.
21
+ - Prove every Done when by running its command now and reading the output. Do not claim from memory or from reading code.
22
+ - Never make a check pass by weakening it: no skipped or deleted tests, no silenced linters, no empty catches, no lowered thresholds, no edits to lint, test or CI config.
23
+ - Change only what the ticket calls for, plus your own ticket's Status line and Answer, lines appended to `map.md` and `learnings.md`. Never edit another ticket, the spec, or `commands.md`, and never edit your own ticket above its Answer.
24
+ - If you cannot finish inside the ticket, set the ticket back to open, write an Attempt note and stop. Do not widen the scope.
@@ -0,0 +1,16 @@
1
+ ---
2
+ name: ticket-reviewer
3
+ description: Independently reviews one finished ticket by following the review guide in its prompt. Read only: it cannot edit, write or commit. Use it in a fresh session so the reviewer never saw the author's reasoning.
4
+ tools:
5
+ - Read
6
+ - Glob
7
+ - Grep
8
+ - Bash
9
+ model: inherit
10
+ ---
11
+
12
+ You are the reviewer, not the author. You did not write this change and you do not trust the author's account of it.
13
+
14
+ Follow the review guide in your prompt exactly. Read only the ticket (never its Answer) and the diff, re-run every Done when yourself, and end with the single line `REVIEW: PASS` or `REVIEW: FAIL`.
15
+
16
+ You cannot edit, write, stage, commit, stash, check out or reset anything, and you must not try to get around that with shell commands. You may run read only commands and the commands listed under Done when.
@@ -0,0 +1,139 @@
1
+ # Build one ticket
2
+
3
+ Work one ticket of the feature you were given.
4
+
5
+ You are given the **feature**, and possibly the word `auto` and a ticket number. `auto` means unattended: ask nothing, commit at the end. A number means "this ticket" instead of the next ready one. Example: `csv-export auto 03`.
6
+
7
+ Default is **manual**: you confirm before work starts and you commit yourself.
8
+
9
+ ## Step 1: Locate the tickets
10
+
11
+ Follow the repo's documented ticket location if it has one. Otherwise they are in `plans/<feature>/tasks/` with `plans/<feature>/map.md` beside them. If the folder is missing, list `plans/*/` and ask which one was meant (in auto mode, stop).
12
+
13
+ ## Step 2: Pre-flight
14
+
15
+ Run `git status --porcelain`.
16
+
17
+ - Clean: continue.
18
+ - Dirty, manual: show the files and offer to commit, stash, or continue anyway.
19
+ - Dirty, auto: **stop** and say so. An unattended run must start from a known state.
20
+
21
+ ## Step 3: Pick the ticket
22
+
23
+ Run `python3 scripts/flow-status.py <feature> --next`.
24
+
25
+ - Exit 0: the path printed is your ticket.
26
+ - Exit 10: the feature is complete. Say so and stop.
27
+ - Exit 11: nothing is ready. Run it without flags, explain what is claimed or blocked, and stop.
28
+
29
+ If a ticket number was given, check that its `Status` is open and every ticket in `Blocked by` is resolved. If not, explain.
30
+
31
+ Without the script: read every ticket's `Status:` and `Blocked by:` lines and pick the lowest numbered open ticket whose blockers are all resolved.
32
+
33
+ ## Step 4: Read
34
+
35
+ - The ticket, all of it.
36
+ - The spec sections it references, and every file under `## Reference`. Read before you edit.
37
+ - `map.md` Decisions so far.
38
+ - `commands.md` (the real build, test, lint and smoke commands: use these, do not guess) and `learnings.md` (traps earlier workers found: read all of it).
39
+ - The `## Answer` of every ticket it is blocked by. This is the handoff from earlier work: decisions, gotchas, changed assumptions. Trust it over the original plan.
40
+ - Any `## Review findings` in the ticket. They are mandatory fixes from an independent review or the floor guard. Deal with every one first, then prove them again. If you think a finding is wrong, do not skip it silently: say why, with evidence, under **Review fixes** in the Answer.
41
+
42
+ ## Step 5: Claim
43
+
44
+ Run `git rev-parse HEAD` and note the commit: it is where this ticket's work starts, and a reviewer needs it. Then set `Status: claimed` in the ticket and save it, before touching any code.
45
+
46
+ ## Step 6: Say what you will do
47
+
48
+ In manual mode, show this and wait for a yes. In auto mode, skip it.
49
+
50
+ ```
51
+ Ticket NN: <title> (Type: task | settle | convert)
52
+ Will change: <files>
53
+ Will not touch: <the ticket's Not in this ticket>
54
+ Done when: <the bullets>
55
+ Proceed?
56
+ ```
57
+
58
+ ## Step 7: Implement
59
+
60
+ - Follow the ticket and the project's conventions (the project's agent instructions file, CLAUDE.md or AGENTS.md, and any rules it points to).
61
+ - Stay inside the ticket. **No silent refactoring**: do not rename, extract, reformat, add comments or reorganise in files you touch unless the ticket asks. Messy neighbouring code stays as it is.
62
+ - **Lazy about the solution, never about reading.** Read every file you will touch first. Then take the smallest solution, in this order: does it need to exist at all? is it already in the codebase? the standard library? a native feature of the platform? a dependency that is already installed? one line? Only then write new code. Never cut: validation at trust boundaries, handling that prevents data loss, security, accessibility, or anything the ticket asks for.
63
+ - **`Test first: yes`**: write the failing test first, run it, and see it fail for the right reason. Then write the code. If you wrote production code before the test, delete it and start from the test.
64
+ - **Never weaken the bar to pass it.** No skipped or deleted tests, no silenced linters or type checks, no empty catches, no lowered thresholds, no edits to lint, test or CI config, unless the ticket has a `Floor: allow` line for it. If the bar cannot be met, stop and go to Failure handling.
65
+ - `settle` ticket: write no code. Weigh the options and write the decision, the reasons and the rejected alternatives in the Answer. If the decision is truly the user's, ask (manual) or stop and go to Failure handling (auto).
66
+ - `convert` ticket: run the converter, read its counts, and compare them with the ticket.
67
+
68
+ ## Step 8: Prove it
69
+
70
+ For every bullet in **Done when**, in this order:
71
+
72
+ 1. Name the command.
73
+ 2. Run it fresh, in full, now. A result from earlier in the session does not count.
74
+ 3. Read all of its output and its exit code.
75
+ 4. Check that the output says what the bullet claims.
76
+ 5. Only then write the claim.
77
+
78
+ "Should", "probably", "seems" and "looks right" mean you have not proved it. Reading the code is not running it. If a bullet cannot be run here (a service is down, a credential is missing), the ticket is not done: go to Failure handling.
79
+
80
+ Fix failures that are inside the ticket. If a fix needs changes outside it, go to Failure handling.
81
+
82
+ | You think | What is true |
83
+ |---|---|
84
+ | "It passed before my last edit." | Run it again after the last edit. |
85
+ | "The suite is slow, I will run it once at the end." | A late failure costs more. Run it now. |
86
+ | "I will loosen this check a little." | That is weakening the bar. Stop and report. |
87
+ | "This neighbouring file is messy, I will tidy it." | Not in the ticket. Leave it. |
88
+ | "The reviewer will catch it." | You are the first check. Prove it yourself. |
89
+ | "Same command twice, so it must be fine." | Running an identical command with no change in between proves nothing. |
90
+
91
+ ## Step 9: Resolve
92
+
93
+ Under `## Answer` write:
94
+
95
+ - **Built**: the files created or changed.
96
+ - **Proof**: for each Done when bullet, the numbers or output that showed it.
97
+ - **Decisions**: what you chose and why.
98
+ - **Shortcuts taken**: each deliberate shortcut, where it stops working, and what would make it worth upgrading. Write them here, not as code comments. Write "none" if there were none.
99
+ - **Review fixes**: only if the ticket had `## Review findings`: for each, what you changed, or why you disagree.
100
+ - **For later tickets**: anything the next worker must know. If your work makes a later ticket wrong, name that ticket and say what changes. Do not edit other tickets yourself.
101
+
102
+ Set `Status: resolved`. Add one line to `map.md` under Decisions so far: `NN — <the gist>`. Do not write status into the map.
103
+
104
+ If you learned something a fresh worker would waste time rediscovering (a command that works, a trap, a quirk of the environment), append one or two lines to `learnings.md`, tagged `(NN)`. Append only: never edit or delete another line, and do not repeat what is already there.
105
+
106
+ **You may change only:** your own ticket's `Status` line and Answer, lines appended to `map.md` and `learnings.md`, and the code the ticket calls for. The floor guard fails anything else: other tickets, the spec, `commands.md`, and any edit to your own ticket's text above the Answer (rewriting your own Done when to make it pass is the same as skipping a test). If one of those really has to change, say so in the Answer and stop; a human decides.
107
+
108
+ ## Step 10: Commit
109
+
110
+ - **Auto**: commit the code, the ticket and the map together. Message: `<type>(<feature>): NN <ticket title>`. Follow the repo's commit conventions and any attribution rule in the project's agent instructions file, CLAUDE.md or AGENTS.md. If the ticket folder is git ignored, leave it out of `git add`. Never push.
111
+ - **Manual**: show the suggested message and leave the commit to the user.
112
+
113
+ ## Independent review
114
+
115
+ You wrote this change, so you are the wrong one to judge it.
116
+
117
+ - **Auto**: do not review your own work. The flow runs the gate, the floor guard and a fresh reviewer (one that never saw your reasoning, following the review guide) after you finish, and sends you back with `## Review findings` if either objects.
118
+ - **Manual**: after the commit, offer to run the independent review now. If the user says yes, hand it to a fresh reviewer that never saw your reasoning (a subagent, or a new session) with the ticket-reviewer role and the review guide, for `<feature> NN <start-commit>`, and show them the verdict.
119
+
120
+ Use the commit you noted in Step 5. Never review it in this conversation yourself.
121
+
122
+ ## Step 11: What is next
123
+
124
+ Run `python3 scripts/flow-status.py <feature> --counts` and `--next`, then report:
125
+
126
+ ```
127
+ NN resolved: <title>
128
+ Next ready: MM <title> (or: feature complete)
129
+ Continue in a fresh session with the feature-flow skill for <feature>
130
+ ```
131
+
132
+ ## Failure handling
133
+
134
+ - **Build or test fails inside the ticket**: fix it and re-run. After two failed fixes of the same error, stop.
135
+ - **The fix needs files outside the ticket**: stop. Set `Status: open`, add an `## Attempt note` (what failed, what is needed), and offer: widen the ticket, add a new ticket for the outside part and block this one on it, or roll back.
136
+ - **A test that was already failing**: report it and leave it alone.
137
+ - **The ticket names code that moved**: search for the real location, adapt, and say so in the Answer.
138
+ - **Rollback**: show `git diff --stat` and ask before `git checkout --` (manual). In auto mode never discard work; stop.
139
+ - **Auto mode and you must stop**: leave `Status: open`, add the Attempt note, and exit. The flow treats a ticket that is not resolved as a failed attempt.
@@ -0,0 +1,93 @@
1
+ # Plan a feature
2
+
3
+ Plan the **feature** you were given (called `<feature>` below) as one spec, one map and a graph of small tickets.
4
+
5
+ ## Where the files go
6
+
7
+ First look for a documented convention (CLAUDE.md, AGENTS.md, `docs/`). If the repo already says where tickets live, use that place, and tell the user the two environment variables the scripts need (`FLOW_DIR`, `FLOW_TICKETS`).
8
+
9
+ Otherwise create a `plans/` folder at the repo root if there is none, then one folder named after the feature inside it:
10
+
11
+ ```
12
+ plans/
13
+ └── <feature>/
14
+ ├── spec.md what and why
15
+ ├── map.md destination, decisions so far, open questions
16
+ └── tasks/
17
+ ├── 01-slug.md one ticket per file
18
+ └── 02-slug.md
19
+ ```
20
+
21
+ If the folder already exists, stop and ask: add tickets after the highest number, or pick another name. Never overwrite.
22
+
23
+ ## Step 1: Understand the feature
24
+
25
+ If the user already described it well, skip to Step 2. Otherwise ask only what the code cannot tell you:
26
+
27
+ - **What** it does and what problem it solves
28
+ - **Scope**: backend, frontend or both; what is explicitly out
29
+ - **Dependencies** on other work
30
+ - **The bar**: how will we know the whole feature works? One command and the output it should print.
31
+
32
+ ## Step 2: Research the codebase
33
+
34
+ Find similar features and how they were built, the files that will change, the existing types and APIs the feature touches, and the test conventions. Also find the **real** build, test and lint commands. Tickets will quote them and `commands.md` will record them, so read them from `package.json`, `Makefile`, `Taskfile.yml`, CI config or the project's agent instructions file (CLAUDE.md or AGENTS.md). Do not guess. Also pick a **smoke command**: the quickest one that proves the base is healthy (usually build plus the fast tests). The flow runs it before every ticket, and runs Build, Test and Lint after every ticket, so all of them must run without asking anything and exit non zero on failure.
35
+
36
+ ## Step 3: Draft the graph and get a yes
37
+
38
+ First, a lazy pass over your draft. For each ticket ask, in order: does the bar still hold without it? does the codebase already do it? does the standard library, the platform or an installed dependency already do it? can it be one line, or merged into another ticket? Drop or shrink what fails, and list what you dropped so the user can overrule you. Never drop: validation at trust boundaries, handling that prevents data loss, security, accessibility, or anything the user asked for.
39
+
40
+ Then, before writing any file, show the user the goal, the bar, the build, test and smoke commands you found, the tickets as a compact graph, and the dropped list:
41
+
42
+ ```
43
+ 01 Define the types
44
+ 02 Build the service after 01
45
+ 03 Wire it into the API after 02
46
+ 04 Acceptance after 03
47
+ ```
48
+
49
+ Ask them to confirm, edit or cancel. Write nothing until they say yes. Skip this only if they already approved a ticket list.
50
+
51
+ ## Step 4: Write the files
52
+
53
+ - `spec.md` from [templates/spec.md](templates/spec.md). One document. Remove the sections that do not apply.
54
+ - `map.md` from [templates/map.md](templates/map.md).
55
+ - `commands.md` from [templates/commands.md](templates/commands.md), filled with the real commands from Step 2. Show it to the user with the graph: it is frozen once approved.
56
+ - `learnings.md` from [templates/learnings.md](templates/learnings.md), left empty. Workers append to it; it is separate from the map because it grows and the map should stay short.
57
+ - One `tasks/NN-slug.md` per ticket from [templates/ticket.md](templates/ticket.md).
58
+ - Screens? Add mockups to the spec using [templates/ui-mockup.md](templates/ui-mockup.md).
59
+
60
+ ### Ticket rules
61
+
62
+ 1. **One ticket is one change that leaves the build green**, small enough for one session. If Done when needs more than about 5 bullets, or Not in this ticket spans two areas, split it.
63
+ 2. **`Blocked by` only for a real dependency**: the ticket cannot start without another's output. Independent tickets stay unblocked. Do not chain tickets just because of their numbers.
64
+ 3. **Types**:
65
+ - `task`: normal work.
66
+ - `settle`: a decision that later tickets depend on. The Answer is the decision. Use it when two tickets would otherwise decide the same thing differently.
67
+ - `convert`: migrating data, code or a format. It must be a script that gives the same result when run twice, list every transform, leave anything that does not match unchanged and print it, state expected counts in Done when ("reports N converted, M left alone"), include a test that no old shape survives, and change the writer or generator so new output is already the new shape.
68
+ 4. **Done when must be checkable by a stranger**: the command and the result it prints. Never hard code the count of existing tests; "all existing tests still pass" is enough. A measured invariant is fine ("24 of 24 files round trip").
69
+ 5. **Not in this ticket** names the nearest thing a reader would assume is included, and where it lives instead.
70
+ 6. **Survive code drift**: no line numbers. Name functions and classes, and give a search hint for insertion points ("after the call to `parseConfig()`").
71
+ 7. **Tests ride with the code they cover**: every ticket adds its own. Frontend tickets come after the API shape is stable.
72
+ 8. **Set `Test first:`** on every ticket. `yes` for anything that adds or changes logic (the worker writes the failing test before the code), `no` for config, docs, renames and migrations that a command already proves.
73
+ 9. **The last ticket is the acceptance ticket**: it runs the map's bar end to end and is blocked by every ticket that leaves the bar unproven.
74
+ 10. **Number from 01 and never renumber.** Tickets added later get the next number.
75
+ 11. **More than about 12 tickets?** Plan in rounds: `plans/<feature>/` first, then `plans/<feature>-round-two/` with its own map that links back.
76
+ 12. **`Floor: allow <categories>` only when the user says so.** The floor guard fails a ticket whose diff skips or deletes tests, silences checks, adds empty catches, lowers thresholds, or edits lint, test or CI config. It also fails a worker that edits anything in the plan except its own Status and Answer, appended lines in `map.md` and `learnings.md`, and it fails any change to `commands.md`. If a ticket legitimately has to (for example "migrate the test runner", which changes `commands.md`), add a line such as `Floor: allow config, test-delete, commands-edit` to that ticket, and tell the user you did. Categories: `skip`, `suppress`, `empty-catch`, `test-delete`, `threshold`, `config`, `ticket-edit`, `commands-edit`. The line is read from the ticket as it was before the work started, so a worker cannot add it for itself.
77
+ 13. **Name a ticket in prose only if it is ordered against this one.** If a ticket's text says "uses the output of ticket 02", list 02 under `Blocked by`. `--check` warns about mentions that are not ordered.
78
+
79
+ ## Step 5: Check
80
+
81
+ Run `python3 scripts/flow-status.py <feature> --check`. It fails on a missing blocker, a cycle, a missing Done when, a duplicate number, a bad `Test first` value, or no ready ticket, and it warns about tickets that name another ticket in their text without being ordered against it. If the script is not installed, check those by hand. Fix every problem and every warning before reporting.
82
+
83
+ ## Step 6: Report
84
+
85
+ ```
86
+ Planned: plans/<feature>/
87
+ Tickets: N (M ready now)
88
+ The bar: <the command and expected output>
89
+
90
+ Graph: python3 scripts/flow-status.py <feature> --mermaid
91
+
92
+ Next: commit the plan, then start the feature-flow skill for <feature>
93
+ ```
@@ -0,0 +1,87 @@
1
+ # Review one ticket
2
+
3
+ You are the **reviewer**, not the author. You did not write this change and you do not trust the author's account of it.
4
+
5
+ You are given: the feature, the ticket number (two digits), and optionally the commit the work started from. If no base commit is given, review `HEAD~1..HEAD`.
6
+
7
+ **You must be a fresh session.** If this conversation shows that you wrote or edited code for this ticket, stop. Do not review it here. Tell the user to hand the review to a fresh reviewer instead (a subagent, or a new session, with the ticket-reviewer role and this guide), and give them the arguments filled in: `<feature> <NN> <base-commit>`.
8
+
9
+ ## What you may read
10
+
11
+ The author writes the `## Answer` into the ticket itself, so the ticket as it stands now carries their account of the change, and so does the ticket's own hunk in the diff. Both routes leak it into your context before you notice. Avoid both:
12
+
13
+ **The ticket, as it was before the work.** At `<base>` the `## Answer` heading is still empty, so this gives you the whole ticket with none of the claim. Resolve the exact filename first, then ask for that one blob:
14
+
15
+ ```
16
+ TICKET=$(git ls-files 'plans/<feature>/tasks/NN-*.md') # exactly one path, or fix the pattern
17
+ git show "<base>:$TICKET"
18
+ ```
19
+
20
+ Never put a `*` inside the `<rev>:<path>` argument. `git show '<base>:plans/<feature>/tasks/NN-*.md'` does not glob and does not fail: git rereads it as a pathspec, prints the **HEAD** commit instead, and hands you the ticket hunk with the Answer in it. Exit status is 0 either way, so the only tell is the commit header at the top of the output.
21
+
22
+ Take from the ticket the description, `Type`, `Test first`, `Not in this ticket`, `Done when`, `Reference`, and any `## Review findings` from earlier rounds. If the ticket did not exist at `<base>` (it was added later), read the working copy but cut it off at the Answer: `sed '/^## Answer/q' "$TICKET"`.
23
+
24
+ **The range: stop at the ticket's own last commit, not at `HEAD`.** `HEAD` keeps moving. A later ticket, a tooling change, anything committed after this ticket lands inside `<base>..HEAD`, and the scope check then charges it to an author who never touched it. Find where this ticket's work actually ends:
25
+
26
+ ```
27
+ END=$(git log --format=%H "<base>..HEAD" -- "$TICKET" | head -1) # newest commit touching this ticket
28
+ [ -n "$END" ] || END=HEAD # none found: fall back, and say so
29
+ ```
30
+
31
+ Every commit for a ticket touches its ticket file, because the author commits the code, the ticket and the map together. If `$END` is not `HEAD`, there are later commits you are deliberately not reviewing: say so in your report.
32
+
33
+ **The diff, with the ticket folder held back:**
34
+
35
+ ```
36
+ git diff --stat "<base>..$END" # every changed file, for the scope check
37
+ git diff "<base>..$END" -- . ':(exclude)plans/<feature>/tasks/' # the content you review
38
+ ```
39
+
40
+ Read the files that diff touches. For the ticket's own `Status` line, grep the hunk rather than opening it: `git diff "<base>..$END" -- "$TICKET" | grep '^[+-]Status:'`.
41
+
42
+ (Substitute the repo's own ticket folder if it keeps them elsewhere.)
43
+
44
+ **Do not read `## Answer`.** It is the author's claim. Your job is to check the work without it. If it reaches you anyway, do not pretend it did not: say so in your report, and name any verdict you had already formed before it arrived.
45
+
46
+ ## What you must not do
47
+
48
+ Edit, write, stage, commit, stash, check out or reset anything. You may run read-only commands and the commands in `Done when`. If a command leaves files behind, say so in your report and leave them.
49
+
50
+ ## Verdict 1: does it meet the ticket?
51
+
52
+ For every bullet under **Done when**: run its command yourself, now, and write `MET` or `NOT MET` with what the command printed. A bullet you could not run is `NOT MET`.
53
+
54
+ Then scope: over `<base>..$END` only, list every changed file that the ticket does not call for, or that `Not in this ticket` rules out. Unrelated refactors, renames and reformatting count. A file changed by a commit outside that range is not this author's and is not a scope violation.
55
+
56
+ If the ticket says `Test first: yes`: check that the diff adds or changes a test, and say whether that test would fail without the production change.
57
+
58
+ ## Verdict 2: is it good?
59
+
60
+ Look for, in this order:
61
+
62
+ - bugs and missing handling at boundaries (input, files, network, empty and error cases)
63
+ - checks made weaker instead of being met: skipped or deleted tests, silenced linters, empty catches, lowered thresholds, tests that cannot fail
64
+ - code that need not exist: something the standard library, the platform or the codebase already provides; a feature nobody asked for; a longer way of doing a shorter thing
65
+ - anything a later ticket will trip over
66
+
67
+ Rate each finding `blocker`, `major` or `minor`, name the file, and say what to change.
68
+
69
+ ## Output
70
+
71
+ Use exactly this shape. Keep it short; no praise.
72
+
73
+ SPEC
74
+ - <Done when bullet>: MET | NOT MET - <command and what it printed>
75
+ - scope: OK | <files outside the ticket>
76
+ - test first: n/a | OK | MISSING
77
+
78
+ QUALITY
79
+ 1. blocker|major|minor <file>: <finding> - <what to change>
80
+
81
+ REVIEW: PASS
82
+
83
+ The indentation above only marks where the template starts and stops. Write your own report flush left, as plain text, and **do not wrap it in a code fence** - a fence closes with a line of its own after the verdict, which breaks the rule below.
84
+
85
+ The last line of your whole reply must be exactly `REVIEW: PASS` or `REVIEW: FAIL`, flush left: no closing fence, no trailing note, no sign-off, nothing after it. The flow (`scripts/flow.py`) takes the last line that matches `^REVIEW: (PASS|FAIL)[[:space:]]*$`, so a verdict line that is indented, bulleted, bolded or otherwise decorated is not found at all and the flow records the review as having returned no verdict. Anything you still want to say goes above the verdict line.
86
+
87
+ `PASS` only if every Done when bullet is `MET`, scope is `OK`, test first is not `MISSING`, and there is no `blocker` or `major` finding. `minor` findings are listed but do not fail the review. When in doubt, fail it and say why.
@@ -0,0 +1,30 @@
1
+ # Show the ticket graph
2
+
3
+ Show the ticket graph for the feature you were given. The word `watch` means the run is in progress and the page should keep updating.
4
+
5
+ If no feature was given, list `plans/*/` and ask which one.
6
+
7
+ ## Step 1: Write and open the page
8
+
9
+ Run `python3 scripts/flow-view.py <feature>`. Add `--watch` when the user said `watch`. It writes one HTML file, normally `.git/flow-<feature>.html`, and opens it in the default browser.
10
+
11
+ If the script is not installed, say so and point to `install.sh`. If no browser could be opened (a remote machine, for example), print the path and tell the user to open it. Set `FLOW_NO_OPEN=1` to never open one.
12
+
13
+ ## Step 2: Summarise it in words
14
+
15
+ Run `python3 scripts/flow-status.py <feature>` and `--counts`, then write a few lines:
16
+
17
+ - how many tickets are resolved, in progress, ready and waiting
18
+ - which tickets are **ready now**
19
+ - the **longest chain of unfinished tickets**: that is the critical path, and it decides how soon the feature can finish
20
+ - what is blocking the acceptance ticket, if one is still open
21
+ - any ticket that was sent back (the page marks it with a badge)
22
+
23
+ ## Step 3: Tell the user what the page does
24
+
25
+ - **Replay** plays the run back from git history: tickets turn green in the order they resolved, and a ticket sent back flashes red.
26
+ - Dashes flow along the edges into tickets that are ready to start. Ready tickets pulse, and a ticket being worked spins.
27
+ - Hover or click a ticket for its blockers, Done when, Answer, and the rounds it was sent back.
28
+ - **Now** returns to the current state. The Theme button switches light and dark. Space plays or pauses, the arrow keys step, `t` switches theme.
29
+
30
+ The page needs no server and no network. It is generated from the ticket files and the git history, so it is always read only.
@@ -0,0 +1,15 @@
1
+ # Commands: <feature>
2
+
3
+ The real commands for this project, read from its own config (package.json, Makefile,
4
+ Taskfile, CI). Approved by a human when the plan is approved. Workers read this file and
5
+ must not change it.
6
+
7
+ The flow runs Smoke before every ticket and Build, Test and Lint after every
8
+ ticket, so each must run without asking anything and exit non zero on failure. Delete a line
9
+ you do not have; a line left as a placeholder is skipped.
10
+
11
+ Build: `<command>`
12
+ Test: `<command>`
13
+ Lint: `<command>`
14
+ Smoke: `<the quickest command that proves the base is healthy, run before every ticket>`
15
+ Run: `<command, if the feature has something to start>`
@@ -0,0 +1,7 @@
1
+ # Learnings: <feature>
2
+
3
+ Things a fresh worker would otherwise waste time rediscovering: a command that works, a
4
+ trap, a quirk of the environment. Append one or two lines, newest last, tagged with your
5
+ ticket number. Never edit or delete another line. Keep it short; a human prunes it.
6
+
7
+ - (NN) <what you found, and what to do about it>
@@ -0,0 +1,26 @@
1
+ # Map: <feature>
2
+
3
+ ## Destination
4
+
5
+ <One paragraph: what exists when this is done.>
6
+
7
+ **The bar.** <The single measurable check that proves the whole feature: the command and the
8
+ output it must print. The last ticket runs it.>
9
+
10
+ ## How to work this
11
+
12
+ - See what is ready: `python3 scripts/flow-status.py <feature>`. Take the lowest numbered READY ticket.
13
+ - Claim: set `Status: claimed` in the ticket and save before starting.
14
+ - Resolve: write the result under `## Answer`, set `Status: resolved`, then add one line to
15
+ Decisions so far below.
16
+ - See the graph: `python3 scripts/flow-status.py <feature> --mermaid` (coloured by status, drawn on demand).
17
+ - Commands live in `commands.md` (frozen). Lessons live in `learnings.md` (append only).
18
+ - Status lives only in the ticket files. This map never repeats it.
19
+
20
+ ## Decisions so far
21
+
22
+ <One line per resolved ticket: `NN — the decision or finding, in a sentence`.>
23
+
24
+ ## Open questions
25
+
26
+ - <things not known yet that may change later tickets>
@@ -0,0 +1,62 @@
1
+ # <Feature> — Spec
2
+
3
+ Delete any section that does not apply. Do not write "N/A".
4
+
5
+ ## Problem
6
+
7
+ <1-2 paragraphs: who is affected and what hurts today.>
8
+
9
+ ## Goal and the bar
10
+
11
+ <What done looks like, and the one measurable check for the whole feature.>
12
+
13
+ ## Stories
14
+
15
+ ### <Story title>
16
+
17
+ **As a** <user>, **I want** <goal>, **so that** <benefit>.
18
+
19
+ - [ ] <acceptance criterion>
20
+ - [ ] <acceptance criterion>
21
+
22
+ ## Scope
23
+
24
+ In: <list>
25
+ Not in scope: <list, with where each one lives instead>
26
+
27
+ ## Happy path
28
+
29
+ 1. <user action> → <system response> → <what the user sees>
30
+ 2. ...
31
+
32
+ ## Edge cases
33
+
34
+ | Trigger | Expected behaviour | Handled in ticket |
35
+ |---|---|---|
36
+ | <what causes it> | <what should happen> | NN |
37
+
38
+ ## Design
39
+
40
+ <How it fits into the existing system: components, data flow. An ASCII diagram is welcome:
41
+ boxes `[ ]`, arrows `──►`, at most 70 columns, one arrow label per data or event passed.
42
+ Show the happy path and the main failure path. Show only parts this feature adds or changes.>
43
+
44
+ ### Decisions
45
+
46
+ - **<Decision>** — options: A, B, C. Chosen: A. Why: <reason>.
47
+
48
+ ## Interfaces
49
+
50
+ <New or changed types, endpoints, events or messages.>
51
+
52
+ ## Migration and compatibility
53
+
54
+ <Breaking changes and how existing data or callers are handled.>
55
+
56
+ ## Risks
57
+
58
+ - <risk> — <mitigation>
59
+
60
+ ## UI
61
+
62
+ <Only for features with screens. Use ui-mockup.md.>
@@ -0,0 +1,29 @@
1
+ # <One change, in the imperative: "Add the retry policy to the job runner">
2
+
3
+ Type: task
4
+ Status: open
5
+ Blocked by: —
6
+ Test first: yes
7
+
8
+ <What to do and why, in prose. Name files, functions and types. Never use line numbers.
9
+ For Type: settle, say what has to be decided and who decides. For Type: convert, say what
10
+ is being converted and what the safe default is for anything that does not match.>
11
+
12
+ ## Not in this ticket
13
+
14
+ - <the nearest thing a reader would assume is included, and where it lives instead>
15
+
16
+ ## Done when
17
+
18
+ - <an observable outcome, with the exact command and the result it should print>
19
+ - <every bullet is checkable by someone who has not read the rest of the plan>
20
+
21
+ ## Reference
22
+
23
+ - spec.md § <section>
24
+ - <files to read first; mark any that are read-only>
25
+
26
+ ## Answer
27
+
28
+ <left empty until the ticket is resolved. The worker fills in: Built, Proof, Decisions,
29
+ Shortcuts taken, Review fixes (only after a review), For later tickets.>