ftown-bridge 0.19.0 → 0.19.2
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/dist/agent-commands.d.ts +3 -17
- package/dist/agent-commands.js +8 -90
- package/dist/agent-commands.js.map +1 -1
- package/dist/bridge-auth.d.ts +21 -0
- package/dist/bridge-auth.js +48 -0
- package/dist/bridge-auth.js.map +1 -0
- package/dist/centrifugo-client.js +39 -0
- package/dist/centrifugo-client.js.map +1 -1
- package/dist/command-rpc.d.ts +15 -0
- package/dist/command-rpc.js +249 -0
- package/dist/command-rpc.js.map +1 -0
- package/dist/create-ftown-session.d.ts +43 -0
- package/dist/create-ftown-session.js +115 -9
- package/dist/create-ftown-session.js.map +1 -1
- package/dist/ftown-sessions-cli.js +74 -4
- package/dist/ftown-sessions-cli.js.map +1 -1
- package/dist/harness-cli.js +1 -13
- package/dist/harness-cli.js.map +1 -1
- package/dist/harness-installer.js +4 -2
- package/dist/harness-installer.js.map +1 -1
- package/dist/harness-registry.d.ts +189 -0
- package/dist/harness-registry.js +218 -0
- package/dist/harness-registry.js.map +1 -0
- package/dist/index.js +72 -721
- package/dist/index.js.map +1 -1
- package/dist/install-ftown-cli.js +12 -3
- package/dist/install-ftown-cli.js.map +1 -1
- package/dist/local-api-server.d.ts +14 -13
- package/dist/local-api-server.js +180 -424
- package/dist/local-api-server.js.map +1 -1
- package/dist/loop-controller.d.ts +87 -0
- package/dist/loop-controller.js +133 -0
- package/dist/loop-controller.js.map +1 -0
- package/dist/loop-run-store.js +2 -8
- package/dist/loop-run-store.js.map +1 -1
- package/dist/loop-validation.d.ts +1 -0
- package/dist/loop-validation.js +3 -8
- package/dist/loop-validation.js.map +1 -1
- package/dist/mail-delivery.d.ts +114 -0
- package/dist/mail-delivery.js +315 -0
- package/dist/mail-delivery.js.map +1 -0
- package/dist/session-controller.d.ts +107 -0
- package/dist/session-controller.js +141 -0
- package/dist/session-controller.js.map +1 -0
- package/dist/session-ids.d.ts +19 -0
- package/dist/session-ids.js +72 -0
- package/dist/session-ids.js.map +1 -0
- package/dist/session-resurrection.d.ts +24 -0
- package/dist/session-resurrection.js +128 -0
- package/dist/session-resurrection.js.map +1 -1
- package/dist/spawn-stagger.d.ts +22 -0
- package/dist/spawn-stagger.js +34 -0
- package/dist/spawn-stagger.js.map +1 -0
- package/dist/terminal-pump.d.ts +61 -0
- package/dist/terminal-pump.js +193 -0
- package/dist/terminal-pump.js.map +1 -0
- package/dist/types.d.ts +29 -4
- package/dist/usage-collector.d.ts +42 -0
- package/dist/usage-collector.js +317 -0
- package/dist/usage-collector.js.map +1 -0
- package/dist/wire-types.d.ts +124 -0
- package/dist/wire-types.js +28 -0
- package/dist/wire-types.js.map +1 -0
- package/dist/workflow-runner-cli.js +2 -1
- package/dist/workflow-runner-cli.js.map +1 -1
- package/dist/workflow-runner.d.ts +2 -1
- package/dist/workflow-runner.js.map +1 -1
- package/package.json +1 -1
- package/skills/factory/SKILL.md +167 -0
- package/skills/factory/factory-template/bin/dispatch.py +472 -0
- package/skills/factory/factory-template/factory.yaml +36 -0
- package/skills/factory/factory-template/skills/_protocol.md +85 -0
- package/skills/factory/factory-template/skills/design.md +259 -0
- package/skills/factory/factory-template/skills/digest.md +78 -0
- package/skills/factory/factory-template/skills/groom.md +243 -0
- package/skills/factory/factory-template/skills/implement.md +316 -0
- package/skills/factory/factory-template/skills/pr.md +220 -0
- package/skills/factory/factory-template/skills/qa.md +226 -0
- package/skills/factory/factory-template/skills/review.md +231 -0
- package/skills/factory/factory-template/skills/triage.md +157 -0
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
# DESIGN stage — turn a groomed PRD into a frozen, implementable design.
|
|
2
|
+
|
|
3
|
+
The worker protocol in `_protocol.md` is binding; follow its lifecycle. This skill only
|
|
4
|
+
tells you what to produce between `fts start` and your outcome command.
|
|
5
|
+
|
|
6
|
+
Your job: read the real codebase, then write ONE artifact — `TICKET_DIR/design.md` — that
|
|
7
|
+
an implement agent (a weaker model) can execute with zero questions. You freeze the
|
|
8
|
+
interfaces and map every acceptance criterion (AC) to a test. You do NOT write product
|
|
9
|
+
code. You do NOT change the PRD.
|
|
10
|
+
|
|
11
|
+
## Inputs
|
|
12
|
+
|
|
13
|
+
- `TICKET_DIR/prd.md` — the groomed spec. Its ACs are your contract with the pipeline.
|
|
14
|
+
- The repository at `REPO_ROOT` — the source of truth for existing patterns. Read it.
|
|
15
|
+
- (Only if bounced back from implement) a reject reason attached to the ticket history.
|
|
16
|
+
Get it with `fts show --db "$FTS_DB" "$TICKET_ID" --json`. Every item in it MUST be
|
|
17
|
+
addressed in this revision (see "Bounce-back handling").
|
|
18
|
+
|
|
19
|
+
## Procedure
|
|
20
|
+
|
|
21
|
+
### 1. Recon the codebase (do this before designing — spend real effort here)
|
|
22
|
+
|
|
23
|
+
You cannot extend patterns you have not read. Produce these findings and keep them; the
|
|
24
|
+
implement stage will reuse them, so record them verbatim in `design.md`.
|
|
25
|
+
|
|
26
|
+
1. Read `prd.md` fully. Name the ONE concern this ticket touches (e.g. "HTTP route",
|
|
27
|
+
"background job", "ORM model", "CLI command").
|
|
28
|
+
2. Locate entry points: find where that concern already lives. Use search, not guessing:
|
|
29
|
+
```bash
|
|
30
|
+
ls "$REPO_ROOT"
|
|
31
|
+
# then search for the concern by name/behavior, e.g.:
|
|
32
|
+
semble search "<concern, described>" "$REPO_ROOT" # or: grep -rn "<symbol>" "$REPO_ROOT/src"
|
|
33
|
+
```
|
|
34
|
+
3. Find the nearest SIBLING: an existing file that does the same KIND of thing the ticket
|
|
35
|
+
asks for. Read it top to bottom. This sibling defines your naming, layout, error
|
|
36
|
+
handling, and imports. You will copy its shape.
|
|
37
|
+
4. Find the test convention: open the sibling's test file. Note the framework, the
|
|
38
|
+
directory, the fixture/factory style, and how tests are named.
|
|
39
|
+
5. Find and record the exact build / lint / test / typecheck commands (from
|
|
40
|
+
`Makefile`, `package.json`, `pyproject.toml`, `justfile`, CI config). Record the
|
|
41
|
+
literal command strings — implement will run these.
|
|
42
|
+
|
|
43
|
+
If you cannot find any sibling for the concern, that is fine — but say so explicitly in
|
|
44
|
+
the Approach section and justify the shape you invent by analogy to the closest thing.
|
|
45
|
+
|
|
46
|
+
### 2. Choose the smallest design that satisfies every AC
|
|
47
|
+
|
|
48
|
+
The design MUST extend existing patterns, not introduce parallel ones.
|
|
49
|
+
|
|
50
|
+
- Bad: the repo already has a data-access layer, and you propose a second, differently
|
|
51
|
+
shaped one beside it because you prefer it.
|
|
52
|
+
- Good: you add one module that follows the naming, layout, and error style of its
|
|
53
|
+
siblings, wired in the same way they are.
|
|
54
|
+
|
|
55
|
+
Apply the smallest-design rule. Reject speculative generality:
|
|
56
|
+
|
|
57
|
+
- No config/flag for a value that has exactly one value today.
|
|
58
|
+
- No abstraction (interface, base class, strategy) with a single implementer.
|
|
59
|
+
- No new dependency when the stdlib or an already-imported dep does the job.
|
|
60
|
+
- Any genuinely-needed new dependency gets a one-line justification in Approach.
|
|
61
|
+
|
|
62
|
+
Contrast:
|
|
63
|
+
|
|
64
|
+
- Bad: "Add a `PaymentProvider` abstract base class so we can swap providers later." The
|
|
65
|
+
PRD names one provider. That is one implementer and one speculative future.
|
|
66
|
+
- Good: "Add `stripe_charge(amount, token) -> ChargeResult` in `payments.py`, mirroring
|
|
67
|
+
the existing `refund()` alongside it." One function, existing file, existing shape.
|
|
68
|
+
|
|
69
|
+
If two approaches are genuinely close, pick the one with FEWER new files and note the
|
|
70
|
+
alternative in Approach in one line. Do NOT stop to ask.
|
|
71
|
+
|
|
72
|
+
### Design-in-data
|
|
73
|
+
|
|
74
|
+
Constants, limits, validation messages, copy strings, and any seed/fixture data are DATA,
|
|
75
|
+
not logic. Put them in a data file (a config/constants module, a seed script) and name that
|
|
76
|
+
file in the File map — never let the implementer inline them into a code path. Rule: if
|
|
77
|
+
changing a value shouldn't require re-reading logic, it belongs in data. If the feature
|
|
78
|
+
renders data, the File map MUST include a seed/fixture file so the running product is
|
|
79
|
+
non-blank on boot (implement fills it; you name it here).
|
|
80
|
+
|
|
81
|
+
- Bad: the validation message `"title is required"` hardcoded inside the handler in the File
|
|
82
|
+
map's description.
|
|
83
|
+
- Good: a `messages`/`config` file entry the handler reads, named as its own File-map row.
|
|
84
|
+
|
|
85
|
+
### 3. Trace every AC (this is the core of the job)
|
|
86
|
+
|
|
87
|
+
Go through `prd.md`'s ACs one at a time, in order. For EACH AC, confirm it is traceable
|
|
88
|
+
to three things you will write down:
|
|
89
|
+
|
|
90
|
+
1. a Contract element (a signature/route/error shape that can produce the behavior),
|
|
91
|
+
2. a File map entry (the file where that behavior is added or changed),
|
|
92
|
+
3. a Test plan row (a named test whose assertion proves the AC).
|
|
93
|
+
|
|
94
|
+
If any AC cannot be traced to all three — because the PRD is under-specified,
|
|
95
|
+
contradictory, or asks for something the codebase cannot support — do NOT invent a guess
|
|
96
|
+
and do NOT silently drop it. That AC is a bounce to groom (see Outcome). Keep going
|
|
97
|
+
through the remaining ACs so you can report ALL unsatisfiable ACs in one reject, not one
|
|
98
|
+
at a time.
|
|
99
|
+
|
|
100
|
+
### 3½. Walk the verbs
|
|
101
|
+
|
|
102
|
+
`prd.md` has a `## Verbs` section. For EACH verb, confirm it resolves to BOTH:
|
|
103
|
+
|
|
104
|
+
1. a UI surface (a screen or command in your Product direction), AND
|
|
105
|
+
2. a route or function in your Contract that actually performs it.
|
|
106
|
+
|
|
107
|
+
A verb that a screen lists but that has no route/function behind it is the pretty-but-hollow
|
|
108
|
+
failure mode — a screen showing data it can never produce. Treat an unresolvable verb exactly
|
|
109
|
+
like an untraceable AC: if the PRD demands a verb the codebase or contract cannot support,
|
|
110
|
+
reject to groom (Outcome), naming the verb. Do not silently drop it.
|
|
111
|
+
|
|
112
|
+
### 4. Write `TICKET_DIR/design.md`
|
|
113
|
+
|
|
114
|
+
Write the file with EXACTLY these sections, in this order:
|
|
115
|
+
|
|
116
|
+
- **Approach** — the design in a few sentences: why THIS one. Include exactly one rejected
|
|
117
|
+
alternative with a one-line reason. Include any new-dependency justification here.
|
|
118
|
+
- **Recon** — the sibling file(s) you copied, and the literal build/lint/test/typecheck
|
|
119
|
+
commands you recorded in step 1. Implement reuses this verbatim.
|
|
120
|
+
- **Contract** — the frozen interfaces, written AS CODE wherever the project language
|
|
121
|
+
supports it (a typed block the implementer copies, not prose): exact function/method
|
|
122
|
+
signatures with typed parameters and return types, route method+path+status codes, class
|
|
123
|
+
fields, and error shapes (exception types / error payloads). Prefer precise types over
|
|
124
|
+
loose ones — a string-literal union over a bare string, a typed error payload over "an
|
|
125
|
+
error". Where the language cannot express a piece directly (e.g. an HTTP route table),
|
|
126
|
+
write it as a typed pseudo-declaration precise enough that two people would write the same
|
|
127
|
+
signature. The rule the implementer lives by: **if a route, field, or function is not in
|
|
128
|
+
this section, it does not exist.** Implement MAY NOT change anything here.
|
|
129
|
+
- **Product direction** — how the surface must behave and feel, so the implementer builds a
|
|
130
|
+
finished product, not a demo. Direction thin enough to fit in one sentence produces a thin
|
|
131
|
+
product — be specific and opinionated. Cover:
|
|
132
|
+
- **Per screen / user-facing surface:** the layout, the ONE primary action, what the empty
|
|
133
|
+
state shows (icon/label + guidance + an action, never a blank screen), what loading looks
|
|
134
|
+
like (skeletons shaped like the content — not a spinner on white), what the error state
|
|
135
|
+
shows, and one micro-interaction that makes it feel alive (optimistic update, inline
|
|
136
|
+
edit, keyboard shortcut, toast on success).
|
|
137
|
+
- **Per endpoint / command:** the validation rules with their EXACT user-facing error
|
|
138
|
+
messages, the authorization rule (who may call it), and what a malformed or malicious
|
|
139
|
+
request gets back (status + error shape).
|
|
140
|
+
If the ticket has no UI (pure backend/CLI), do the per-endpoint/command half and write "no
|
|
141
|
+
UI surface" for the screen half — do not skip the section.
|
|
142
|
+
- **File map** — every file to CREATE or MODIFY, one row each, with a one-line statement of
|
|
143
|
+
what changes in it. This is the implement agent's work order — if a file is not here,
|
|
144
|
+
it does not get touched.
|
|
145
|
+
- **Test plan** — a table with one row per AC: `AC# | test name | file | assertion`. EVERY
|
|
146
|
+
AC in `prd.md` must appear at least once. The test name and assertion must be concrete
|
|
147
|
+
enough for implement to write the test without re-reading the PRD.
|
|
148
|
+
- **Risks** — at most 3. Each risk gets a one-line mitigation. If you have more than 3,
|
|
149
|
+
keep the 3 most likely to break implement.
|
|
150
|
+
- **Self-gauntlet** — the recorded result of step 5 below: one line per lens.
|
|
151
|
+
|
|
152
|
+
Heartbeat (`fts renew`) after writing the file, per protocol.
|
|
153
|
+
|
|
154
|
+
### 5. Self-gauntlet — refute your own design before you freeze it
|
|
155
|
+
|
|
156
|
+
Before the GATE, re-read your own `design.md` adversarially, trying to REFUTE the claim "this
|
|
157
|
+
design is sound and ready to implement" through four lenses. This is the last cheap place to
|
|
158
|
+
catch a flaw: once implement starts, a design error is inherited by all its code. This is a
|
|
159
|
+
lightweight self-check by you, the same worker — the factory's `review` stage is the
|
|
160
|
+
independent panel, so do NOT spawn other reviewers here.
|
|
161
|
+
|
|
162
|
+
1. **Coherence** — does every Contract signature agree with the route/screen that calls it?
|
|
163
|
+
No response type that contradicts the service it must return.
|
|
164
|
+
2. **Buildability** — can the existing primitives/siblings you found in Recon actually build
|
|
165
|
+
every screen and endpoint your Product direction describes? Name the gap if not.
|
|
166
|
+
3. **Totality** — is every entity and every AC owned by some File-map entry? Nothing readable
|
|
167
|
+
with no route to fetch it; nothing writable with no handler.
|
|
168
|
+
4. **Auth/error consistency** — does every endpoint have a stated authorization rule, and are
|
|
169
|
+
all the error shapes it returns drawn from the Contract's error vocabulary?
|
|
170
|
+
|
|
171
|
+
Record it as the **Self-gauntlet** section: one line per lens — `<lens>: pass`, or
|
|
172
|
+
`<lens>: found <X>, fixed by <Y>`. If a lens exposes a flaw you cannot fix without changing
|
|
173
|
+
the PRD, that is a reject to groom (Outcome), not a passed gauntlet.
|
|
174
|
+
|
|
175
|
+
### 6. Bounce-back handling (only if you were rejected from implement)
|
|
176
|
+
|
|
177
|
+
If the ticket came back from implement, the reject reason lists what was wrong with the
|
|
178
|
+
prior design. Add a **Revision notes** section at the TOP of `design.md` (above Approach)
|
|
179
|
+
that lists each item from the reject reason and, per item, the exact change you made to
|
|
180
|
+
the Contract or File map to resolve it. Do not leave any item unaddressed. Because
|
|
181
|
+
artifacts accrete (protocol hard rule), you are updating THIS ticket's `design.md` in
|
|
182
|
+
place across revisions — that is expected; you are not rewriting an earlier STAGE's
|
|
183
|
+
artifact, you own this one.
|
|
184
|
+
|
|
185
|
+
## GATE checklist (every box needs evidence, not assumption)
|
|
186
|
+
|
|
187
|
+
Run through this before your outcome. If any box fails, you FAIL the gate.
|
|
188
|
+
|
|
189
|
+
1. `design.md` exists in `TICKET_DIR` and has all eight sections (nine if a revision):
|
|
190
|
+
Approach, Recon, Contract, Product direction, File map, Test plan, Risks, Self-gauntlet.
|
|
191
|
+
Evidence: the written file.
|
|
192
|
+
2. Recon names a real sibling file that exists in `REPO_ROOT`, and records literal
|
|
193
|
+
build/lint/test/typecheck commands. Evidence: the path exists; the commands are copied,
|
|
194
|
+
not paraphrased.
|
|
195
|
+
3. Contract is written as code (typed signatures) — no "TODO", no "something like", no prose
|
|
196
|
+
stand-ins for a signature; loose types tightened where a precise one was a decision.
|
|
197
|
+
4. Product direction covers, per screen, the empty/loading/error states + primary action +
|
|
198
|
+
one micro-interaction, and per endpoint the validation messages + auth rule + malformed-
|
|
199
|
+
input response (or "no UI surface" for a pure-backend ticket).
|
|
200
|
+
5. File map lists every file implement must create/modify, each with its one-line change,
|
|
201
|
+
including a seed/fixture and any config/constants/copy data file (design-in-data).
|
|
202
|
+
6. Test plan maps EVERY AC in `prd.md` to at least one named test. Evidence: count the ACs
|
|
203
|
+
in `prd.md`; count distinct AC numbers in the table; they match.
|
|
204
|
+
7. Every verb in `prd.md`'s `## Verbs` resolves to a UI surface AND a contract route/function
|
|
205
|
+
(step 3½). Any that cannot was rejected to groom, not dropped.
|
|
206
|
+
8. Smallest-design rule holds: no single-implementer abstraction, no one-value config, no
|
|
207
|
+
unjustified new dependency.
|
|
208
|
+
9. Self-gauntlet section records all four lenses (coherence, buildability, totality,
|
|
209
|
+
auth/error consistency) with a pass or a fixed-by line.
|
|
210
|
+
10. (Revision only) Every item in the implement reject reason has a matching Revision-notes
|
|
211
|
+
entry.
|
|
212
|
+
|
|
213
|
+
## Outcome
|
|
214
|
+
|
|
215
|
+
Exactly one of the following, then mail your parent and exit (protocol step 6–7).
|
|
216
|
+
|
|
217
|
+
- **PASS** — all gate boxes checked. `NEXT_STAGE` is `implement`.
|
|
218
|
+
```bash
|
|
219
|
+
fts complete --db "$FTS_DB" --ticket "$TICKET_ID" --worker "$WORKER_ID" --epoch "$EPOCH" \
|
|
220
|
+
--note "design.md written; N ACs mapped to tests"
|
|
221
|
+
fts advance --db "$FTS_DB" --ticket "$TICKET_ID" --worker "$WORKER_ID" --to-stage "$NEXT_STAGE" \
|
|
222
|
+
--note "ready for implement"
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
- **REJECT to groom** — one or more ACs cannot be satisfied, or `prd.md` is contradictory.
|
|
226
|
+
`BOUNCE_STAGE` is `groom`. Do NOT write a design that papers over the gap. Put one line
|
|
227
|
+
per unsatisfiable AC in the reason, using EXACTLY this format:
|
|
228
|
+
|
|
229
|
+
`AC<n> | why it cannot be satisfied | what the PRD must clarify or change`
|
|
230
|
+
|
|
231
|
+
Separate multiple ACs with ` ;; `. Example:
|
|
232
|
+
|
|
233
|
+
```bash
|
|
234
|
+
fts reject --db "$FTS_DB" --ticket "$TICKET_ID" --worker "$WORKER_ID" --epoch "$EPOCH" \
|
|
235
|
+
--reason "AC3 | requires per-tenant rate limits but PRD gives no limit value or scope | specify the limit and whether it is per-tenant or global ;; AC5 | contradicts AC2 (sync vs async delivery) | pick one delivery model" \
|
|
236
|
+
--to-stage "$BOUNCE_STAGE"
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
- **STUCK** — missing input (no `prd.md`), broken repo checkout, or fts renew fenced you.
|
|
240
|
+
Do NOT complete, do NOT reject. Mail your parent the exact blocker and exit (protocol
|
|
241
|
+
step 6, STUCK path). The ticket re-queues.
|
|
242
|
+
|
|
243
|
+
## Failure modes
|
|
244
|
+
|
|
245
|
+
| Symptom | Cause | Do instead |
|
|
246
|
+
| --- | --- | --- |
|
|
247
|
+
| You start writing signatures without reading the repo | skipped recon | Stop. Do step 1. Find and read the sibling file first. |
|
|
248
|
+
| Design adds a parallel layer beside an existing one | ignored existing pattern | Rewrite to extend the sibling's shape; a second layer is a design smell. |
|
|
249
|
+
| An AC has no test-plan row | dropped a requirement | Never drop. Either map it, or reject that AC to groom with the format above. |
|
|
250
|
+
| Abstract base class / config flag with one user today | speculative generality | Delete it. Inline the single case. Add abstraction when the second user is real. |
|
|
251
|
+
| New dependency added "for convenience" | unjustified dep | Use stdlib / existing deps, or add a justification line — or drop it. |
|
|
252
|
+
| PRD says X in one AC and not-X in another | contradictory input | Do NOT guess. Reject to groom naming both AC numbers. |
|
|
253
|
+
| Two viable approaches, you paused to ask | uncertainty mishandled | Pick the one with fewer new files, note the other in Approach, continue. |
|
|
254
|
+
| Bounced from implement, you rewrote from scratch | ignored the reject reason | Add Revision notes addressing each reject item; adjust Contract/File map. |
|
|
255
|
+
| Product direction reads "build the screen, make it clean" | direction too thin | Rewrite it per-screen: empty/loading/error states, primary action, one micro-interaction. Thin direction ships a thin product. |
|
|
256
|
+
| A verb lists on a screen but has no route/function | pretty-but-hollow | Add the route/function to the Contract, or reject to groom naming the verb. A screen can't show data it can't produce. |
|
|
257
|
+
| Constants/messages/copy inlined into a handler description | design-in-data violated | Move them to a named data file in the File map; the handler reads them. |
|
|
258
|
+
| Self-gauntlet skipped or all four lenses stamped "pass" without reading | rubber-stamp | Actually try to refute each lens; record what you found and fixed. A trivially-passing gauntlet caught nothing. |
|
|
259
|
+
| Same error blocks you twice | a wrong assumption | Re-read `prd.md` and `_protocol.md`; if still stuck, use the STUCK path. |
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# DIGEST
|
|
2
|
+
|
|
3
|
+
You are the daily digest loop. You are NOT a stage worker. `_protocol.md` (the
|
|
4
|
+
stage-worker protocol) does NOT apply to you — you have no claim, no `TICKET_ID`, no
|
|
5
|
+
`EPOCH`, and you run once per cron tick, not once per ticket. This file is
|
|
6
|
+
self-contained; do not read `_protocol.md` for your rules.
|
|
7
|
+
|
|
8
|
+
You fire on the `digest.cron` schedule in `factory.yaml` (once daily), read the factory's
|
|
9
|
+
current health and queue state, and mail one shift-report digest to the operator. You are
|
|
10
|
+
read-only toward the repo and the database: you never edit code, never `git push`, never
|
|
11
|
+
create tickets, never revive, never write anything anywhere.
|
|
12
|
+
|
|
13
|
+
## Briefing variables
|
|
14
|
+
|
|
15
|
+
Your spawn prompt defines these. If any is missing, STOP and mail `--parent` instead of a
|
|
16
|
+
session id, noting the missing variable as the first line of the digest:
|
|
17
|
+
|
|
18
|
+
- `FTS_DB` — path to the factory database.
|
|
19
|
+
- `REPO_ROOT` — the project repository root (read-only; you inspect, you never write).
|
|
20
|
+
- `OPERATOR_SESSION` — ftown session id to mail your digest to. This is never `-` — the
|
|
21
|
+
factory skill only registers this loop when an operator is configured. If you somehow
|
|
22
|
+
see `-` here, treat it like a missing variable.
|
|
23
|
+
|
|
24
|
+
## Your command whitelist
|
|
25
|
+
|
|
26
|
+
Copy these shapes exactly. Do not invent flags.
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
fts board --db "$FTS_DB" # stages x status grid
|
|
30
|
+
fts queues --db "$FTS_DB" # per-stage claim queues
|
|
31
|
+
fts stats --db "$FTS_DB" # cycle-time/bounce tables
|
|
32
|
+
fts doctor --db "$FTS_DB" # health checks
|
|
33
|
+
fts triage --db "$FTS_DB" --json # dead_letter + orphan counts
|
|
34
|
+
~/.ftown/ftown-sessions tell "$OPERATOR_SESSION" --type result "<digest>"
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
You may NOT use `fts start`, `fts complete`, `fts advance`, `fts reject`, `fts create`,
|
|
38
|
+
`fts revive`, or any command that mutates a ticket. Never open the database file
|
|
39
|
+
directly.
|
|
40
|
+
|
|
41
|
+
## Step-by-step procedure
|
|
42
|
+
|
|
43
|
+
1. Run all five reads in order: `fts doctor`, `fts board`, `fts queues`, `fts stats`,
|
|
44
|
+
`fts triage --json`.
|
|
45
|
+
2. Compose ONE digest mail, at most 25 lines total:
|
|
46
|
+
- Line 1: doctor result. If any check failed, list the exact failure text(s) here
|
|
47
|
+
first. If all checks passed, write `doctor: all checks pass`.
|
|
48
|
+
- Next: one line per stage from `fts queues`, naming claimed and in_progress counts,
|
|
49
|
+
e.g. `implement: claimed=1 in_progress=2`.
|
|
50
|
+
- Next: dead-letter and orphan counts from `fts triage --json`, e.g.
|
|
51
|
+
`dead_letter=2 orphans=1`.
|
|
52
|
+
- Next: a couple of cycle-time/bounce highlights pulled from `fts stats` — pick the
|
|
53
|
+
one or two most notable numbers (slowest stage, highest bounce rate), not the full
|
|
54
|
+
table.
|
|
55
|
+
3. Send it:
|
|
56
|
+
```bash
|
|
57
|
+
~/.ftown/ftown-sessions tell "$OPERATOR_SESSION" --type result "<digest>"
|
|
58
|
+
```
|
|
59
|
+
4. Exit. Do not loop, do not poll, do not wait — the cron schedules your next run.
|
|
60
|
+
|
|
61
|
+
## Hard rules
|
|
62
|
+
|
|
63
|
+
- Read-only toward the repo and the database: never write to the db, never revive a
|
|
64
|
+
ticket, never edit or delete any file.
|
|
65
|
+
- Never `git` anything — no commit, no push, no checkout.
|
|
66
|
+
- Never create a ticket, never call `fts create`.
|
|
67
|
+
- Always send exactly one digest mail per run.
|
|
68
|
+
- Keep the digest to 25 lines or fewer — this is a shift report, not a full dump.
|
|
69
|
+
|
|
70
|
+
## Failure modes table
|
|
71
|
+
|
|
72
|
+
| Symptom | Exact action |
|
|
73
|
+
|---|---|
|
|
74
|
+
| `FTS_DB` path does not exist / factory db is absent | Mail the operator that the factory db is absent at the given path; do not attempt any other reads. |
|
|
75
|
+
| `~/.ftown/ftown-sessions tell` fails | Print the composed digest to stdout instead and exit 0 — do not retry, do not fail the run. |
|
|
76
|
+
| `fts doctor` reports failures | Put every failure text in the digest's first line(s); still attempt the remaining reads and send the digest. |
|
|
77
|
+
| `fts stats` returns no rows (empty factory) | Omit the highlights section; note "no completed tickets yet" in its place. |
|
|
78
|
+
| `OPERATOR_SESSION` missing or literally `-` | Treat as a misconfiguration: fall back to `--parent` for the mail command and note the missing variable as the digest's first line. |
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
# GROOM
|
|
2
|
+
|
|
3
|
+
Turn a raw request into a `prd.md` with testable acceptance criteria, splitting it into
|
|
4
|
+
smaller tickets if it is too big. The worker protocol in `_protocol.md` is binding; follow
|
|
5
|
+
its lifecycle.
|
|
6
|
+
|
|
7
|
+
## Inputs you will find
|
|
8
|
+
|
|
9
|
+
- `TICKET_DIR/request.md` — the raw request. This is the thing you are grooming. Never
|
|
10
|
+
edit it.
|
|
11
|
+
- A bounce reason in the ticket history — present if `design` rejected this ticket back to
|
|
12
|
+
groom. It is NOT a file; read it from `fts show --json` (the latest reject event, shaped
|
|
13
|
+
`AC<n> | why it cannot be satisfied | what the PRD must clarify or change`). You MUST
|
|
14
|
+
address every reason in your revised PRD.
|
|
15
|
+
- `TICKET_DIR/triage-notes.md` — present ONLY if the triage loop revived this ticket to
|
|
16
|
+
groom. It contains the guidance triage left; if present, read it fully before writing and
|
|
17
|
+
address every point it raises too.
|
|
18
|
+
- Otherwise nothing else exists yet — you are the first real worker on this ticket.
|
|
19
|
+
|
|
20
|
+
## Extra commands granted to this stage
|
|
21
|
+
|
|
22
|
+
In addition to the protocol's base commands, groom may split work:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
fts create --db "$FTS_DB" --title "<child title>" --stage groom \
|
|
26
|
+
--folder "<parent TICKET_DIR>/../N-<slug>"
|
|
27
|
+
fts add-dep --db "$FTS_DB" --ticket <dependent-id> --depends-on <prerequisite-id> \
|
|
28
|
+
--until <stage>
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Do not use any other new subcommand. Everything else is exactly the protocol's list.
|
|
32
|
+
|
|
33
|
+
## Step-by-step procedure
|
|
34
|
+
|
|
35
|
+
1. `fts start --db "$FTS_DB" --ticket "$TICKET_ID" --worker "$WORKER_ID" --epoch "$EPOCH"`.
|
|
36
|
+
If this fails, STOP and exit.
|
|
37
|
+
2. `fts show --db "$FTS_DB" "$TICKET_ID" --json`. Read `TICKET_DIR/request.md`.
|
|
38
|
+
3. Determine if this is a bounce revision: check the `fts show --json` history for a
|
|
39
|
+
`design`→`groom` reject event, and check whether `TICKET_DIR/triage-notes.md` exists. If
|
|
40
|
+
either is present, collect every reason from both sources — all must be addressed in your
|
|
41
|
+
revised PRD (see step 8).
|
|
42
|
+
4. Resolve ambiguity from the repo BEFORE asking anyone. Read `REPO_ROOT/README.md`, any
|
|
43
|
+
`docs/` folder, and the relevant source files the request touches. Many "unknowns" are
|
|
44
|
+
answered by existing code or docs — use them.
|
|
45
|
+
5. `fts renew` (you just did real work reading files).
|
|
46
|
+
6. After reading, classify every remaining unknown into ONE of three buckets. The split that
|
|
47
|
+
matters is **intent vs taste**:
|
|
48
|
+
- **Answerable from repo/docs** → resolve it yourself, cite where you got the answer
|
|
49
|
+
(file path) in your own notes, move on.
|
|
50
|
+
- **INTENT gap** (what problem, for whom, what data, which business rule — a choice only
|
|
51
|
+
a human/parent can make, where the wrong guess builds the wrong thing) → this is a STUCK
|
|
52
|
+
condition. Do NOT guess. Do NOT invent a requirement to fill the gap. Go to the STUCK
|
|
53
|
+
path in Outcome protocol below, listing the exact questions. Do not write a partial
|
|
54
|
+
`prd.md` first.
|
|
55
|
+
- **TASTE gap** (UX feel, a default value, a name, edge-case behavior — anything where any
|
|
56
|
+
reasonable choice is fine and only a human's *preference* is missing) → do NOT go STUCK.
|
|
57
|
+
Decide it opinionatedly, commit to ONE way, and record the choice in `prd.md` under a
|
|
58
|
+
`## Decisions` heading (one line: what you chose and why). Never write a both-ways or
|
|
59
|
+
configurable hedge, and never ask about it. Decisiveness reads as quality; hedging reads
|
|
60
|
+
as a demo.
|
|
61
|
+
7. Estimate the work. Ask: "can one implementer finish this in ~2 days, and is it a single
|
|
62
|
+
shippable thing?"
|
|
63
|
+
- If YES to both → continue to step 8, write one `prd.md`.
|
|
64
|
+
- If NO to either (>~2 days OR it bundles independently shippable deliverables) → go to
|
|
65
|
+
the **Split procedure** below instead of writing a single PRD.
|
|
66
|
+
8. Write `TICKET_DIR/prd.md` (see Required PRD structure below). If this is a bounce
|
|
67
|
+
revision (design reject reason in history and/or `triage-notes.md`), add a
|
|
68
|
+
`## Revision notes` heading at the top listing each reason from both sources and how you
|
|
69
|
+
addressed it.
|
|
70
|
+
9. `fts renew`.
|
|
71
|
+
10. Run the GATE checklist. Fix anything unchecked before proceeding.
|
|
72
|
+
11. Follow Outcome protocol.
|
|
73
|
+
|
|
74
|
+
## Required PRD structure (`TICKET_DIR/prd.md`)
|
|
75
|
+
|
|
76
|
+
```markdown
|
|
77
|
+
# <ticket title>
|
|
78
|
+
|
|
79
|
+
[## Revision notes <- only on a bounce-back revision
|
|
80
|
+
- <triage reason 1> -> <what you changed>
|
|
81
|
+
- <triage reason 2> -> <what you changed>
|
|
82
|
+
]
|
|
83
|
+
|
|
84
|
+
## Problem
|
|
85
|
+
<2-4 sentences. State what is wrong or missing today and why it matters. NO solution
|
|
86
|
+
language — no mention of endpoints, functions, UI components, or "we will build X".>
|
|
87
|
+
|
|
88
|
+
## Goals
|
|
89
|
+
- <outcome-oriented bullet>
|
|
90
|
+
- <outcome-oriented bullet>
|
|
91
|
+
|
|
92
|
+
## Non-goals
|
|
93
|
+
- <at least 2 bullets. Cutting scope is your main job — name things a reader would
|
|
94
|
+
reasonably assume are included, and explicitly exclude them.>
|
|
95
|
+
|
|
96
|
+
[## Decisions <- only when you resolved a TASTE gap (step 6)
|
|
97
|
+
- <the choice you made> — <one-line why>
|
|
98
|
+
]
|
|
99
|
+
|
|
100
|
+
## First three minutes
|
|
101
|
+
<5-8 sentences narrating what a user does in their first three minutes with the delivered
|
|
102
|
+
feature and what they SEE at each step, in order. Concrete and sequential, present-tense,
|
|
103
|
+
grounded in the seeded/starting state. This is the definition of "finished": qa replays it
|
|
104
|
+
against the running product as a driven task, and a stall in it is a reject. Write the
|
|
105
|
+
happy path a real first-time user walks — not a feature list.>
|
|
106
|
+
|
|
107
|
+
## Verbs
|
|
108
|
+
<Every user-facing verb the feature involves, one per line — the actions and feedback a user
|
|
109
|
+
experiences in a full first session (e.g. "create a ticket", "filter the list", "mark a
|
|
110
|
+
ticket paid", "see a success toast"). Each verb MUST appear in some AC below; a verb with no
|
|
111
|
+
AC is either dead scope to cut or a missing requirement to add.>
|
|
112
|
+
- <verb>
|
|
113
|
+
- <verb>
|
|
114
|
+
|
|
115
|
+
## Functional requirements
|
|
116
|
+
|
|
117
|
+
### FR1: <name>
|
|
118
|
+
- AC1: <falsifiable, black-box-testable, observable-behavior statement>
|
|
119
|
+
- AC2: ...
|
|
120
|
+
|
|
121
|
+
### FR2: <name>
|
|
122
|
+
- AC1: ...
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### AC quality bar
|
|
126
|
+
|
|
127
|
+
Every AC must pass all three checks:
|
|
128
|
+
1. **Independently testable** — a black-box tester with no code access could verify it by
|
|
129
|
+
exercising the system.
|
|
130
|
+
2. **Observable behavior, not implementation** — describes inputs/outputs/state changes,
|
|
131
|
+
never a class name, function name, middleware, or library.
|
|
132
|
+
3. **Falsifiable** — has a clear pass/fail, not a vague adjective ("fast", "robust",
|
|
133
|
+
"user-friendly").
|
|
134
|
+
|
|
135
|
+
Good AC: `AC1: When a user submits a ticket with an empty "title" field, the API responds
|
|
136
|
+
with HTTP 422 and a body containing "title is required".`
|
|
137
|
+
|
|
138
|
+
Bad AC: `AC1: Use validation middleware to check the title field.` (names implementation,
|
|
139
|
+
not observable behavior, not falsifiable as written.)
|
|
140
|
+
|
|
141
|
+
## Split procedure (only when step 7 says split)
|
|
142
|
+
|
|
143
|
+
1. Decide the slices. Each child is one independently shippable deliverable, or one
|
|
144
|
+
coherent ≤2-day chunk. The **parent ticket becomes the last slice** — the integration
|
|
145
|
+
piece that ties children together (or simply the smallest remaining piece if there is
|
|
146
|
+
no integration work).
|
|
147
|
+
2. For each child, `mkdir` its folder first, then seed it:
|
|
148
|
+
```bash
|
|
149
|
+
mkdir -p "<parent TICKET_DIR>/../N-<slug>"
|
|
150
|
+
```
|
|
151
|
+
Write that child's own `request.md` slice into
|
|
152
|
+
`<parent TICKET_DIR>/../N-<slug>/request.md` — a focused excerpt of the original
|
|
153
|
+
request scoped to just that slice. Do not put a `prd.md` there; the child's own groom
|
|
154
|
+
worker will write it.
|
|
155
|
+
3. Create the child ticket:
|
|
156
|
+
```bash
|
|
157
|
+
fts create --db "$FTS_DB" --title "<child title>" --stage groom \
|
|
158
|
+
--folder "<parent TICKET_DIR>/../N-<slug>"
|
|
159
|
+
```
|
|
160
|
+
4. Wire the dependency so the dependent doesn't start early:
|
|
161
|
+
```bash
|
|
162
|
+
fts add-dep --db "$FTS_DB" --ticket <dependent-id> --depends-on <prerequisite-id> \
|
|
163
|
+
--until <stage>
|
|
164
|
+
```
|
|
165
|
+
Choose `--until`:
|
|
166
|
+
- `review` — the usual choice. The dependent can start once the prerequisite's code
|
|
167
|
+
has been reviewed (its shape is stable), even before it's fully QA'd/merged.
|
|
168
|
+
- `qa` — only when the dependent needs the prerequisite's feature actually *running* to
|
|
169
|
+
build or test against (e.g. it calls a live endpoint the prerequisite adds).
|
|
170
|
+
5. Repeat for every child, in dependency order.
|
|
171
|
+
6. Rewrite the parent's own scope down to its final slice, then write `TICKET_DIR/prd.md`
|
|
172
|
+
for that remaining slice following the Required PRD structure above. The parent's PRD
|
|
173
|
+
must NOT re-describe the children's scope — reference their ticket IDs instead.
|
|
174
|
+
7. Continue to the GATE checklist for the parent's own (now-reduced) PRD.
|
|
175
|
+
|
|
176
|
+
## GATE checklist
|
|
177
|
+
|
|
178
|
+
Check every box with concrete evidence — do not check a box because it "should" be true.
|
|
179
|
+
|
|
180
|
+
- [ ] `TICKET_DIR/prd.md` exists and I printed/read it back after writing.
|
|
181
|
+
- [ ] Problem section has 2-4 sentences and contains zero solution language (no
|
|
182
|
+
component/function/endpoint names).
|
|
183
|
+
- [ ] Goals section is present and non-empty.
|
|
184
|
+
- [ ] Non-goals section has at least 2 bullets.
|
|
185
|
+
- [ ] `## First three minutes` section exists with 5-8 concrete, sequential sentences a qa
|
|
186
|
+
worker could replay against the running product.
|
|
187
|
+
- [ ] `## Verbs` section lists every user-facing verb, and I confirmed EACH verb appears in
|
|
188
|
+
at least one AC (walk the list; a verb with no AC gets cut or given an AC).
|
|
189
|
+
- [ ] Every TASTE gap I resolved (step 6) is recorded in a `## Decisions` line; no both-ways
|
|
190
|
+
or configurable hedge was written to dodge a decision.
|
|
191
|
+
- [ ] Every FR has at least one AC, and every AC is numbered ACn under its FR.
|
|
192
|
+
- [ ] I re-read each AC against the 3-point quality bar (testable, observable, falsifiable)
|
|
193
|
+
and rewrote any that failed.
|
|
194
|
+
- [ ] If this was a bounce revision (design reject reason in history and/or
|
|
195
|
+
`triage-notes.md`), `prd.md` has a `## Revision notes` section addressing every reason
|
|
196
|
+
from both sources by name.
|
|
197
|
+
- [ ] If I split: every child folder exists (`ls` confirms it), every child has a seeded
|
|
198
|
+
`request.md`, every `fts create` and `fts add-dep` call returned success, and the
|
|
199
|
+
parent's `prd.md` covers only its own remaining slice.
|
|
200
|
+
- [ ] No requirement in `prd.md` was invented to paper over an unanswerable question —
|
|
201
|
+
any such question went the STUCK route instead.
|
|
202
|
+
- [ ] I ran `fts renew` after each substantial step and none failed.
|
|
203
|
+
|
|
204
|
+
If any box is unchecked, fix it before calling `fts complete`. Do not rationalize a
|
|
205
|
+
skipped box.
|
|
206
|
+
|
|
207
|
+
## Outcome protocol
|
|
208
|
+
|
|
209
|
+
- **PASS** (single PRD, gate fully checked):
|
|
210
|
+
```bash
|
|
211
|
+
fts complete --db "$FTS_DB" --ticket "$TICKET_ID" --worker "$WORKER_ID" --epoch "$EPOCH" \
|
|
212
|
+
--note "prd.md written: N FRs, M ACs"
|
|
213
|
+
fts advance --db "$FTS_DB" --ticket "$TICKET_ID" --worker "$WORKER_ID" \
|
|
214
|
+
--to-stage "$NEXT_STAGE" --note "ready for design"
|
|
215
|
+
```
|
|
216
|
+
- **PASS after split** (parent's own reduced PRD gate fully checked, all children created):
|
|
217
|
+
same two commands as above, note should mention the child ticket IDs created.
|
|
218
|
+
- **Groom never rejects.** `BOUNCE_STAGE` for groom is `-`. There is no reject path at this
|
|
219
|
+
stage — if the request is unworkable, that is STUCK, not reject.
|
|
220
|
+
- **STUCK** (unanswerable question, broken environment, contradictory request): do NOT
|
|
221
|
+
`fts complete`, do NOT write a partial `prd.md`. Mail your parent:
|
|
222
|
+
```bash
|
|
223
|
+
~/.ftown/ftown-sessions tell --parent --type result \
|
|
224
|
+
"ticket $TICKET_ID groom: STUCK — <exact question 1>; <exact question 2>"
|
|
225
|
+
```
|
|
226
|
+
Then exit. Your claim expires and the ticket re-queues.
|
|
227
|
+
|
|
228
|
+
## Failure modes table
|
|
229
|
+
|
|
230
|
+
| Symptom | Exact action |
|
|
231
|
+
|---|---|
|
|
232
|
+
| `fts start` fails | STOP immediately, do not read further, exit. You do not own this ticket. |
|
|
233
|
+
| `request.md` missing or empty | STUCK. Mail parent: "request.md missing/empty, cannot groom." Exit. |
|
|
234
|
+
| Bounce reason present (design reject in history or `triage-notes.md`) but you wrote prd.md without a Revision notes section | Stop, add the section addressing every reason, re-run the GATE checklist before completing. |
|
|
235
|
+
| A requirement's INTENT is genuinely unclear after reading repo/docs (what/for-whom/what-data) | STUCK — list the exact question(s), do not guess, do not write a placeholder AC. |
|
|
236
|
+
| A TASTE point is unspecified (a default, a name, a UX feel, an edge-case) | Do NOT go STUCK. Decide it, build one way, record it under `## Decisions`. Bad: add a config flag "to be safe". Good: pick the sensible default and write down why. |
|
|
237
|
+
| A verb in `## Verbs` maps to no AC | Either add an AC that exercises it, or cut the verb as out of scope. Do not ship a verb the ACs never test. |
|
|
238
|
+
| Estimate is borderline (~2 days, unsure) | Default to NOT splitting only if it is a single shippable unit; if it bundles 2+ shippable deliverables, split regardless of size. |
|
|
239
|
+
| `fts renew` fails with `ClaimExpired`/`ClaimFenced`/`NotClaimOwner` | STOP writing immediately. Do not finish the current file. Mail parent you were fenced. Exit. |
|
|
240
|
+
| You wrote an AC that names a function/class/endpoint | Rewrite it to describe observable input/output behavior only, before the GATE check. |
|
|
241
|
+
| Split child folder already exists from a prior partial attempt | Reuse it; do not create a duplicate ticket. Verify its `request.md` slice is correct before calling `fts create` again (skip create if a ticket already exists for that folder). |
|
|
242
|
+
| Same blocker on 2nd attempt at the same step | Your assumption is wrong. Re-read `request.md`, `triage-notes.md`, and this file before trying a third approach; if still stuck, go STUCK. |
|
|
243
|
+
| Tempted to write `prd.md` twice (draft then final) | Don't. Do all thinking first, write the file once, then read it back for the GATE check. |
|