pi-dev-flow 0.0.1-stage → 0.52.1

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Dima Marchevskyi
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1 +1,189 @@
1
- Placeholder version. Install the real one: `pi install npm:pi-dev-flow`.
1
+ # dev-flow
2
+
3
+ ![dev-flow stages: plan, implement, pre-release, release, done — with the four operator checkpoints and the tag that publishes to npm](banner.png)
4
+
5
+ An enforced feature workflow for [Pi](https://pi.dev), shipped as a pi package. You say what you
6
+ want; an agent builds it; **the harness — not the agent's good manners — decides whether a stage
7
+ may advance.** One feature at a time, five stages, and four questions asked of you —
8
+ `plan → implement → pre-release → release → done`, mapped out under [How you work it](#how-you-work-it).
9
+
10
+ ## The idea
11
+
12
+ **Every feature starts as a spec.** `flow start <slug>` opens a feature: a folder under
13
+ `.dev-flow/<slug>/` where the agent researches and writes a plan — problem, approach, files to
14
+ touch, test strategy, risks — plus rendered screenshots for anything user-visible. Until you approve
15
+ it, the agent cannot edit code anywhere else in the repo. That is the whole "does it actually plan?"
16
+ question, answered by the harness instead of by trust.
17
+
18
+ **Stages end in gates, not vibes.** Each stage has one exit gate that refuses until its criteria
19
+ hold: your plan file exists; every command your playbook names as a check passes; the app really
20
+ starts, with real data, and answers a health probe **after** a real deploy; changelog and version
21
+ manifest agree and the version is ahead of the last tag; the release is tagged and on the remote.
22
+ Gates read state that the agent is blocked from writing, so a step cannot be quietly skipped — and
23
+ each gate asks you exactly **one** question.
24
+
25
+ The flow itself ships with the plugin; a project carries only a `.dev-flow/` folder — state files,
26
+ plans and worktrees, nothing the plugin needs to run.
27
+
28
+ **Git stays clean by construction.** Nothing commits to the default branch while a feature is in
29
+ flight: work happens in its own git worktree and branch (`feat/<slug>`), so your checkout keeps
30
+ moving while a feature is under way. Publishing has one order — PR → merge → annotated tag → forge
31
+ release — and only in the release stage does dev-flow let git touch main again. A `pre-commit` hook
32
+ covers commits made outside Pi.
33
+
34
+ **Many features at once.** One Pi session = one feature = one worktree, and a repo can host as many
35
+ as you want in parallel. A write inside worktree X is judged by X's stage whoever made it, so
36
+ sessions cannot dirty each other's branches or borrow each other's approvals. [More here.](docs/concurrency.md)
37
+
38
+ **It learns from friction.** Every time the agent hits something that cost it a step — a playbook
39
+ command that no longer exists, a guard that refused the wrong thing, a gate that forced a detour — it
40
+ writes the observation down in `.dev-flow/reflections.md` instead of just working around it. Those
41
+ notes are advisory and never gate anything, but they are how your playbook stops drifting: you review
42
+ them with `flow doctor` and decide what to fix. [More here.](docs/playbook.md)
43
+
44
+ ## Install
45
+
46
+ ```bash
47
+ pi install npm:pi-dev-flow # a published release
48
+ pi install git:github.com/dmarchevsky/pi-dev-flow # or from git — pin a ref with @v0.49.1
49
+ pi install ./pi-dev-flow # or a local checkout
50
+ ```
51
+
52
+ Later releases arrive with `pi update --extensions` (an npm install reconciles to the newest version);
53
+ then `/reload` — a running session keeps the code it loaded at startup.
54
+
55
+ Then, once per project repo, `/flow init`. It reads the repo (default branch and forge from git,
56
+ package manager, test/build/run conventions, version manifest) and writes **`.dev-flow/project.md`
57
+ — your playbook**: the commands that count as checks, the manual checklist, the version and tag
58
+ convention, and any hard safety rules of your own. It asks about everything detection could not
59
+ decide (self-hosted forge? Docker artifacts? what "healthy" means?) and installs a `pre-commit` hook
60
+ that blocks direct main commits. Run `/flow init` again later and it becomes a **doctor**: drift in
61
+ your playbook, dead commands, config problems — it never rewrites your hand edits without asking.
62
+ The flow is completely inert in repos with no `.dev-flow/`.
63
+
64
+ → [The playbook](docs/playbook.md) — what goes in `project.md`, the three tiers, your own guard rules.
65
+
66
+ ## How you work it
67
+
68
+ You are not a command runner. You ask for a feature and then answer four words — say *"sessions
69
+ expire silently, add sliding renewal and a test"* and it becomes the feature `session-renewal`,
70
+ walking five stages with a gate between each:
71
+
72
+ ```
73
+ you ask for a feature
74
+ │
75
+ ┌────────▼────────┐
76
+ │ plan │ spec (+ mockups) in .dev-flow/<slug>/plan/
77
+ └────────┬────────┘
78
+ Approve? · refuses without the written plan (and renders for UI work)
79
+ ┌────────▼────────┐
80
+ │ implement │ code + tests, only inside the feature worktree
81
+ └────────┬────────┘
82
+ Verified? · refuses until checks are green and the deployed app is healthy
83
+ ┌────────▼────────┐
84
+ │ pre-release │ review, docs, changelog entry, version bump
85
+ └────────┬────────┘
86
+ Release? · refuses unless the version passes the last tag and the changelog agrees
87
+ ┌────────▼────────┐
88
+ │ release │ PR → merge → annotated tag → release notes
89
+ └────────┬────────┘
90
+ Finish? · refuses until the release is recorded and main is clean and pushed
91
+ ┌────────▼────────┐
92
+ │ done │ worktree, branch and feature folder removed
93
+ └─────────────────┘
94
+ ```
95
+
96
+ | Stage | What the agent does | It asks you |
97
+ |---|---|---|
98
+ | **plan** | reads the code, asks its clarifying questions, writes the spec to `.dev-flow/session-renewal/plan/plan.md` | **Approve?** — the plan is right, build it |
99
+ | **implement** | works in a fresh worktree on `feat/session-renewal`, commits in small steps, runs your `test` command to green, walks the manual checks with you | **Verified?** — after you used it running |
100
+ | **pre-release** | review and fixes, docs, changelog, version bump, checks re-run; shows the PR summary and the version it means to tag | **Release?** — ship it |
101
+ | **release** | PR → merge → annotated tag → release notes; only here may git touch main | **Finish?** — clean up, we're done |
102
+ | **done** | removes the worktree, branch and `.dev-flow/session-renewal/` | — |
103
+
104
+ Four answers from you; everything between them was the agent's problem and your playbook's gates. If
105
+ the feature grows mid-flight it is folded into this one (the plan gains an `Added mid-stage:` line and
106
+ checks re-run); a *different* problem waits for the next flow — one feature, one plan, one PR.
107
+
108
+ Your answers at a checkpoint:
109
+
110
+ - **yes** — or **no**, with the reason. The agent fixes it and asks again; that loop is free.
111
+ - **yolo** — answers this checkpoint *and* pre-confirms the rest, so the remaining gates run back to
112
+ back with every check still enforced. Attach your wishes to the word: *"yolo, patch bump"*,
113
+ *"yolo, no tag"*.
114
+ - **no version this time** (at Release?) — merge and changelog land, no bump or tag; the next
115
+ versioned release folds your `Unreleased` entry under its heading.
116
+
117
+ Two things you grant rather than run: a **mode** when the agent proposes one (`normal` is the
118
+ default; `--patch` for a small change you intend to ship now, `--docs` for docs-only work — both skip
119
+ the written plan), and a **`/reload`** whenever dev-flow itself was updated — a running session keeps
120
+ the code it loaded at startup, and `flow version` tells you when it is behind. Updating or installing
121
+ the plugin, and running git outside Pi when one of your `confirm` rules fires with no UI to ask you,
122
+ are the only things dev-flow cannot do for you.
123
+
124
+ Nothing here trusts the agent in either direction: a checkpoint gate refuses if you never answered
125
+ (its refusal begins `NEEDS-OPERATOR Approve?`), and the agent is told never to ask *you* to type a
126
+ `flow` command. `/flow …` is your manual override — for when the session is stuck, wrong, or
127
+ claiming a gate result it never produced. → [Gate reference](docs/gates.md)
128
+
129
+ ### Reflections: the agent's notes to you
130
+
131
+ When something costs the agent a step — a playbook command that stopped existing, a guard that
132
+ refused the wrong thing, a gate that forced a detour, a rough edge in dev-flow itself — it does not
133
+ just work around it. It parks a one-line note in `.dev-flow/reflections.md`, under **playbook**
134
+ (something about your project) or **plugin** (something about dev-flow); the open count surfaces in
135
+ `flow status` and in the agent's stage context (`↻ 2 reflection(s) open`).
136
+
137
+ They never gate anything, and the fix is never the agent's call alone. You ask it to go through them
138
+ — usually with `flow doctor`, which reviews each open item and routes it: a playbook item becomes a
139
+ small docs flow that edits `project.md` through the flow (so the change is committed and reviewed like
140
+ any other), a dev-flow item goes upstream as an issue. At the end of a session the agent does the same
141
+ review over the whole exchange before `Finish?`, so friction it noticed but never filed shows up too.
142
+ Fold, fix or drop them — but leaving them is the one option that lets the same friction come back next
143
+ feature.
144
+
145
+ ## What is enforced
146
+
147
+ While a feature is active, every tool call passes a guard:
148
+
149
+ - no commit, merge or push to the default branch outside the release stage (plus a git hook for work outside Pi);
150
+ - at plan stage the agent can only write inside its own feature folder;
151
+ - at implement/pre-release it can only write inside its own worktree;
152
+ - with a `.dev-flow/` present, an unbound session cannot edit source at all — everything goes through the flow;
153
+ - `.dev-flow/*/state.json` is unwritable by the agent, so gates cannot be skipped or forged;
154
+ - `project.md` is yours: the agent cannot write it (except inside a docs/patch flow), and every
155
+ sanctioned overwrite keeps a `.bak`;
156
+ - your own `deny` / `confirm` / `protect` / `forbid` rules run on every command and path, ahead of
157
+ dev-flow's verdicts, with the reason you wrote;
158
+ - a shell call must state its own timeout, and a bash command named `flow` is refused (there is no
159
+ CLI — gates live in the session).
160
+
161
+ → [Guardrails](docs/guardrails.md) — the full list, and [what this is *not*](docs/guardrails.md#limitations-deliberate-v1).
162
+
163
+ ## Where things live
164
+
165
+ ```
166
+ .dev-flow/
167
+ project.md your playbook — the only committed file here
168
+ reflections.md the agent's suggestion queue for you (advisory)
169
+ roadmap.md your backlog of future features (advisory)
170
+ <slug>/
171
+ plan/plan.md the spec you approve (embeds the mockup renders)
172
+ plan/mockups/ mockup HTML + screenshots ← UI features
173
+ worktree/ the feature's git worktree ← all the work
174
+ state.json stage, approvals, checks (agent cannot write)
175
+ ```
176
+
177
+ `roadmap.md` is your backlog of future features (`flow roadmap add / pick / done`), also advisory — a
178
+ shipped feature checks off the item it was picked from.
179
+
180
+ ## Documentation
181
+
182
+ | Doc | For |
183
+ |---|---|
184
+ | [docs/playbook.md](docs/playbook.md) | writing `project.md`: checks, checklist, prose, guard rules, `flow init`/`doctor` |
185
+ | [docs/gates.md](docs/gates.md) | every gate and flag, what makes each refuse |
186
+ | [docs/guardrails.md](docs/guardrails.md) | everything enforced, and the deliberate limits |
187
+ | [docs/concurrency.md](docs/concurrency.md) | several agents, one repo: bindings, claims, attribution |
188
+ | [docs/headless.md](docs/headless.md) | driving a session from a bot/UI/queue: `NEEDS-OPERATOR`, `--json` surfaces |
189
+ | [docs/development.md](docs/development.md) | hacking on dev-flow itself: tests, self-hosting, `/reload` |
package/banner.png ADDED
Binary file
package/banner.svg ADDED
@@ -0,0 +1,52 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="1280" height="640" viewBox="0 0 1280 640" font-family="ui-monospace, SFMono-Regular, Menlo, monospace">
2
+ <rect width="1280" height="640" rx="18" fill="#0f1216"/>
3
+ <text x="60" y="112" fill="#e6edf3" font-size="46" font-weight="700">dev-flow</text>
4
+ <text x="60" y="156" fill="#8b98a9" font-size="21">enforced feature workflow for pi — one flow, one branch, one worktree per feature</text>
5
+
6
+ <!-- gates (checkpoint words the operator answers in chat) -->
7
+ <g fill="none" stroke="#f0b429" stroke-width="2">
8
+ <path d="M 260 236 l 14 14 l -14 14 l -14 -14 z"/>
9
+ <path d="M 500 236 l 14 14 l -14 14 l -14 -14 z"/>
10
+ <path d="M 740 236 l 14 14 l -14 14 l -14 -14 z"/>
11
+ <path d="M 980 236 l 14 14 l -14 14 l -14 -14 z"/>
12
+ </g>
13
+ <g fill="#f0b429" font-size="17" text-anchor="middle">
14
+ <text x="260" y="212">Approve?</text>
15
+ <text x="500" y="212">Verified?</text>
16
+ <text x="740" y="212">Release?</text>
17
+ <text x="980" y="212">Finish?</text>
18
+ </g>
19
+
20
+ <!-- stages -->
21
+ <g font-size="19" text-anchor="middle">
22
+ <rect x="60" y="270" width="200" height="62" rx="10" fill="#161b22" stroke="#39424e"/>
23
+ <text x="160" y="308" fill="#e6edf3">plan</text>
24
+ <rect x="300" y="270" width="200" height="62" rx="10" fill="#161b22" stroke="#39424e"/>
25
+ <text x="400" y="308" fill="#e6edf3">implement</text>
26
+ <rect x="540" y="270" width="200" height="62" rx="10" fill="#161b22" stroke="#39424e"/>
27
+ <text x="640" y="308" fill="#e6edf3">pre-release</text>
28
+ <rect x="780" y="270" width="200" height="62" rx="10" fill="#12261f" stroke="#3fb950"/>
29
+ <text x="880" y="308" fill="#7ee2a3">release</text>
30
+ <rect x="1020" y="270" width="200" height="62" rx="10" fill="#161b22" stroke="#39424e"/>
31
+ <text x="1120" y="308" fill="#e6edf3">done</text>
32
+ </g>
33
+ <g stroke="#4b5563" stroke-width="2" fill="#4b5563">
34
+ <line x1="260" y1="301" x2="296" y2="301"/><path d="M 296 301 l -8 -5 v 10 z"/>
35
+ <line x1="500" y1="301" x2="536" y2="301"/><path d="M 536 301 l -8 -5 v 10 z"/>
36
+ <line x1="740" y1="301" x2="776" y2="301"/><path d="M 776 301 l -8 -5 v 10 z"/>
37
+ <line x1="980" y1="301" x2="1016" y2="301"/><path d="M 1016 301 l -8 -5 v 10 z"/>
38
+ </g>
39
+
40
+ <text x="60" y="404" fill="#8b98a9" font-size="18">each gate refuses until its exit criteria hold — plan, checks, deploy, changelog, manifest, tag</text>
41
+
42
+ <!-- publishing hangs off the release stage: where it goes is the project's playbook, not dev-flow's -->
43
+ <g font-size="17">
44
+ <line x1="880" y1="332" x2="880" y2="444" stroke="#3fb950" stroke-width="2"/>
45
+ <path d="M 880 444 l -5 -8 h 10 z" fill="#3fb950"/>
46
+ <rect x="620" y="452" width="600" height="82" rx="10" fill="#12261f" stroke="#3fb950"/>
47
+ <text x="920" y="484" fill="#7ee2a3" text-anchor="middle">the release ships: registry · image · release page</text>
48
+ <text x="920" y="512" fill="#8b98a9" text-anchor="middle" font-size="15">release-done refuses to close while the artifact is missing</text>
49
+ </g>
50
+
51
+ <text x="60" y="576" fill="#5b6672" font-size="16">.dev-flow/&lt;slug&gt;/ — plan, state, worktree · guards keep edits inside the feature</text>
52
+ </svg>
@@ -0,0 +1,18 @@
1
+ # Concurrency: one session, one feature
2
+
3
+ How several agents work one repo at the same time without stepping on each other.
4
+
5
+ **One pi session = one feature** (flow + branch + worktree). A repo can host any number of
6
+ features, worked by separate sessions in parallel — that is the supported shape, not an edge
7
+ case. The binding lives with the session (persisted via session entries, restored on
8
+ resume/fork), so nothing is repo-global. One unfinished feature binds automatically and the
9
+ single-session case needs no ceremony — unless a **live** session already works that feature:
10
+ every bind leaves a claim (`claim.json`, session + pid) and auto-bind refuses foreign features,
11
+ so a second session must `flow use <slug>` to take over — and the gates honor the claim too:
12
+ passing another session's slug to `flow approve`/`verify`/`finish` is refused until you take over,
13
+ or two sessions quietly overwrite one state file. `flow use <slug>` switches, and ambiguous sessions must
14
+ bind or pass explicit slugs. With nothing unfinished and no binding, the session must start a
15
+ flow — work does not happen outside the flow.
16
+ Enforcement is by attribution: a write inside worktree X is judged
17
+ by X's own feature stage no matter which session touches it; main-checkout code stays closed
18
+ while your bound feature is in `plan`/`implement` and while any feature is unbound-and-ambiguous.
@@ -0,0 +1,37 @@
1
+ # Hacking on dev-flow
2
+
3
+ This checkout dogfoods itself — the repo is developed with dev-flow.
4
+
5
+ ```bash
6
+ npm i && npm test # unit tests + wiring smoke test (node --test)
7
+ pi -e . run /flow status # try it against any repo
8
+ ```
9
+
10
+ This checkout dogfoods itself: `.pi/extensions/dev-flow.ts` is a committed one-line re-export of
11
+ `extensions/dev-flow.ts`, so pi loads the working tree rather than an installed copy, and a fresh
12
+ clone needs no setup. It is a pointer, not a copy — after any merge or pull, new sessions here run
13
+ main's current code, with nothing to re-run. A symlink would not work: pi resolves a module's
14
+ relative imports from the link's own directory, so the extension's `../src/core.ts` would not
15
+ resolve. `flow doctor` flags a missing or edited shim.
16
+
17
+ A release cannot update a session that is already running — pi keeps the code it loaded at startup.
18
+ After merging and tagging, `/reload` in every session that must carry the new behavior.
19
+
20
+ ## Publishing
21
+
22
+ `pi-dev-flow` is published to npm by `.github/workflows/release.yml`, triggered by the `v*` tag the
23
+ release stage pushes: it checks out that tag, runs `npm ci && npm test`, and `npm publish --provenance`
24
+ from exactly that commit. Nothing publishes from a developer machine.
25
+
26
+ Auth is npm **trusted publishing** — the workflow's OIDC identity, no token in the repo. The publisher
27
+ registered on npmjs.com names this repo **and the workflow file name**, so after renaming either one,
28
+ re-register it there or the next publish is rejected. And the publisher must be registered on a package
29
+ that already exists: npm has no place to put the credential before the first publish.
30
+
31
+ `--provenance` requires a **public** source repository — npm refuses the attestation from a private one
32
+ (`422 … Unsupported GitHub Actions source repository visibility: "private"`), which is why this repo is
33
+ public. If it ever goes private, drop the flag rather than the release.
34
+
35
+ If the job fails, fix the cause and **Re-run jobs** on the same failed run: the tag already points at what
36
+ should ship, so no re-tag. `flow release-done` then runs `npm view pi-dev-flow@<version> version` and
37
+ refuses to close the flow while the version is missing — which also catches a tag that never published.
package/docs/gates.md ADDED
@@ -0,0 +1,29 @@
1
+ # Gate reference
2
+
3
+ Every gate, what it advances, and what makes it refuse.
4
+
5
+ You normally type none of this: the agent runs the gates and you answer words in chat. `/flow <sub>` is
6
+ your **manual surface** — read state when the agent is confused, or advance a gate yourself when it is
7
+ stuck or claims a result it never ran. Same gates either way; an interactive run asks you in a dialog,
8
+ so it needs no `--confirmed`.
9
+
10
+ | Gate | Advances | Refuses unless |
11
+ |---|---|---|
12
+ | `start <slug> [--ui] [--patch|--docs --confirmed]` | → plan | slug is new (binds the session); relaxed modes are operator-granted in chat, anchored by `--confirmed`: `--patch` = chore release, no plan document, every gate unchanged; `--docs` = docs-only, no plan document and no tier-1 checks (worktree and release intact). Start prints the dev-flow build and stamps it into the feature state (`flow status` shows `flow=<ver>`) |
13
+ | `use <slug>` | — (binds this session to a feature) | slug unfinished |
14
+ | `approve [--yolo]` | plan → implement (creates worktree + branch) | `plan/*.md` exists; if `--ui`, rendered mockup screenshots (per `mockupGlobs`, default `*.png`) exist under `plan/` — headless chromium renders embedded in plan.md; **one question** — or `--confirmed` after you answered in chat; **yolo** (your chat answer at **any** checkpoint question) → run that gate with `--yolo --confirmed` (`approve`/`verify`/`release`/`finish`): that question is answered and every later one (Verified?/Release?/Finish?) is pre-confirmed — the agent then runs the remaining gates back to back, honoring guidance attached to the yolo word (e.g. "yolo, create a patch version release") — checks and guardrails stay fully enforced. `commands.setup` runs in the worktree and is retried until it succeeds once (a worktree left behind by a failed setup is set up on the next `approve`, never silently reused); a `--docs` feature runs no project command at all and says `setup skipped (docs mode)` |
15
+ | `test` | records a check | — (runs the `test` command/check, records pass/fail) |
16
+ | `check` | records all tier-1 checks | — (runs every `flow-check`/test/health from project.md `## implement`) |
17
+ | `health` | records a check | — (runs `seedData` + the health command, retrying up to `healthWait` seconds; app must be up locally with real data) |
18
+ | `verify [--yolo]` | implement → pre-release | all tier-1 checks green — a check with no passing record is **run by the gate itself** (`checks ran at the gate (test)`) rather than refused and pointed at `flow check`, since entering the stage or editing the worktree is what cleared it, and a failure there prints the run output, **the feature branch holds at least one commit** (verify refuses a branch with nothing committed — the checks describe the working tree, so an uncommitted state has never been recorded — and prints `commits: N ahead of <default>, M uncommitted path(s)` with what it ran; uncommitted paths are reported, never gated), build (if configured) green, `dev` deploy (if configured) runs first so you can review the running app **and the health probe (if configured) re-runs against what it just deployed, retrying every 2s up to `healthWait` seconds** (a failing probe refuses the gate and reports how many attempts it took: the `flow check` PASS predates the deploy, and a service that needs seconds after its container starts is not a broken deploy; with no probe the output says `health: none configured`), tier-2 checklist (ui-only items excluded for non-UI features) confirmed as a set; a `--docs` feature runs none of it (`docs mode: project commands skipped`) and only asks you; **one question** — or `--confirmed`; `--yolo --confirmed` = you answered 'yolo' at Verified? — answers it and pre-confirms the rest |
19
+ | `release [--yolo|--no-version]` | pre-release → release | checks re-greened after the prep edits; with `versioning.file`: the manifest version parses and is **not tagged yet** (the bump must be ahead of the last release); with `release.changelog`: the file exists, mentions the version and its `Unreleased` section holds no leftover entries (a versioned release **folds** what `--no-version` flows left there under the new version heading); tier-2 `## pre-release` checklist confirmed; the manifest version it validates is **stamped as the release's version of record** (`release-done` publishes that, whatever the checkouts say later); **one question** (Release?) — or `--confirmed`; `--no-version` (your call at Release?) ships this flow as **merge + changelog** — the bump/tag requirements and the tag checks at `release-done` are skipped for it, but the branch must still change the changelog (your entry rides under `Unreleased` until the next versioned release folds it) (default: versioned) |
20
+ | `release-done` | records the release (the stage stays `release`) | **no question — the Release? answer covered the publication**; verify approval; the version published is the one `release` **stamped** from the manifest it validated (a main checkout still behind cannot republish the previous version), and it must not be older than the newest matching tag **on origin** (tag lists come from `git ls-remote`, so an unfetched clone does not decide what exists; when an origin exists but cannot be asked (offline, no ssh agent) **both release gates refuse** rather than answering with local tags); with `versioning.file`: that tag exists, is reachable from the default branch **and is on origin** (a local-only tag is not a release); with `release.artifact`: the published-artifact check runs (`{version}`/`{tag}` placeholders — the line it prints names the version it verified; skip once with `--no-artifact-check`, operator-instructed only) · creates the forge release from the tag, its body the tag's annotation, else the `release.changelog` entry for that version, else nothing (and it says why — a body that is really a shell error is dropped, never shipped) — `gh` for GitHub, `glab` for GitLab) and then **proves the release exists**: it asks the forge (`gh release view <tag>` / `glab release view <tag>`) and refuses when nothing is there, because #51, #60 and #63 were three ways of ending `release recorded as complete` with nothing published. Skipping is deliberate and loud — `--no-release-create`, `release.releases = false` or `forge = self` each print that no forge release was created, and the view check is skipped for them |
21
+ | `finish [--yolo] [slug]` | release → done (removes worktree + branch, deletes `.dev-flow/<slug>/`, and deletes `origin/<branch>` **only** when its merged state is verified — ancestor of the default branch or a merged PR/MR via `gh`/`glab`; unverifiable branches stay with a note) | a release is recorded (`release-done` ran), the main checkout is clean **and** pushed to `origin/<default>`, and the worktree has no uncommitted changes; **one question** (Finish? — mentions unmerged-looking squash-merge commits, and warns when the forge release create failed at `release-done` and no retry succeeded) — or `--confirmed`; to abandon an unreleased feature use `abort` |
22
+ | `reflect ["<note>"]` | — | `reflect "<note>"` queues a suggestion in `reflections.md` (never gates; gate flags such as `--confirmed` are ignored, they never become part of the note). Bare `reflect` prints the full **session-reflection protocol** for the agent (from the `/flow reflect` bar it additionally wakes the model, so the protocol executes immediately): review the whole session log, re-check every open item against the current playbook and **newer dev-flow versions** (`npm view pi-dev-flow version` vs the running version — dev-flow ships from npm; recommend `pi install npm:pi-dev-flow` + `/reload`, the operator performs updates), dedupe new findings against the open ones, then report playbook vs plugin items to the operator and ask whether to fix now — plugin items: search upstream issues first (`gh issue list --repo <upstream> --state all --search …`; an author-addressed match is reported back, no match → `gh issue create` with a detailed description and a suggested fix), playbook items: `flow doctor` routes each to the docs flow that implements it |
23
+ | `roadmap [add "<feature>" \| pick <n> <slug> \| done <n> \| tidy]` | — | manages `.dev-flow/roadmap.md`, the gitignored backlog of future features (`- [ ]` checklist): bare prints the numbered open items, `add` appends one, `pick <n> <slug>` stamps open item n with `(slug)` so `flow start <slug>` can take it — **`finish` checks the stamped item off when that feature ships**; `done <n>` checks off by hand (items shipped outside a flow), `tidy` drops the checked lines. Never gates; the open count is surfaced in `status` and session context |
24
+ | `doctor ["<issue note>"]` | — | read-only findings: playbook drift, dead commands/scripts, untracked playbook, ghost feature dirs, orphan worktrees/branches — **plus a review of every open reflection, each routed to the flow that should fix it** (playbook item → docs flow; plugin item → upstream issue, or a patch/normal flow inside the dev-flow repo). It writes nothing and asks nothing, safe for agents and CI at any stage; the agent investigates, you confirm, the routed flow implements. `doctor "<note>"` prints an investigation brief and a fix route for a fresh observation |
25
+ | `status [slug]` / `abort [slug]` | — | — |
26
+ | `help` (`--help`, `-h`) | — | — (prints the gate help — answerable in any repo, at any stage, with no `.dev-flow/`) |
27
+ | `version` | — | — (prints dev-flow's **own** version and the directory it was loaded from — which copy this session runs, this checkout vs an installed `pi-dev-flow`; when the checkout has moved past the build this session loaded it says so and names both, because the gates execute the loaded code — `/reload` to run the newer one; answerable in any repo, at any stage, with no `.dev-flow/`) |
28
+
29
+ `--confirmed` is the operator-presence flag: pass it only after the operator literally answered the stage question in chat — one checkpoint per stage, no second dialog.
@@ -0,0 +1,27 @@
1
+ # Guardrails (the teeth)
2
+
3
+ Everything dev-flow enforces while a feature is in flight, and what it deliberately does not do. Short version: [README → What is enforced](../README.md#what-is-enforced).
4
+
5
+ While a feature is active, the extension intercepts every tool call:
6
+
7
+ - **Nothing on main, ever** (until release): `git commit` on the default branch is blocked at every stage except `release` — where release chores commit in the main checkout (prefixed `DEV_FLOW_ALLOW_MAIN=1` for the hook) — and except one chore that needs no flow: a commit whose **only** change in the checkout is `.dev-flow/project.md`, because tracking the operator's playbook is setup, not feature work (any other dirty path closes that lane). pushes are judged by the ref they write — only pushing *to* the default branch (or a bare push while on it) requires stage `release`, so tag pushes and feature-branch pushes pass from any stage; `git merge` onto the default branch also requires stage `release`. Plus the standalone pre-commit hook for commits made outside Pi.
8
+ - **No code before an approved plan**: at stage `plan`, edits are confined to `.dev-flow/<slug>/` — the session's **own** feature directory, so one session cannot write another feature's plan and have its gate rubber-stamp what it never wrote.
9
+ - **Work stays in the worktree**: at stages `implement` and `pre-release`, edits are confined to that feature's worktree — judged per-path by the owning feature, so one session can never write into another session's worktree or into the main checkout.
10
+ - **Flow by default**: in a repo with `.dev-flow/`, an unbound session cannot touch source files at all — every request goes through `flow start`/`flow use`; there is no bypass. `flow start` refuses when the main checkout isn't on the default branch, worktrees are always created from the default branch, and the agent cannot check feature branches out in the main checkout.
11
+ - **Gates can't be skipped**: flow state is untouchable by the agent (edit tool and bash) — writing `state.json`, moving it away, or deleting a feature directory or the whole `.dev-flow/` folder all block; the `project.md` playbook is operator-owned — the main-checkout copy is blocked for the agent outright, and only a **docs or patch flow's** worktree copy may be edited (normal features route playbook changes to their own flow); every sanctioned playbook overwrite keeps the previous file as `project.md.bak`; editing worktree code invalidates a passing test check until `flow test` reruns.
12
+ - **Your project's own rules**: every `deny`/`confirm`/`protect`/`forbid` rule from the `flow-guard` block in project.md `## global` runs on every bash command and every edit/write path — in every stage and binding, ahead of dev-flow's own verdicts. A block always carries the reason you wrote.
13
+ - **A forced push asks the operator**: `git push` carrying `--force`, `--force-with-lease…` or `-f` is confirmed before it runs — a rewritten branch is history CI and other sessions already have. The one spelling that answers for it is the prefix the refusal prints (`DEV_FLOW_ALLOW_REWRITE=1 git push -f …`), the same "the operator said so" contract as `DEV_FLOW_ALLOW_MAIN`. Local history rewrites (`git filter-branch --force`) are not pushes and stay free, so a project never needs a blunt flag-only deny of its own.
14
+ - **Shell calls can't hang forever**: pi kills a shell command only when the model passed a `timeout` — it omits one in ~75% of calls, so a `gh` waiting on a prompt or a dead `curl` froze sessions for hours. By default the call is therefore **refused** until the model states one (`bashTimeout = require`): the refusal names the exact value the class table would have armed, so the retry is a one-token fix and the call line in the transcript shows the timeout that is actually running. The injected class default (30s quick inspection · 120s local git and unknown · 180s network · 300s containers/services · 900s builds/tests/installs · 1,200s watchers like `gh run watch`) stays armed underneath either way, and absurd explicit values clamp to 1,800s. A project that would rather not nag sets `bashTimeout = inject` in `## global` — then the default is armed silently and the call passes. It applies to every session that loads the package, flow-bound or not (a session with no playbook has no way to opt out).
15
+ - **Compact-aware**: dev-flow paces compaction the way pi counts it: the self-compaction trigger uses the *effective* `reserveTokens` (settings and per-model overrides, not the built-in default) and is checked at every turn end, not only when a run settles. When pi force-compacts on its threshold anyway — the chat-only states (open tier-2 dialog, plan discussion not yet in `plan/`) refuse to self-compact by design — dev-flow takes the compaction over and redoes it with flow-aware summary instructions (overflow recovery keeps pi's compact-and-retry and is left alone). Auto-compaction turned off in settings is honored; a note about any compaction dev-flow could not shape still lands in `reflections.md`.
16
+ - The active stage is injected into the model's context every turn; the bundled `dev-flow` skill is the agent's playbook, including the recommended sequences: pre-release — review (`/ponytail-review`) → fixes → docs → changelog/version bump; release — PR → merge → tag → `release-done`.
17
+ - **Status bar**: while a feature is active the pi footer shows `<stage glyph> <slug>` plus mode markers — stages 📝 plan, 🔨 implement, 🔍 pre-release, 🚀 release; markers ⚡ patch, 📚 docs, 🎱 yolo, and `⚠️` while a check fails (emoji render in their own color, so the footer carries no theme coloring). `flow status` is the spelled-out surface: `stage=implement`, the per-feature mode tags (`[ui]`, `[yolo]`, `[no-version]`) and the `flow=<ver>` stamp of the build that started the feature.
18
+
19
+ ## Limitations (deliberate, v1)
20
+
21
+ - Production deployment stays manual by design (phase 2 automation); `flow finish` only closes the flow.
22
+ - Bash git-guard parses shell segments to find real `git` invocations (honoring `cd … &&`, `git -C`, full paths) and judges them by the branch of the directory the command runs in — the feature worktree when you work there. A `~` / `$HOME` in that directory is expanded the way the shell would (any other `$VAR` stays literal); when the directory cannot be answered for, no branch is invented — the guard stays quiet and git itself fails. A commit refusal names the directory it judged, so a wrong verdict is readable as one. Splitting respects quotes, so text inside `"…"` / `'…'` is an argument's prose, never a command: `gh pr create --body "…git commit…"` is not a commit. A wrapper that execs its argument (`sh`, `bash`, `node`, `python`, `env`, `xargs`, …) is judged on its payload — `sh -c "git push origin main"` is a push. An exotic wrapper (subshell, variable) could still slip past; the `.git` hook covers plain commits outside Pi too, but neither is a security sandbox.
23
+ - `flow-guard` and the git guard are guardrails for what an agent types, **not a parser and not a sandbox**. Doing-verbs an agent types in the clear are caught; quoted prose is deliberately read as data, so a wrapper deciding to run something at runtime is out of reach. Write rules for the common mistakes, keep validation you actually depend on in scripts and CI.
24
+ - `protect`/`forbid` judge edit/write tools against the repo root the session runs in — a copy of a protected file inside a feature worktree is a different path, and a bash redirect (`echo k > docker/secrets/k`) is a `deny` rule's job. A glob in the **last** segment matches the siblings of that directory (`protect docker/.env*` covers `.env` and `.env.bak-20261021`); a glob anywhere else names one literal path, so `flow init` warns that the rule guards nothing.
25
+ - The read-only heads that get skipped (`find`, `awk`, `jq`, …) can write in their own dialects — `find . -delete`, `awk … > file`. A deny rule on `rm` does not cover them; write a rule for the effect you actually need to prevent.
26
+ - Gate commands (`test`, `check`, `verify` build, `health`) run inside the feature worktree once it exists — the code under test lives there; before the worktree exists they run in the main checkout.
27
+ - A bash segment whose first word is `flow` is always refused, with no per-project opt-out: dev-flow assumes the name is free because there is no `flow` CLI. A project that really ships a binary called `flow` calls it by path (`/usr/bin/flow`, `./node_modules/.bin/flow`) or through `npx` — the rule matches the head token only, so those pass.
@@ -0,0 +1,42 @@
1
+ # Driving dev-flow from an orchestrator (headless)
2
+
3
+ For whoever puts a Pi session behind a bot, a web UI or a queue. The interactive story is in [the README](../README.md#how-you-work-it).
4
+
5
+ There is **no `flow` CLI** — gates exist only inside a Pi session (the `flow` tool, `/flow`
6
+ command). A remote orchestrator therefore drives a **Pi session** (e.g. via the Pi SDK) and
7
+ calls the `flow` tool from inside it. That is not a limitation: the guards are harness-side,
8
+ so a driver that shelled around Pi would bypass every teeth except the `pre-commit` hook.
9
+
10
+ The contract a headless driver (a session with no UI) relies on:
11
+
12
+ - **Refuse → relay → `--confirmed`.** A checkpoint gate (`approve`, `verify`, `release`,
13
+ `finish`) never dialogs headless — it refuses with a line that starts with the stable
14
+ marker `NEEDS-OPERATOR <Question?> — …` (the four questions: Approve? / Verified? /
15
+ Release? / Finish?). Route on the marker, relay the question verbatim to the human, and on
16
+ their literal answer re-run the same gate with `--confirmed`. A project `flow-guard`
17
+ `confirm` rule with no UI present refuses with the same marker (no question word). The
18
+ marker is also on top of prose that stays readable for humans — nothing to parse beyond
19
+ the first token.
20
+ - **`--confirmed` is honor-system.** The plugin cannot verify a human answered; the flag
21
+ means "the operator literally answered this question in chat". In remote setups the
22
+ orchestrator owns that trust — relaying the exact question and adding `--confirmed` on its
23
+ own inference is the one orchestrator bug dev-flow cannot catch. `--patch`/`--docs` mode
24
+ grants work the same way (operator grant in chat, then `--confirmed`).
25
+ - **Machine surfaces.** `flow status --json` (bound slug + per-feature stage, mode flags,
26
+ branch, worktree, checks, next hint — no git subprocesses) and `flow help --json` (the
27
+ gate contract as data, incl. the checkpoint questions and flag names). Both are additive:
28
+ the prose views are unchanged. Read-only-safe at any stage/binding, even without
29
+ `.dev-flow/`: `status`, `help`, `version`, `doctor`, `roadmap` (list).
30
+ - **One session = one feature.** Bindings are per-session and survive resume/fork; a
31
+ reconnecting driver either rejoins the same session or rebinds with `flow use <slug>`.
32
+ Auto-bind fires only for a single unclaimed unfinished feature — with several, always pass
33
+ the slug explicitly. Parallel features = parallel sessions.
34
+ - **Budget for long gates.** `approve` runs `commands.setup`; `verify` runs build and the
35
+ `dev` deploy (runs are killed past 15 min) and re-probes health with `healthWait` retries.
36
+ A remote timeout must be minutes, not seconds — the gate prints progress lines while it
37
+ runs.
38
+ - **Edits invalidate checks.** Any worktree write clears recorded checks and the tier-2
39
+ confirmation — re-run `flow check` before `verify` after the last edit.
40
+ - **Version skew.** Gates execute the dev-flow build the session loaded at start; `flow version`
41
+ reports when the checkout has moved ahead of it (and `release-done` stamps a warning into
42
+ its output when the session shipped from behind) — reload the session before publishing.
@@ -0,0 +1,141 @@
1
+ # The playbook — `.dev-flow/project.md`
2
+
3
+ The operator-owned file that says how *your* project is tested, run, versioned and guarded. Written once per repo with `flow init`, edited by you.
4
+
5
+ `.dev-flow/` ignores itself — its own `.dev-flow/.gitignore` is `*` with the single exception
6
+ `!project.md`, so no state, plan or worktree path is ever tracked by accident. Tracking the playbook
7
+ in a repo that ignores the folder is a deliberate one-off:
8
+ `git add -f .dev-flow/project.md && DEV_FLOW_ALLOW_MAIN=1 git commit -m "track project.md"` — the
9
+ env prefix is for dev-flow's `pre-commit` hook, which allows a main commit whose only change is the
10
+ playbook.
11
+
12
+ It is **committed to git**; you edit it (the agent is blocked from writing the main-checkout copy
13
+ and may edit it only inside a docs/patch flow's worktree, or apply it through `flow init --write`
14
+ on your word, which validates). Content is organized per stage
15
+ (`## plan`, `## implement`, `## pre-release`, `## release`…) in **three tiers** — tier 1 splits into
16
+ executable checks and hard safety rules:
17
+
18
+ | Tier | Notation | Enforcement |
19
+ |---|---|---|
20
+ | 1 — executable | ` ```flow` (`key = value`) config blocks; ` ```flow-check <name>` command blocks | gates run every check, record pass/fail, worktree edits invalidate — hard |
21
+ | 1 — guard | ` ```flow-guard ` (`deny`/`confirm`/`protect`/`forbid`) rules in `## global` | enforced on every bash command and every edit/write path, at every stage — hard block or operator confirmation |
22
+ | 2 — checklist | `- [ ] item` lines (`- [ ] (ui) item` = only for `--ui` features) | gate confirms cover them as a set (hashed, invalidated on edits); `(ui)` items never appear for non-UI features |
23
+ | 3 — prose | any other text | injected to the agent when its stage begins — advisory |
24
+
25
+ Example `## implement` section:
26
+
27
+ ````markdown
28
+ ## implement
29
+ ```flow
30
+ test = npm test
31
+ run = npm run dev
32
+ health = curl -fs localhost:3000/healthz
33
+ healthWait = 30
34
+ ```
35
+ ```flow-check overflow
36
+ node scripts/check-overflow.mjs
37
+ ```
38
+ - [ ] visual pass at 390px and 1280px: no overflows, no stray scrollbars
39
+ Prefer feature flags for risky paths.
40
+ ````
41
+
42
+ `healthWait` (default 30) is the seconds the post-deploy probe may keep retrying, every 2s — a service
43
+ whose readiness lags its container start needs a wait, not a poll loop hand-written into the playbook.
44
+ `healthWait = 0` restores the single shot.
45
+
46
+ Your project's own safety rules are a `flow-guard` block in `## global` — the reason a project
47
+ adopting dev-flow can delete its own guard extension:
48
+
49
+ ````markdown
50
+ ## global
51
+ ```flow-guard
52
+ deny \bgit\b[^\n]*\bpush\b[^\n]*--force(?!\w) :: history stays linear — PRs squash-merge
53
+ deny \bgit\b[^\n]*\bworktree\s+prune\b :: it prunes other sessions' worktrees
54
+ confirm \bgit\b[^\n]*\bbranch\s+(?:-D\b|--delete\b) :: deletes a squash-merged branch git cannot see as merged
55
+ confirm \bdocker\b[^\n]*\bcompose\b[^\n]*\brestart\b :: drops a permanent upstream connection
56
+ protect docker/.env* :: gitignored live secrets — and any backup copied beside them
57
+ forbid docker/secrets :: gitignored secret storage
58
+ ```
59
+ ````
60
+
61
+ | Verb | Applies to | What happens |
62
+ |---|---|---|
63
+ | `deny` | one segment of a bash command | hard block — no confirmation offered |
64
+ | `confirm` | one segment of a bash command | asks the operator; **blocked when no UI is present** — a hidden confirmation is not a confirmation |
65
+ | `protect` | an edit/write path | asks the operator, same no-UI rule |
66
+ | `forbid` | an edit/write path | hard block |
67
+
68
+ ` :: ` separates the matcher from the reason, so reasons are free text and show up verbatim when a
69
+ rule fires. `deny`/`confirm` matchers are JS regex sources — an unparseable regex is a hard finding,
70
+ so `flow init --write` refuses the file (an unknown verb is a warning). Anchor a matcher on the **verb**
71
+ you mean, not on a flag that can appear anywhere after `git`: `\bgit\b[^\n]*--force` also refuses
72
+ `git filter-branch --force` (a local rewrite) and `git show -f`, and a rule that blocks unrelated
73
+ commands gets routed around. You do not need a rule for forced pushes at all — dev-flow confirms every
74
+ `git push --force` / `-f` with the operator itself, and prints the one prefix that answers for it
75
+ (`DEV_FLOW_ALLOW_REWRITE=1`). `protect`/`forbid` take a repo-relative path; a rule on a directory covers
76
+ its subtree, including files that do not exist yet. A rule whose **last segment** globs
77
+ (`protect docker/.env*`) covers the siblings in that directory whose name matches — the backup copied
78
+ beside a secret is the same secret, and the commit that adds it never names the file, so no `deny` regex
79
+ ever sees it. A glob anywhere else in the path warns at `flow init`: it names one literal file called
80
+ `.env*` and guards nothing.
81
+ The block belongs to `## global` — these rules are never stage-scoped, and one under a stage warns and
82
+ applies nothing.
83
+
84
+ Matching is per shell segment (the guard splits on `;`, `|`, `&&` and newlines, honouring `cd … &&` and
85
+ `git -C`), and a segment whose head only reads (`grep`, `cat`, `ls`, `git log`, `jq`, …) is skipped — so
86
+ `grep "git push --force" docs/` searches, it does not push. A read-only head that can still write
87
+ (`find -delete`, `find -exec …`, `sort -o FILE`, `awk 'BEGIN{system(…)}'`) is judged like a writer —
88
+ your `deny` rules do not have a back door through the read-only list.
89
+
90
+ `## pre-release` prose is where rules like “follow semver: minor for user-facing features, patch
91
+ otherwise; confirm the number before tagging” live — the agent sees them at stage entry, and
92
+ `flow release` / `flow release-done` hard-check the manifest side (bumped past the last tag, then
93
+ manifest↔tag agreement). `## global` holds
94
+ `defaultBranch`, `branchPattern`, `worktreeDir`, `mainHook`, `mockupGlobs`, `bashTimeout = require`
95
+ (default: every shell call must state its own timeout) `| inject`, `release.review`,
96
+ `release.changelog` (default `CHANGELOG.md`; set it empty to opt out), `release.releases` and `release.artifact = <cmd>` (verify the published artifact — image registry probe etc. — before release-done completes); the implement stage takes `setup`/`test`/`build`/`run`/`seedData`/`health`/
97
+ `dev`. Of those, `setup` runs once inside a freshly created worktree at `approve` (where `cp .env` +
98
+ `npm install` belong — a failing setup fails the gate), and `run` is advisory: the agent starts the app
99
+ with it for the running-and-healthy walk, dev-flow never runs it itself. `dev` is the dev deploy run by
100
+ `flow verify` before the operator question — it must background and exit,
101
+ e.g. `docker compose up -d .`; the gate names what it ran (`dev ran (<cmd>)`, or
102
+ `dev: none configured`) so minutes of deploys never read as "nothing happened". After a deploy that
103
+ ran, verify re-runs the health probe (`commands.health` or a `flow-check` block named `health`, with
104
+ `seedData` first when set) in the worktree and refuses the gate when it fails — the pre-deploy
105
+ `flow check` PASS describes a container that no longer exists. What "healthy and serving this
106
+ build" means stays the project's own assertion: the probe runs with the worktree as cwd, so it can
107
+ wait for readiness and compare the served build stamp with `git rev-parse HEAD`. No probe → the
108
+ gate prints `health: none configured` and `flow doctor` flags the combination. The pre-release and release stages take
109
+ `versionFile`/`versionTag` (defaults `v{version}`; accepted under either section). `## global` also takes
110
+ `forge = github | gitlab | self` — the release machinery's target; when unset it is detected from the
111
+ `origin` URL at the moment of use (GitHub gets `gh`, GitLab gets `glab`, self-hosted stays forge-neutral:
112
+ no automatic release creation, merge proof by ancestry only), and `release.releases = false` skips
113
+ automatic release creation entirely (the legacy `github` key still parses, with a rename warning).
114
+ A leftover `project.json` is no longer read — the doctor tells you to fold it in and delete it.
115
+
116
+ `flow init` is the dialogue around this file: **fresh repo** → prints the setup brief (detection report
117
+ plus one question per undetected knob — forge, version convention, releases, artifacts, checks, prose),
118
+ the agent asks you each question, fills the draft and writes your answers back validated
119
+ (`--write <content>`; `--draft` prints the draft or current file whenever you'd rather read raw — an
120
+ interactive init may also confirm the draft with you on the spot and write it then); **existing file** → doctor mode (drift, dead commands/scripts,
121
+ unparseable manifest, dual configs, **ghost feature dirs** (a `.dev-flow/<slug>/` whose `state.json`
122
+ is gone — it offers to delete those, on your confirm, and says so instead when no UI is present) —
123
+ it never runs your commands) offering a refresh — which it
124
+ **refuses** whenever your file has lines a regenerated draft cannot reproduce (prose, your own
125
+ checklist items, hand-set keys): it prints the findings and the lines it would drop instead of
126
+ overwriting them. `--force` remains the deliberate overwrite — with your word (`--confirmed`, or one
127
+ confirm dialog) and a `project.md.bak` of what was there. At any time
128
+ you can just edit `.dev-flow/project.md` yourself — the next `flow init` (doctor) validates and
129
+ flags problems, and `flow doctor` runs the same probes on demand without offering to change
130
+ anything (findings are also printed whenever `flow init` runs without a UI, e.g. in CI). `--force`
131
+ rewrites from fresh detection. Improvement
132
+ ideas the agent notices along the way queue up in `.dev-flow/reflections.md` under `## playbook`
133
+ (what to change in *your* playbook) and `## plugin` (what to change in dev-flow itself) —
134
+ `flow reflect "<note>"` appends them, plain `flow doctor` reviews the open ones and routes
135
+ each fix to the flow that implements it (docs flow for playbook items — those edits only pass a
136
+ docs/patch flow's worktree), bare `flow reflect` hands the agent the full end-of-session
137
+ reflection protocol (review the session log, re-check open items against fixes that already
138
+ shipped, dedupe new findings, put the fix decision to the operator) — and nothing there ever gates.
139
+ Future features have their own file: `.dev-flow/roadmap.md`, a gitignored backlog `flow roadmap`
140
+ manages (add / pick → `flow start` / done / tidy) — a picked item is checked off by `finish` when
141
+ its feature ships, and it never gates either.