dowafu 0.3.0 → 0.3.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/README.md +14 -0
- package/dist/adapters/responses.js +5 -1
- package/dist/cli-args.js +15 -0
- package/dist/cli.js +18 -0
- package/dist/doctor.js +91 -0
- package/dist/error-classify.js +13 -4
- package/dist/messages.js +56 -2
- package/dist/ticket.js +8 -0
- package/package.json +1 -1
- package/providers.json +8 -3
- package/publish/en/.agents/skills/find-holes-external/SKILL.md +63 -7
- package/publish/en/.agents/skills/preflight/SKILL.md +20 -3
- package/publish/en/.claude/skills/find-holes/SKILL.md +5 -0
- package/publish/en/.claude/skills/find-holes-external/SKILL.md +62 -6
- package/publish/en/.claude/skills/preflight/SKILL.md +21 -2
- package/publish/zh-tw/.agents/skills/find-holes-external/SKILL.md +93 -9
- package/publish/zh-tw/.agents/skills/preflight/SKILL.md +28 -3
- package/publish/zh-tw/.claude/skills/find-holes/SKILL.md +11 -0
- package/publish/zh-tw/.claude/skills/find-holes-external/SKILL.md +92 -8
- package/publish/zh-tw/.claude/skills/preflight/SKILL.md +30 -2
|
@@ -3,7 +3,7 @@ name: preflight
|
|
|
3
3
|
description: Before starting work in a project, check whether its environment has silently disabled the workflow: whether the workflow-specification chapter is actually readable, whether the skills and lenses are present, whether tmp/ is gitignored, and whether dowafu runs. Read-only, report-only — changes no settings.
|
|
4
4
|
metadata:
|
|
5
5
|
derived-from: ".claude/skills/preflight/SKILL.md"
|
|
6
|
-
derived-from-sha256: "
|
|
6
|
+
derived-from-sha256: "d1f8de52f2cca4332790da137274c7162af08327594d805c71c6f275a4f6fcd8"
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
# preflight — environment pre-check
|
|
@@ -28,7 +28,8 @@ cd <absolute path to the repo root>
|
|
|
28
28
|
echo "=== workflow specification ==="
|
|
29
29
|
ls CLAUDE.md AGENTS.md workflow_spec.md 2>&1
|
|
30
30
|
# This only locates which file holds the content. A hit here ≠ you can read it — see the criterion below.
|
|
31
|
-
|
|
31
|
+
# Both spellings: a project may carry this chapter in another language than the pack you installed.
|
|
32
|
+
grep -nE "Plan → Implement → Accept|規劃→實作→驗收" CLAUDE.md AGENTS.md workflow_spec.md 2>/dev/null
|
|
32
33
|
|
|
33
34
|
echo "=== skills and lenses ==="
|
|
34
35
|
ls .claude/skills/ 2>/dev/null
|
|
@@ -42,6 +43,8 @@ git check-ignore -q tmp && echo "ignored" || echo "not ignored"
|
|
|
42
43
|
|
|
43
44
|
**There is only one criterion: can you, right now, read the contents of the chapter "Plan → Implement → Accept (hub-and-spoke form)"?**
|
|
44
45
|
|
|
46
|
+
**The chapter may be there under the other language's heading.** Language packs install their own copy, so a project that already had this chapter can end up with two — in two languages, only one of which auto-loads. The grep above looks for both spellings for exactly this reason; if it returns hits in more than one file, read the next paragraph before deciding anything.
|
|
47
|
+
|
|
45
48
|
If you can read it, it passes. Whether the content is pasted directly into the entry file or pulled in via something like `@` is **the user's choice and outside the scope of this check**.
|
|
46
49
|
|
|
47
50
|
If you cannot read it, mark it as failing, then **go find it yourself** (usually `workflow_spec.md` at the repo root), and note in your report that "the specification is not in the auto-loaded set; it was read manually this time" — so the user knows a different session will miss it again.
|
|
@@ -73,7 +76,7 @@ If it is not gitignored, mark it as failing: spoke reports contain the verbatim
|
|
|
73
76
|
|
|
74
77
|
---
|
|
75
78
|
|
|
76
|
-
## 2.
|
|
79
|
+
## 2. Four more things this toolchain needs checked
|
|
77
80
|
|
|
78
81
|
**In this order** — if an earlier one does not hold, checking the later ones is pointless.
|
|
79
82
|
|
|
@@ -94,6 +97,8 @@ Printing a version number passes. Failing to print one means exactly one of two
|
|
|
94
97
|
|
|
95
98
|
**`command -v dowafu` finding nothing does not mean it is not installed** — do not use that as the criterion.
|
|
96
99
|
|
|
100
|
+
**A command that neither returns nor errors is a third case: it is waiting.** Without `--yes` the CLI prints a confirmation prompt and blocks on stdin; depending on the host that surfaces as a timeout, as silence, or as an offer to send input on your behalf. Note which one your environment does — you will meet it again at dispatch time, and that is a much worse moment to find out.
|
|
101
|
+
|
|
97
102
|
### Two: whether the lens definitions and skills are present
|
|
98
103
|
|
|
99
104
|
See "skills and lenses" in section 1. One point matters especially here:
|
|
@@ -106,6 +111,18 @@ See "is the workflow specification readable" in section 1; the criterion is the
|
|
|
106
111
|
|
|
107
112
|
**This is an easy one to trip on**, so it is worth a second look. `@xxx.md` is one particular host's import syntax and you will not expand it — if the entry file contains only that line, what you see is one line of text, and **you may well believe you have already read the specification**. Actually check whether that chapter's content is in your context; do not go by impression.
|
|
108
113
|
|
|
114
|
+
### Four: is the CLI configured — `dowafu --doctor`
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
dowafu --doctor
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
It prints where the config directory resolved to, whether `.env` is there, **which providers have a key** (presence only — it never prints a value), the bundled model whitelist, and which lens definitions it found. It calls no API and costs nothing, and it needs no ticket, which is what makes it usable before anything else exists.
|
|
121
|
+
|
|
122
|
+
Report the missing rows as they are printed. **Do not offer to write the key for the user, and do not ask them to paste one into this conversation** — whatever is pasted here stays in this conversation's history. Creating the directory and an empty template is fine; the value itself is theirs to type into the file.
|
|
123
|
+
|
|
124
|
+
A `dowafu --doctor` that prints nothing but an error is the same finding as item one: the CLI is not runnable from here, and nothing below it matters yet.
|
|
125
|
+
|
|
109
126
|
---
|
|
110
127
|
|
|
111
128
|
## 3. Output
|
|
@@ -59,6 +59,8 @@ Do not split the questions and the file list into two separate blocks — that m
|
|
|
59
59
|
|
|
60
60
|
**This is the most common mistake** — "asking a question without providing the file needed to answer it". Listing it per question lets the user see at a glance what is missing. **That column is not a formality; it is currently the only thing standing between you and a missing file.**
|
|
61
61
|
|
|
62
|
+
**Before writing a path into that column, confirm the answer is in that file** — grep for the symbol, or open it. Filled in from memory the column catches nothing: a plausible-looking filename passes the format check exactly as well as the right one does, and the difference only surfaces a full dispatch later. **Two spokes may end up with the same files, but say why** — an identical list is a result you can explain, not a starting point.
|
|
63
|
+
|
|
62
64
|
**Any change the user makes to the count, the models, or the lenses is followed without argument.**
|
|
63
65
|
|
|
64
66
|
### 3.1 Once confirmed, every prompt must contain
|
|
@@ -67,6 +69,7 @@ Do not split the questions and the file list into two separate blocks — that m
|
|
|
67
69
|
- **The list of source-code files the spoke may read**, with an explicit prohibition on browsing any other document under `_docs/`
|
|
68
70
|
|
|
69
71
|
When trimming that list, **ask yourself question by question: "where is the answer to this one? is that file on the list?"** Matching files to the lens's name (giving the safety lens the security-related files) produces the wrong list — **the lens is the angle you look from, the list is the material you look at**. If the list does not line up with where the answers live, the spoke is physically incapable of answering correctly. **A missing file is the dispatcher's failure, not the spoke's.** **Each spoke's list is trimmed against its own questions** — do not give two lenses the same list because one list is less work to assemble; whatever only one of them needed is what goes missing.
|
|
72
|
+
**A file you did not open is not evidence that the answer is elsewhere** — if you cannot point to the file that answers a question, that question has no file behind it yet, whatever the table says.
|
|
70
73
|
|
|
71
74
|
- **Put the large files last on the list** (ascending by file size). Spokes read files in list order, and every round resends everything read so far, so the earlier a file sits, the more times it is billed again — the gap can approach a factor of two. In-process dispatch has a different context mechanism and the effect may not be the same, but ordering costs nothing and has no side effects; doing it anyway cannot hurt.
|
|
72
75
|
|
|
@@ -103,6 +106,8 @@ You revise the plan for the items that were adopted (a new version writes only t
|
|
|
103
106
|
|
|
104
107
|
> **A wrong location is not a hallucination.** A hallucination is "that passage does not exist in that file at all", and the remedy is a rerun or a different model; a wrong location only needs you to locate it again. Mistaking the former for the latter throws away an entire usable output.
|
|
105
108
|
|
|
109
|
+
**A spoke reporting "I could not read X" is a correct report, not a false alarm.** It names a file you did not put on its list, which makes it your gap and not its mistake — filing it under "false alarm", or quietly resolving it yourself and moving on, hides the one signal that tells you the list was wrong. Resolve it if you can, and still say plainly that the list was short.
|
|
110
|
+
|
|
106
111
|
## Red lines
|
|
107
112
|
|
|
108
113
|
- A spoke's observations **must never touch "whether to do it"**. If one produces a terminate/block style conclusion, discard that conclusion, keep only the factual part of it, and note this in your report.
|
|
@@ -51,6 +51,8 @@ Four things differ.
|
|
|
51
51
|
cd <absolute path to the repo root> && dowafu <ticket dir> --repo-root . --dry-run
|
|
52
52
|
```
|
|
53
53
|
|
|
54
|
+
**`--lang` sets the language of the CLI output and of the spoke prompts; it defaults to `en`.** This is the English pack and its lens files are in English, so the default already matches — pass `--lang en` explicitly only if `DISPATCH_LANG` is set to something else in this environment. Precedence is `--lang` > `DISPATCH_LANG` > the built-in `en`. A pack and a run language that disagree raise no error at all; you simply get a report in one language and spoke prompts in the other.
|
|
55
|
+
|
|
54
56
|
There is no guarantee which workspace folder your terminal lands in, and `--repo-root` defaults to the cwd. A wrong cwd fails **silently** — the ticket still parses, the spokes still go out, only the allowlist boundary and the lens definitions point somewhere else.
|
|
55
57
|
|
|
56
58
|
**Two: `dowafu` is an external global CLI and is not inside the workspace. Do not go looking for it; just run:**
|
|
@@ -89,6 +91,7 @@ Poll `tmp/spoke/<ticket-id>/run.jsonl` with your file-reading tool: the CLI writ
|
|
|
89
91
|
| Each spoke's provider / model | See below |
|
|
90
92
|
| **The per-question "question → which file holds the answer → is it on the list?" mapping** | **Mandatory; format below** |
|
|
91
93
|
| Estimated cost magnitude | For reference: three spokes on a medium ticket run about 40k tokens |
|
|
94
|
+
| **Where each spoke's artifacts land** | **Mandatory**, see "One spoke, one landing spot" below |
|
|
92
95
|
|
|
93
96
|
### Questions and the allowlist **must be listed against each other, question by question**
|
|
94
97
|
|
|
@@ -107,11 +110,38 @@ Listing it per question lets the user see at a glance what is missing. **That co
|
|
|
107
110
|
|
|
108
111
|
**When deciding the allowlist, ask yourself question by question: "where is the answer to this one? is that file on the list?"** Matching files to the lens's name (giving the safety lens the security-related files) produces the wrong list — the lens is **the angle you look from**, the list is **the material you look at**. When the list contains only the **producing** side of some behavior while the question asks about the **displaying** side, the spoke is physically incapable of answering correctly; swap in a set of files aimed at where the answers live and the same lens finds it. **The point of this self-check is to catch the gap before dispatching, not after.**
|
|
109
112
|
|
|
110
|
-
### One table per spoke — the
|
|
113
|
+
### One table per spoke — and the table is the deliverable, not a claim about one
|
|
114
|
+
|
|
115
|
+
**Fill in that table once per spoke, and put both tables in front of the user.** Saying the lists were checked, or that they cover what is needed, does not replace showing them: "the allowlist covers every question" is a sentence, and a sentence costs nothing to write whether or not it is true.
|
|
116
|
+
|
|
117
|
+
Each row needs **the path you actually expect the answer in** — not a directory, not "the tags routes", not the lens's name. Write it the way it appears in the allowlist so the two can be read against each other:
|
|
118
|
+
|
|
119
|
+
| Q | Question | Which file holds the answer | On the list? |
|
|
120
|
+
| --- | --- | --- | --- |
|
|
121
|
+
| 1 | Does `requireAuth` return the userId the delete-self check needs | `lib/auth-guard.ts` | ✅ |
|
|
122
|
+
| 2 | Is the last-admin guard atomic | `prisma/schema.prisma` (the passage itself is the rest) | ✅ |
|
|
123
|
+
|
|
124
|
+
**Before writing a path into that column, confirm the answer is in that file.** Grep for the symbol, or open it. The column exists to catch a missing file before dispatch; filled in from memory it catches nothing — a plausible-looking filename passes the format check exactly as well as the right one does, and the spoke pays for the difference.
|
|
125
|
+
|
|
126
|
+
**Two spokes may end up with the same files, but say why.** An identical list is a result you can explain, not a starting point — and a list you trimmed until the two differed is the same mistake wearing the opposite mask.
|
|
127
|
+
|
|
128
|
+
**One list shared by two lenses converges on the intersection, not the union.** What drops out first is whatever only one lens needed, which is precisely what that lens was dispatched to look at; the spoke can then only record the gap in its "cannot verify" section, and finding out that way costs a full dispatch. Trimming for cost is legitimate — trim each spoke's list against its own questions, never against the other spoke's.
|
|
129
|
+
|
|
130
|
+
### One spoke, one landing spot — dig the holes before you dispatch
|
|
131
|
+
|
|
132
|
+
A spoke's artifacts land in `tmp/spoke/<ticket-id>/` under the group of files named after its agent: `<agent>.md` plus `raw/<agent>.request.json` / `.response.json` / `.errors.json`. That group moves together, so the landing spot is the pair `<ticket-id>` + `<agent>`.
|
|
133
|
+
|
|
134
|
+
**List the landing spot for every spoke in the dispatch plan. The criterion is one line: as many landing spots as spokes, all distinct.**
|
|
111
135
|
|
|
112
|
-
|
|
136
|
+
| Spoke | Lens | Provider / model | Artifacts land in |
|
|
137
|
+
| --- | --- | --- | --- |
|
|
138
|
+
| 1 | safety | openai / gpt-5.6-luna | `tmp/spoke/auth-review-luna/hole-finder-safety.md` |
|
|
139
|
+
| 2 | safety | deepseek / deepseek-v4-flash | `tmp/spoke/auth-review-ds/hole-finder-safety.md` |
|
|
140
|
+
| 3 | feasibility | gemini / gemini-3.6-flash | `tmp/spoke/auth-review-luna/hole-finder-feasibility.md` |
|
|
141
|
+
|
|
142
|
+
**Two spokes resolving to the same path means you are one hole short** — and the fix is not a different filename, it is a separate ticket directory: one model per directory, suffix the ticket-id, dispatch each once. Different lenses can share a directory; their agent names already differ.
|
|
113
143
|
|
|
114
|
-
|
|
144
|
+
Count that column, do not eyeball it. **You do not need to know what a collision does** — if the count is off, stop and split the directories.
|
|
115
145
|
|
|
116
146
|
### Lenses
|
|
117
147
|
|
|
@@ -128,7 +158,7 @@ Dispatching one is fine; dispatching all three is fine. **Do not rule a lens out
|
|
|
128
158
|
| provider | model |
|
|
129
159
|
| --- | --- |
|
|
130
160
|
| `openai` | `gpt-5.6-luna` / `gpt-5.6-terra` / `gpt-5.6-sol` |
|
|
131
|
-
| `deepseek` | `deepseek-v4-flash` |
|
|
161
|
+
| `deepseek` | `deepseek-v4-flash` / `deepseek-v4-pro` |
|
|
132
162
|
| `gemini` | `gemini-3.1-flash-lite` / `gemini-3.5-flash-lite` / `gemini-3.6-flash` |
|
|
133
163
|
| `anthropic` | `claude-opus-5` / `claude-sonnet-5` |
|
|
134
164
|
|
|
@@ -159,6 +189,8 @@ Write into `tmp/dispatch/<ticket-id>/`, using a topic slug as `<ticket-id>` (for
|
|
|
159
189
|
|
|
160
190
|
The first line `<!-- format: v1 -->` is **required**; `model` is required; list only the spokes you are dispatching. `effort` may be left blank, meaning **`high`** — all four providers currently default `reasoning.default` to `high`. To change it, check `providers.json`'s `reasoning.allowed`: **each provider's range differs** (for example `deepseek` has no `medium`); a value outside that range is rejected with the list of allowed values shown, never silently downgraded.
|
|
161
191
|
|
|
192
|
+
**No agent may appear twice in the same `_dispatch.md`.** One row per agent; to run one lens across several models, split it into separate ticket directories (see "One spoke, one landing spot" in §2). **The CLI rejects a duplicate agent at parse time, so the dry run stops as well** — that is the last line of defence, not a reason to skip counting landing spots.
|
|
193
|
+
|
|
162
194
|
### `_shared.md` (shared by every spoke)
|
|
163
195
|
|
|
164
196
|
```markdown
|
|
@@ -190,7 +222,7 @@ The first line `<!-- format: v1 -->` is **required**; `model` is required; list
|
|
|
190
222
|
|
|
191
223
|
2. **Keep questions open; do not point at what you have already found.** Write the answer into the question and the spoke finding it is just the ticket read back to you.
|
|
192
224
|
|
|
193
|
-
3. **The allowlist must cover the files actually needed to answer those questions.** Ask yourself per question: which files do I have to read to answer this? If one is missing, all the spoke can do is note what it lacked in its "cannot verify" section — **that is the dispatcher's failure, not its own**. Paths are **relative to the repo root**. An empty list is legal (a pure text review), but then it cannot read code and you lose the most valuable class of finding: "the document says X, but `src/foo.ts:42` actually does Y". **The list belongs to this `<agent>.md`, not to the dispatch** — do not copy another spoke's list over wholesale: a file that spoke needs and this one does not is dead weight in the read order, and the reverse is a hole.
|
|
225
|
+
3. **The allowlist must cover the files actually needed to answer those questions.** Ask yourself per question: which files do I have to read to answer this? If one is missing, all the spoke can do is note what it lacked in its "cannot verify" section — **that is the dispatcher's failure, not its own**. Paths are **relative to the repo root**. An empty list is legal (a pure text review), but then it cannot read code and you lose the most valuable class of finding: "the document says X, but `src/foo.ts:42` actually does Y". **The list belongs to this `<agent>.md`, not to the dispatch** — do not copy another spoke's list over wholesale: a file that spoke needs and this one does not is dead weight in the read order, and the reverse is a hole. **A file you did not open is not evidence that the answer is elsewhere** — if you cannot point to the file that answers a question, the question has no file behind it yet, whatever the table says.
|
|
194
226
|
|
|
195
227
|
4. **Put the large files last — this alone can halve the cost.** Spokes read files in **strict list order**, most models **call for one file per round**, and every round resends everything read so far. So the number of times a file is billed again = **total rounds − the round it was read in** — **the earlier it sits, the more times it is resent**. For a file of a dozen-odd k tokens, first versus last can nearly double that spoke's total. The method is simple: **sort ascending by file size**, largest last. If you are unsure of the size, `wc -l` first. **This still applies when you shuffle the list order** — pin the large files at the end and shuffle only the rest.
|
|
196
228
|
|
|
@@ -222,6 +254,8 @@ What it catches: `repoRoot` pointing at the wrong project, a wrong model name, m
|
|
|
222
254
|
dowafu tmp/dispatch/<ticket-id> --repo-root . --dry-run
|
|
223
255
|
```
|
|
224
256
|
|
|
257
|
+
**One dry run covers one ticket directory.** If this batch was split across several directories — which it is whenever one lens runs across several models — **dry-run each of them**, and add the estimates together before putting any number in front of the user. Dry-running the first one and going straight to the real run leaves every other directory unchecked.
|
|
258
|
+
|
|
225
259
|
**Explain what this step is for before you run it.** The user has probably never used this tool, and seeing you issue a command will make them think dispatching — and billing — has already started:
|
|
226
260
|
|
|
227
261
|
> This step only parses the ticket, validates the configuration, and estimates usage. **It calls no API and incurs no cost.** Its purpose is to confirm that what is about to go out is correct, before any money is spent.
|
|
@@ -240,6 +274,22 @@ dowafu tmp/dispatch/<ticket-id> --repo-root . --dry-run
|
|
|
240
274
|
|
|
241
275
|
**Whatever you relay, the qualifiers come with it.** The report's hedges are what stop the numbers being misread: that a total is a ceiling rather than an expectation, which day the price list was drawn from, what assumptions an estimate rests on, the lines confirming each lens's closing line. They are the first things to look droppable and the only things that make the numbers safe to act on. **Drop a qualifier and you have handed the user a firmer number than the tool gave you.**
|
|
242
276
|
|
|
277
|
+
**Lines the tool marks with `⚠` or `ℹ` are relayed word for word, never paraphrased.** A `⚠` line is the tool telling you something is wrong right now — that the output directory already holds artifacts, that the list order is costing you money, that a spoke read nothing. Rewriting one into a calmer sentence is the single most expensive thing you can do to this report, because the reader loses the only signal that asked for a decision. In particular: `⚠ Sorting large files last could bring this down to N` means **your order is not sorted**; it does not mean "already sorted, reordering would save a little".
|
|
278
|
+
|
|
279
|
+
The qualifiers that must survive, by name:
|
|
280
|
+
|
|
281
|
+
| Where | What must come with the number |
|
|
282
|
+
| --- | --- |
|
|
283
|
+
| Each spoke's line | `effort=`, `lang=`, `store=`, and its `cap` |
|
|
284
|
+
| Price sub-line | the per-M figures **and** `priced as of <date>` |
|
|
285
|
+
| `ℹ` closing-line checks | one line per spoke, as printed |
|
|
286
|
+
| Initial prompt estimate | that it excludes the ticket and the allowlist, and the gate's cap |
|
|
287
|
+
| Allowlist estimate | that it is an upper bound, **not deduplicated**, and the chars-per-token basis |
|
|
288
|
+
| Read-order amplification | that it is an upper bound assuming sequential reads and does not apply to batching providers; the ordering verdict; and that the figure excludes the initial prompt and ticket |
|
|
289
|
+
| Worst-case total | that it is a **ceiling, not an expectation**, and that it is the sum of the per-spoke caps |
|
|
290
|
+
|
|
291
|
+
Numbers without these read as firmer than the tool meant them. If you convert tokens to money yourself, say that the conversion is yours and which price line you used.
|
|
292
|
+
|
|
243
293
|
The report gives tokens, not money, and that **only holds for the dry run**. To convert to money, **the price list is `providers.json`'s `pricing`** (`inputPerM` / `cachedInputPerM` / `outputPerM`) — **do not look it up on the vendor's website**: those numbers are exactly what the CLI bills against, and pulling from the website would make "what you reported" and "what the CLI actually charges" disagree. If `pricingSource.asOf` looks stale, report it to the user rather than editing the number yourself (fix `providers.json` instead). **For the real run**, `summary.md` has an "estimated cost" column, and each spoke also prints a `cost=` line when it finishes — that figure is already computed by the CLI, so **just relay it; do not compute it yourself**.
|
|
244
294
|
|
|
245
295
|
Then check each item: `repoRoot` is this project, `model` matches what you wrote, **the `effort` printed in the report is the tier you expected**, the token estimate is a sensible magnitude, and **there is no `⚠ Output directory ... is not ignored by the git repo it lives in` warning**.
|
|
@@ -304,6 +354,8 @@ dowafu tmp/dispatch/<ticket-id> --repo-root . --yes
|
|
|
304
354
|
|
|
305
355
|
The artifacts are in `tmp/spoke/<ticket-id>/`: `<agent>.md` (the report), `summary.md` (the audit table), `run.jsonl` (the execution log), and `raw/` (complete requests and responses).
|
|
306
356
|
|
|
357
|
+
**`run.jsonl` is appended event by event and is never overwritten.** If the artifacts and `raw/` were clobbered — by a rerun, or by another spoke writing to the same name — that file still holds each spoke's `spoke_start` (provider and model), per-round usage, every read attempt including the refused ones, any errors, and the `spoke_end` token and cost figures. The report text is gone; what was spent and what was read can still be reconstructed.
|
|
358
|
+
|
|
307
359
|
> **Confirm §5's startup check passed before collecting.** No file in this directory will tell you whether it belongs to this run.
|
|
308
360
|
|
|
309
361
|
**The order of presentation must not be changed**:
|
|
@@ -323,7 +375,7 @@ The artifacts are in `tmp/spoke/<ticket-id>/`: `<agent>.md` (the report), `summa
|
|
|
323
375
|
**A segment beginning with `⚠` is never dropped, and neither is `(audit unavailable)`.** Those are not decoration sitting outside the named columns; they are the audit telling you something went wrong, and dropping them leaves the user holding only the parts that said nothing did.
|
|
324
376
|
|
|
325
377
|
**Any segment carrying content is reproduced word for word.** Only `pass` and the CLI's own empty-value word may be compressed. The segments saying "nothing here" are the cheap ones to keep, and the one saying something is the one worth dropping — so this rule is deliberately asymmetric: **the more a segment has to say, the less freedom you have with it.**
|
|
326
|
-
3. **Then open a separate "hub assessment" section** — deduplicate, and annotate each item with your preliminary judgment (holds / does not hold + why / needs the user's ruling).
|
|
378
|
+
3. **Then open a separate "hub assessment" section** — deduplicate, and annotate each item with your preliminary judgment (holds / does not hold + why / needs the user's ruling). **Every item carries the observations it came from**, by spoke and number (`safety 2, 3; feasibility 9`), and **every observation appears against at least one item**. Two spokes' worth of numbered observations either all show up in that column or the ones that did not are visible at a glance — which is the point: without the numbers, an observation that quietly failed to make it into the assessment cannot be told apart from one you judged and dismissed.
|
|
327
379
|
|
|
328
380
|
### Merging multiple runs
|
|
329
381
|
|
|
@@ -337,8 +389,12 @@ The artifacts are in `tmp/spoke/<ticket-id>/`: `<agent>.md` (the report), `summa
|
|
|
337
389
|
|
|
338
390
|
**Line numbers must be re-verified.** A spoke's citations can be off by anywhere from a few to dozens of lines while **the description of the content is usually right** — usable at the fact level, unusable at the location level. **A wrong location is not a hallucination** (a hallucination is "that passage does not exist in that file at all"), and the two are handled differently: a hallucination calls for a rerun or a different model, a wrong location only needs you to locate it again. **Mistaking a wrong location for a hallucination throws away an entire usable output.**
|
|
339
391
|
|
|
392
|
+
**This applies to the line numbers you write, too.** Re-verifying a spoke's citation and then citing it from memory a few paragraphs later puts the drift back in under your own name — and yours carries more weight with the user, because you said you opened the file.
|
|
393
|
+
|
|
340
394
|
**Three: when verifying a spoke's citation, a comment is not evidence.** "The spoke says a comment backs this conclusion" is not enough — you also have to verify **whether what the comment says still holds**. Comments drift away from the code, and a drifted comment reads exactly like a correct one, so verifying only that "the comment exists and matches" turns a wrong claim into an accepted one. The first item of the MUST checklist in AGENTS.md, "a filename, a comment, or a line number is not enough", was written for authoring plans; here it extends to accepting a spoke's citations.
|
|
341
395
|
|
|
396
|
+
**A spoke reporting "I could not read X" is a correct report, not a false alarm.** It names a file you did not put on its list, which makes it your gap and not its mistake — filing it under "false alarm", or quietly resolving it yourself and moving on, hides the one signal that tells you the allowlist was wrong. Resolve it if you can, and still say plainly that the list was short.
|
|
397
|
+
|
|
342
398
|
### Reading the audit table
|
|
343
399
|
|
|
344
400
|
| Column | Meaning |
|
|
@@ -35,7 +35,8 @@ cd <absolute path to the repo root>
|
|
|
35
35
|
echo "=== workflow specification ==="
|
|
36
36
|
ls CLAUDE.md AGENTS.md workflow_spec.md 2>&1
|
|
37
37
|
# This only locates which file holds the content. A hit here ≠ you can read it — see the criterion below.
|
|
38
|
-
|
|
38
|
+
# Both spellings: a project may carry this chapter in another language than the pack you installed.
|
|
39
|
+
grep -nE "Plan → Implement → Accept|規劃→實作→驗收" CLAUDE.md AGENTS.md workflow_spec.md 2>/dev/null
|
|
39
40
|
|
|
40
41
|
echo "=== skills and lenses ==="
|
|
41
42
|
ls .claude/skills/ 2>/dev/null
|
|
@@ -49,6 +50,8 @@ git check-ignore -q tmp && echo "ignored" || echo "not ignored"
|
|
|
49
50
|
|
|
50
51
|
**There is only one criterion: can you, right now, read the contents of the chapter "Plan → Implement → Accept (hub-and-spoke form)"?**
|
|
51
52
|
|
|
53
|
+
**The chapter may be there under the other language's heading.** Language packs install their own copy, so a project that already had this chapter can end up with two — in two languages, only one of which auto-loads. The grep above looks for both spellings for exactly this reason; if it returns hits in more than one file, read the next paragraph before deciding anything.
|
|
54
|
+
|
|
52
55
|
If you can read it, it passes. Whether the content is pasted directly into the entry file or pulled in via something like `@` is **the user's choice and outside the scope of this check**.
|
|
53
56
|
|
|
54
57
|
If you cannot read it, mark it as failing, then **go find it yourself** (usually `workflow_spec.md` at the repo root), and note in your report that "the specification is not in the auto-loaded set; it was read manually this time" — so the user knows a different session will miss it again.
|
|
@@ -82,6 +85,8 @@ If it is not gitignored, mark it as failing: spoke reports contain the verbatim
|
|
|
82
85
|
|
|
83
86
|
## 2. If you are a Claude Code agent
|
|
84
87
|
|
|
88
|
+
**If you are going to dispatch externally (`/find-holes-external`), items one and four of §3 apply to you too** — the CLI has to be runnable and configured no matter which host is driving it. The rest of §3 is about hosts other than this one.
|
|
89
|
+
|
|
85
90
|
The two items below affect **only Claude Code's own in-process sub-agents**. For external dispatch (`find-holes-external` via `dowafu`), the spoke's model is decided by the ticket's `_dispatch.md` and is **unaffected by either of them** — if this project only uses external dispatch, checking this section changes nothing.
|
|
86
91
|
|
|
87
92
|
```bash
|
|
@@ -132,7 +137,7 @@ The `availableModels` allowlist then filters the three layers above: a blocked f
|
|
|
132
137
|
|
|
133
138
|
## 3. If you are not a Claude Code agent
|
|
134
139
|
|
|
135
|
-
The two items in section 2 do not exist for you; skip them. What you need to confirm is the
|
|
140
|
+
The two items in section 2 do not exist for you; skip them. What you need to confirm is the four things below, **in this order** — if an earlier one does not hold, checking the later ones is pointless.
|
|
136
141
|
|
|
137
142
|
### One: where `dowafu` is, and whether it runs
|
|
138
143
|
|
|
@@ -151,6 +156,8 @@ Printing a version number passes. Failing to print one means exactly one of two
|
|
|
151
156
|
|
|
152
157
|
**`command -v dowafu` finding nothing does not mean it is not installed** — do not use that as the criterion.
|
|
153
158
|
|
|
159
|
+
**A command that neither returns nor errors is a third case: it is waiting.** Without `--yes` the CLI prints a confirmation prompt and blocks on stdin; depending on the host that surfaces as a timeout, as silence, or as an offer to send input on your behalf. Note which one your environment does — you will meet it again at dispatch time, and that is a much worse moment to find out.
|
|
160
|
+
|
|
154
161
|
### Two: whether the lens definitions and skills are present
|
|
155
162
|
|
|
156
163
|
See "skills and lenses" in section 1. One point matters especially for you:
|
|
@@ -163,6 +170,18 @@ See "is the workflow specification readable" in section 1; the criterion is the
|
|
|
163
170
|
|
|
164
171
|
**You are more likely than Claude Code to trip on this one**, so it is worth a second look. `@xxx.md` is Claude Code's import syntax and you will not expand it — if the entry file contains only that line, what you see is one line of text, and **you may well believe you have already read the specification**. Actually check whether that chapter's content is in your context; do not go by impression.
|
|
165
172
|
|
|
173
|
+
### Four: is the CLI configured — `dowafu --doctor`
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
dowafu --doctor
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
It prints where the config directory resolved to, whether `.env` is there, **which providers have a key** (presence only — it never prints a value), the bundled model whitelist, and which lens definitions it found. It calls no API and costs nothing, and it needs no ticket, which is what makes it usable before anything else exists.
|
|
180
|
+
|
|
181
|
+
Report the missing rows as they are printed. **Do not offer to write the key for the user, and do not ask them to paste one into this conversation** — whatever is pasted here stays in this conversation's history. Creating the directory and an empty template is fine; the value itself is theirs to type into the file.
|
|
182
|
+
|
|
183
|
+
A `dowafu --doctor` that prints nothing but an error is the same finding as item one: the CLI is not runnable from here, and nothing below it matters yet.
|
|
184
|
+
|
|
166
185
|
---
|
|
167
186
|
|
|
168
187
|
## 4. Output
|
|
@@ -3,7 +3,7 @@ name: find-holes-external
|
|
|
3
3
|
description: 把規劃書段落派給外部模型(OpenAI/DeepSeek/Gemini/Anthropic)做找漏洞審查,經本地 dowafu CLI 執行,spoke 唯讀且受白名單控管。適用於需要異質視角、或要對照真實原始碼審查規劃書時。用法:/find-holes-external <規劃書檔案路徑> [聚焦章節或問題]
|
|
4
4
|
metadata:
|
|
5
5
|
derived-from: ".claude/skills/find-holes-external/SKILL.md"
|
|
6
|
-
derived-from-sha256: "
|
|
6
|
+
derived-from-sha256: "a351ab07c313f0068df0662ba9711e1bec9bb8c15ffb50f4d773fca2d70c7e0f"
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
# find-holes-external — 外派找漏洞
|
|
@@ -43,6 +43,11 @@ system prompt 來源——角色、禁令、輸出格式、收尾句都在裡面
|
|
|
43
43
|
cd <repo 根的絕對路徑> && dowafu <工單目錄> --repo-root . --dry-run
|
|
44
44
|
```
|
|
45
45
|
|
|
46
|
+
**`--lang` 決定 CLI 輸出與 spoke prompt 的語言,預設是 `en`。** 你裝的是中文套件、lens 檔也是
|
|
47
|
+
中文的,所以**中文專案要帶 `--lang zh-tw`**——不帶的話 CLI 會用英文輸出,spoke 也會拿到英文
|
|
48
|
+
prompt,而**兩邊都不會報錯,只是安靜地混語**。不想每次打,可以設環境變數 `DISPATCH_LANG=zh-tw`;
|
|
49
|
+
優先序是 `--lang` > `DISPATCH_LANG` > 內建預設 `en`。
|
|
50
|
+
|
|
46
51
|
你的終端機不保證落在哪一個 workspace folder,而 `--repo-root` 預設取 cwd。cwd 錯了會
|
|
47
52
|
**安靜地錯**——工單照樣解析、spoke 照樣派出去,只是白名單邊界與 lens 定義都指到別處。
|
|
48
53
|
|
|
@@ -89,6 +94,7 @@ dowafu --version
|
|
|
89
94
|
| 每個 spoke 的 provider/model | 見下 |
|
|
90
95
|
| **每題的「問題 → 答案在哪個檔 → 在清單裡嗎」對照** | **必列,見下方格式** |
|
|
91
96
|
| 預估成本量級 | 參考值:三個 spoke、中等工單約 40k token |
|
|
97
|
+
| **每支 spoke 的產物落點** | **必列**,見下方〈幾支 spoke 就要幾個落點〉 |
|
|
92
98
|
|
|
93
99
|
### 具體問題與允許清單**必須逐題對照著列**
|
|
94
100
|
|
|
@@ -115,15 +121,49 @@ dowafu --version
|
|
|
115
121
|
換一批對準答案位置的檔案,同一個 lens 就抓得到。**這個自問是為了在派工前擋下落差,
|
|
116
122
|
不是派工後才發現。**
|
|
117
123
|
|
|
118
|
-
### 每支 spoke
|
|
124
|
+
### 每支 spoke 各一張表——而且**那張表本身就是交件物,不是「我有做」的宣告**
|
|
125
|
+
|
|
126
|
+
**那張表每支 spoke 各填一張,而且兩張都要攤在使用者面前。** 說「清單都對過了」「該有的檔都在」
|
|
127
|
+
**不能取代把表給他看**——「允許清單已涵蓋所有問題」只是一句話,真假都一樣好寫。
|
|
128
|
+
|
|
129
|
+
每一列要寫**你實際預期答案所在的那個檔的路徑**——不是目錄、不是「tags 那幾支路由」、
|
|
130
|
+
不是 lens 的名字。寫成與允許清單同樣的形式,兩邊才對得起來:
|
|
131
|
+
|
|
132
|
+
| Q | 問題 | 答案在哪個檔 | 在清單裡嗎 |
|
|
133
|
+
| --- | --- | --- | --- |
|
|
134
|
+
| 1 | `requireAuth` 回傳的 userId 夠不夠做刪除自己的檢查 | `lib/auth-guard.ts` | ✅ |
|
|
135
|
+
| 2 | 最後一位管理員的防護是不是原子的 | `prisma/schema.prisma`(其餘看待審段落本身) | ✅ |
|
|
136
|
+
|
|
137
|
+
**把路徑寫進那一欄之前,先確認答案真的在那個檔裡**——grep 那個符號,或直接開檔。
|
|
138
|
+
這一欄是為了在派工前擋下漏檔;**憑印象填的話它什麼都擋不住**:一個看起來很合理的檔名,
|
|
139
|
+
與正確的那個一樣能通過格式檢查,而差額是 spoke 用錢付的。
|
|
119
140
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
可以,但要說得出為什麼——**清單相同是結論,不是起點**。
|
|
141
|
+
**兩支最後拿到同一批檔也可以,但要說得出為什麼。** 清單相同是**可以解釋的結論**,不是起點;
|
|
142
|
+
而「裁到兩份不一樣為止」是同一個錯誤的反面。
|
|
123
143
|
|
|
124
144
|
**一份清單餵兩支 lens,會往交集收斂,而不是聯集。** 最先掉的是「只有其中一支需要」的檔,
|
|
125
145
|
而那正是那支 lens 被派去看的東西;spoke 這時只能把缺口寫進「無法驗證」欄,而用這種方式
|
|
126
|
-
|
|
146
|
+
發現要付一整輪派工的錢。為省錢裁清單是合理的——但要**各自對著自己的問題裁,不是對著另一支裁**。
|
|
147
|
+
|
|
148
|
+
### 幾支 spoke 就要幾個落點——先挖好坑再派
|
|
149
|
+
|
|
150
|
+
一支 spoke 的產物落在 `tmp/spoke/<ticket-id>/` 底下**以它的 agent 為名的那一組檔**:
|
|
151
|
+
`<agent>.md`,以及 `raw/<agent>.request.json`/`.response.json`/`.errors.json`。
|
|
152
|
+
那一組同進同出,所以**落點就是 `<ticket-id>` + `<agent>` 這個組合**。
|
|
153
|
+
|
|
154
|
+
**派工計畫要逐支列出落點路徑,判準只有一句:落點數要等於 spoke 數,而且兩兩不同。**
|
|
155
|
+
|
|
156
|
+
| spoke | lens | provider/model | 產物落點 |
|
|
157
|
+
| --- | --- | --- | --- |
|
|
158
|
+
| 1 | safety | openai/gpt-5.6-luna | `tmp/spoke/auth-review-luna/hole-finder-safety.md` |
|
|
159
|
+
| 2 | safety | deepseek/deepseek-v4-flash | `tmp/spoke/auth-review-ds/hole-finder-safety.md` |
|
|
160
|
+
| 3 | feasibility | gemini/gemini-3.6-flash | `tmp/spoke/auth-review-luna/hole-finder-feasibility.md` |
|
|
161
|
+
|
|
162
|
+
**兩支算出同一個路徑,就是坑不夠**——而且解法不是改個檔名,是**拆工單目錄**:同一個 lens 要跑
|
|
163
|
+
多個型號時,一個型號一個目錄(ticket-id 加後綴),各派一次。
|
|
164
|
+
不同 lens 共用一個目錄沒有問題,它們的 agent 名本來就不同。
|
|
165
|
+
|
|
166
|
+
這一欄要用數的,不要用看的。**你不需要知道撞名會發生什麼**——數不對就先停下來拆目錄。
|
|
127
167
|
|
|
128
168
|
### lens
|
|
129
169
|
|
|
@@ -141,7 +181,7 @@ dowafu --version
|
|
|
141
181
|
| provider | model |
|
|
142
182
|
| --- | --- |
|
|
143
183
|
| `openai` | `gpt-5.6-luna`/`gpt-5.6-terra`/`gpt-5.6-sol` |
|
|
144
|
-
| `deepseek` | `deepseek-v4-flash` |
|
|
184
|
+
| `deepseek` | `deepseek-v4-flash`/`deepseek-v4-pro` |
|
|
145
185
|
| `gemini` | `gemini-3.1-flash-lite`/`gemini-3.5-flash-lite`/`gemini-3.6-flash` |
|
|
146
186
|
| `anthropic` | `claude-opus-5`/`claude-sonnet-5` |
|
|
147
187
|
|
|
@@ -176,6 +216,10 @@ dowafu --version
|
|
|
176
216
|
`providers.json` 的 `reasoning.allowed`,**各家值域不同**(例如 `deepseek` 沒有
|
|
177
217
|
`medium`);填了不在值域內的值會被擋下並列出允許值,不會靜默降級。
|
|
178
218
|
|
|
219
|
+
**agent 欄在同一份 `_dispatch.md` 裡不得重複。** 一個 agent 一列;要用同一個 lens 跑多個型號,
|
|
220
|
+
拆成多個工單目錄(見 §2〈幾支 spoke 就要幾個落點〉)。**CLI 在解析期就會擋下重複的 agent,
|
|
221
|
+
乾跑一樣會被擋**——但那是最後一道防線,不是可以省掉數落點那一步的理由。
|
|
222
|
+
|
|
179
223
|
### `_shared.md`(所有 spoke 共用)
|
|
180
224
|
|
|
181
225
|
```markdown
|
|
@@ -215,7 +259,8 @@ dowafu --version
|
|
|
215
259
|
路徑**相對 repo 根目錄**。留空也合法(純文字審查),但那樣就讀不到程式碼,會少掉
|
|
216
260
|
「文件說 X、`src/foo.ts:42` 其實是 Y」這類最有價值的發現。**清單屬於這一份
|
|
217
261
|
`<agent>.md`,不屬於整批派工**——不要把另一支的清單整份複製過來:對它有用、對這一支
|
|
218
|
-
|
|
262
|
+
沒用的檔,在讀取順序裡是純負擔;反過來就是漏洞。**沒開過的檔不能拿來證明「答案不在
|
|
263
|
+
那裡」**——如果你指不出某一題的答案在哪個檔,那一題現在就是沒有檔可依,不管表上填了什麼。
|
|
219
264
|
|
|
220
265
|
4. **大的檔案排在清單最後——這能省掉一半成本。** spoke **嚴格照清單順序**讀檔,而多數
|
|
221
266
|
模型**一輪只叫一個檔**,每一輪又會把先前讀過的全部內容重送一次。所以一個檔被重複
|
|
@@ -258,6 +303,10 @@ dowafu --version
|
|
|
258
303
|
dowafu tmp/dispatch/<ticket-id> --repo-root . --dry-run
|
|
259
304
|
```
|
|
260
305
|
|
|
306
|
+
**一次乾跑只驗一個工單目錄。** 這一批如果拆成了多個目錄(同一個 lens 跑多個型號時就會),
|
|
307
|
+
**每一個都要各乾跑一次**,並把各次的估算**加總**之後再把數字呈給使用者。
|
|
308
|
+
只乾跑第一個就去實跑,等於其餘目錄完全沒經過這道檢查。
|
|
309
|
+
|
|
261
310
|
**跑之前先跟使用者說明這一步在幹嘛。** 他多半沒用過這個工具,看你在下指令會以為已經
|
|
262
311
|
開始派工、開始計費了:
|
|
263
312
|
|
|
@@ -283,6 +332,26 @@ code fence 的語意就是「這是工具印出來的」,把改寫過的內容
|
|
|
283
332
|
最像可以省的,卻正是讓這些數字能拿來做決定的東西。**限定語一刪,你交給使用者的數字就
|
|
284
333
|
比工具給你的更硬。**
|
|
285
334
|
|
|
335
|
+
**工具標了 `⚠` 或 `ℹ` 的那幾行一律逐字轉述,不得改寫。** `⚠` 是工具在說「現在有事」——
|
|
336
|
+
輸出目錄已經有產物、清單順序正在多花錢、某支 spoke 什麼都沒讀。把它改寫成一句比較平順的話,
|
|
337
|
+
是你能對這份報表做的最貴的一件事,因為讀者會失去唯一那個「需要你做決定」的訊號。
|
|
338
|
+
特別是:`⚠ 大檔排清單最後可降至 N(本項省 N%)` 的意思是**你現在沒有排好**,
|
|
339
|
+
不是「已經排好了、重排只能再省一點」。
|
|
340
|
+
|
|
341
|
+
必須跟著數字一起活下來的限定語,逐項點名:
|
|
342
|
+
|
|
343
|
+
| 出現在哪 | 什麼一定要跟著轉 |
|
|
344
|
+
| --- | --- |
|
|
345
|
+
| 每支 spoke 那一行 | `effort=`、`lang=`、`store=`,以及它的 `cap` |
|
|
346
|
+
| 單價子行 | 每 M token 的數字**與**`價目查證日 <日期>` |
|
|
347
|
+
| `ℹ` 收尾句檢查 | 每支一行,照印的轉 |
|
|
348
|
+
| 初始 prompt 估算 | 它**不含工單與允許清單**,以及閘門上限 |
|
|
349
|
+
| 允許清單估算 | 它是上限、**不去重**,以及字元/token 的換算基礎 |
|
|
350
|
+
| 逐個讀順序放大量 | 它是「逐個讀假設下的上限、批次讀的廠牌不適用」、排序的判定,以及**本項不含初始 prompt 與工單** |
|
|
351
|
+
| 最壞總消耗 | 它是**上限不是預期值**,且是各 spoke cap 的加總 |
|
|
352
|
+
|
|
353
|
+
少了這些,數字讀起來會比工具的原意更硬。**自己把 token 換算成錢時,要講明那是你算的、依據哪一行單價。**
|
|
354
|
+
|
|
286
355
|
報表只給 token,不給金額,且**只在乾跑階段成立**。要換算成錢,**價目來源是
|
|
287
356
|
`providers.json` 的 `pricing`(`inputPerM`/`cachedInputPerM`/`outputPerM`),不要查
|
|
288
357
|
官網**——那份數字就是 CLI 計費用的,查官網會讓「你報的錢」與「CLI 算的錢」對不上。
|
|
@@ -376,6 +445,11 @@ dowafu tmp/dispatch/<ticket-id> --repo-root . --yes
|
|
|
376
445
|
產物在 `tmp/spoke/<ticket-id>/`:`<agent>.md`(原文)、`summary.md`(稽核表)、
|
|
377
446
|
`run.jsonl`(執行記錄)、`raw/`(完整請求與回應)。
|
|
378
447
|
|
|
448
|
+
**`run.jsonl` 是逐事件附加寫入的,不會被覆蓋。** 產物與 `raw/` 若被蓋掉——重跑,或另一支
|
|
449
|
+
寫到同一個名字——那個檔裡仍然留著每一支的 `spoke_start`(provider 與型號)、逐輪 usage、
|
|
450
|
+
每一次讀檔(含被拒的)、錯誤,以及 `spoke_end` 的 token 與成本。
|
|
451
|
+
**報告文字救不回來,但花了多少錢、讀了哪些檔,可以還原。**
|
|
452
|
+
|
|
379
453
|
> **回收之前先確認 §5 的啟動確認過了。** 這個目錄底下的任何檔案,都不會告訴你它是不是
|
|
380
454
|
> 這一次的產物。
|
|
381
455
|
|
|
@@ -409,7 +483,10 @@ dowafu tmp/dispatch/<ticket-id> --repo-root . --yes
|
|
|
409
483
|
段落留著最省事,說「有事」的那段才是會被刪掉的——所以這條規則是刻意不對稱的:
|
|
410
484
|
**一段講的東西越多,你對它的處置自由越少。**
|
|
411
485
|
3. **之後另立「hub 判讀」一節**——去重,逐條標註你的初步判讀(成立/不成立+為什麼/
|
|
412
|
-
|
|
486
|
+
需使用者裁決)。**每一項都要標明它來自哪幾條觀察**,寫成 spoke + 條號
|
|
487
|
+
(`safety 2、3;feasibility 9`),而且**每一條觀察都要出現在至少一項裡**。
|
|
488
|
+
兩支 spoke 的編號觀察,要嘛全部出現在那一欄,要嘛沒出現的一眼就看得出來——**這正是重點**:
|
|
489
|
+
沒有編號的話,「安靜地沒被列進來」與「你判讀過但認為不成立」長得一模一樣。
|
|
413
490
|
|
|
414
491
|
### 跑了多次時怎麼合併
|
|
415
492
|
|
|
@@ -430,11 +507,18 @@ dowafu tmp/dispatch/<ticket-id> --repo-root . --yes
|
|
|
430
507
|
不同:幻覺要重跑或換模型,位置錯只需自己重新定位。**把位置錯判成幻覺,會丟掉整份能用的
|
|
431
508
|
產出。**
|
|
432
509
|
|
|
510
|
+
**這條對你自己寫的行號同樣成立。** 驗完 spoke 的引用、隔幾段自己憑印象寫一次行號,
|
|
511
|
+
等於把漂移換上你的名字放回去——**而你的份量更重**,因為你說了你開過檔。
|
|
512
|
+
|
|
433
513
|
**三、驗 spoke 引用時,註解不算證據。** 「spoke 說某段有註解背書某個結論」不夠,還要驗
|
|
434
514
|
**註解說的還成不成立**——註解會與程式碼漂移,而漂移的註解讀起來跟正確的一模一樣,
|
|
435
515
|
只驗「註解存在且內容吻合」會把錯的判成對的。AGENTS.md 必答檢查第 1 條「不能只憑檔名、
|
|
436
516
|
註解或行號」原本管的是寫規劃書時,這裡把它擴到驗收 spoke 引用時。
|
|
437
517
|
|
|
518
|
+
**spoke 說「我讀不到 X 所以無法確認」是正確回報,不是假警報。** 它指出的是**你沒放進清單**的檔,
|
|
519
|
+
那是你的缺口、不是它的錯——把它歸到「假警報」,或自己默默查掉就往下走,
|
|
520
|
+
會把「清單裁錯了」這個唯一的訊號蓋掉。**你可以自己補查,但仍要明說清單當時是短的。**
|
|
521
|
+
|
|
438
522
|
### 稽核表怎麼看
|
|
439
523
|
|
|
440
524
|
| 欄位 | 意思 |
|
|
@@ -3,7 +3,7 @@ name: preflight
|
|
|
3
3
|
description: 開工前檢查這個專案的環境有沒有把工作流程靜默停用:流程規範那一章的內容讀不讀得到、skill 與 lens 齊不齊、tmp/ 有沒有被 gitignore、dowafu 跑不跑得起來。只讀、只報告,不改任何設定。
|
|
4
4
|
metadata:
|
|
5
5
|
derived-from: ".claude/skills/preflight/SKILL.md"
|
|
6
|
-
derived-from-sha256: "
|
|
6
|
+
derived-from-sha256: "ad8b06b543347a387f44eb0fdaffad343b332040477d1b0ac047600a49b26ee6"
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
# preflight — 環境前置檢查
|
|
@@ -31,7 +31,8 @@ cd <repo 根的絕對路徑>
|
|
|
31
31
|
echo "=== 流程規範 ==="
|
|
32
32
|
ls CLAUDE.md AGENTS.md workflow_spec.md 2>&1
|
|
33
33
|
# 這條只幫你定位「內容寫在哪個檔」。它有命中 ≠ 你讀得到——判準見下。
|
|
34
|
-
|
|
34
|
+
# 兩種寫法都要找:專案本來那一章的語言,未必和你裝的語言套件相同。
|
|
35
|
+
grep -nE "規劃→實作→驗收|Plan → Implement → Accept" CLAUDE.md AGENTS.md workflow_spec.md 2>/dev/null
|
|
35
36
|
|
|
36
37
|
echo "=== skill 與 lens ==="
|
|
37
38
|
ls .claude/skills/ 2>/dev/null
|
|
@@ -45,6 +46,10 @@ git check-ignore -q tmp && echo "已忽略" || echo "未忽略"
|
|
|
45
46
|
|
|
46
47
|
**判準只有一條:「規劃→實作→驗收流程規範(主從形態)」這一章的內容,你現在讀得到嗎?**
|
|
47
48
|
|
|
49
|
+
**那一章可能是以另一種語言的標題存在的。** 語言套件各帶各的副本,所以本來就有這一章的專案
|
|
50
|
+
最後會變成兩份——兩種語言,而只有一份會自動載入。上面那道 grep 兩種寫法都找就是為了這個;
|
|
51
|
+
**命中超過一個檔時,先讀下一段再下判斷。**
|
|
52
|
+
|
|
48
53
|
讀得到就算通過。內容是直接貼在入口檔裡、還是用 `@` 之類的方式引入的,**那是使用者的
|
|
49
54
|
選擇,不在檢查範圍內**。
|
|
50
55
|
|
|
@@ -89,7 +94,7 @@ session 又會漏掉。
|
|
|
89
94
|
|
|
90
95
|
---
|
|
91
96
|
|
|
92
|
-
## 2.
|
|
97
|
+
## 2. 這個工具鏈另外要查的四件事
|
|
93
98
|
|
|
94
99
|
按這個順序——前一項不成立,後面查了也沒有意義。
|
|
95
100
|
|
|
@@ -111,6 +116,10 @@ dowafu --version
|
|
|
111
116
|
|
|
112
117
|
**`command -v dowafu` 查不到不代表沒安裝**,別拿那個當判準。
|
|
113
118
|
|
|
119
|
+
**既沒回來也沒報錯,是第三種情況:它在等。** 不帶 `--yes` 時 CLI 會印出確認提示並卡在
|
|
120
|
+
stdin 上,而這在不同 host 會表現成逾時、表現成沒有輸出、或表現成「要不要幫你送出輸入」。
|
|
121
|
+
**記下你的環境是哪一種**——派工當下會再遇到一次,而那個時機點知道就晚了。
|
|
122
|
+
|
|
114
123
|
### 二、lens 定義與 skill 在不在
|
|
115
124
|
|
|
116
125
|
見第 1 節的「skill 與 lens」。有一點對你特別重要:
|
|
@@ -126,6 +135,22 @@ dowafu --version
|
|
|
126
135
|
語法,你不會展開它——入口檔裡若只有那一行,你看到的就是一行字,而**你很可能以為自己
|
|
127
136
|
已經讀過規範了**。實際檢查你的 context 裡有沒有那章的內容,別憑印象。
|
|
128
137
|
|
|
138
|
+
### 四、CLI 的設定到位了嗎——`dowafu --doctor`
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
dowafu --doctor
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
它會印出設定目錄解析到哪、`.env` 在不在、**哪幾家有 key**(只看有沒有,不印值)、
|
|
145
|
+
內建的型號白名單、以及找到哪幾支 lens 定義。不呼叫 API、不花錢,**而且不需要工單**——
|
|
146
|
+
這正是它在「什麼都還沒有」的時候能用的原因。
|
|
147
|
+
|
|
148
|
+
缺的項目照它印的回報。**不要主動幫使用者寫 key,也不要請他把 key 貼進這段對話**——
|
|
149
|
+
貼進來的東西會留在這段對話的歷史裡。幫他建目錄、放一份空範本可以,值要由他自己填進檔案。
|
|
150
|
+
|
|
151
|
+
`dowafu --doctor` 只印得出錯誤時,那與第一項是同一個發現:CLI 從這裡跑不起來,
|
|
152
|
+
下面幾項都還輪不到。
|
|
153
|
+
|
|
129
154
|
---
|
|
130
155
|
|
|
131
156
|
## 3. 輸出
|
|
@@ -60,6 +60,11 @@ description: 【Claude Code 內派專用;VS Code 環境改用 find-holes-exter
|
|
|
60
60
|
**這是最常犯的錯**——「問了某題,卻沒給回答那題所需要的檔」。逐題列出來,
|
|
61
61
|
使用者一眼就能看出漏了什麼。**這一欄不是形式,是目前唯一擋得住漏檔的機制。**
|
|
62
62
|
|
|
63
|
+
**把路徑寫進那一欄之前,先確認答案真的在那個檔裡**——grep 那個符號,或直接開檔。
|
|
64
|
+
憑印象填的話這一欄什麼都擋不住:一個看起來很合理的檔名,與正確的那個一樣通得過形式檢查,
|
|
65
|
+
而差額要等一整輪派工之後才會浮出來。**兩支最後拿到同一批檔也可以,但要說得出為什麼**
|
|
66
|
+
——清單相同是可以解釋的結論,不是起點。
|
|
67
|
+
|
|
63
68
|
**使用者對數量/模型/lens 的修改一律照辦。**
|
|
64
69
|
|
|
65
70
|
### 3.1 確認後,每個 prompt 必含
|
|
@@ -72,6 +77,8 @@ description: 【Claude Code 內派專用;VS Code 環境改用 find-holes-exter
|
|
|
72
77
|
清單是看的材料**。清單對不準問題的答案位置,spoke 物理上不可能答對。
|
|
73
78
|
**漏了是派工端的失誤,不是 spoke 的問題。每支 spoke 的清單各自對著自己的問題裁**
|
|
74
79
|
——不要因為湊一份比較省事就給兩支同一份;掉的會是只有其中一支需要的那個檔。
|
|
80
|
+
**沒開過的檔不能拿來證明「答案不在那裡」**——指不出某一題的答案在哪個檔,
|
|
81
|
+
那一題現在就是沒有檔可依,不管表上填了什麼。
|
|
75
82
|
|
|
76
83
|
- **清單內把大檔排在最後**(依檔案大小遞增)。spoke 照清單順序讀檔,每輪會重送先前
|
|
77
84
|
讀過的全部內容,所以排越前面被重複計費越多次,差距可以到將近一倍。
|
|
@@ -122,6 +129,10 @@ description: 【Claude Code 內派專用;VS Code 環境改用 find-holes-exter
|
|
|
122
129
|
> **位置錯不等於幻覺。** 幻覺是「該檔根本沒有這段」,處置是重跑或換模型;
|
|
123
130
|
> 位置錯只需自己重新定位。把前者誤判成後者會丟掉整份能用的產出。
|
|
124
131
|
|
|
132
|
+
**spoke 說「我讀不到 X 所以無法確認」是正確回報,不是假警報。** 它指出的是**你沒放進清單**
|
|
133
|
+
的檔,那是你的缺口、不是它的錯——把它歸到「假警報」,或自己默默查掉就往下走,
|
|
134
|
+
會把「清單裁錯了」這個唯一的訊號蓋掉。**你可以自己補查,但仍要明說清單當時是短的。**
|
|
135
|
+
|
|
125
136
|
## 紅線
|
|
126
137
|
|
|
127
138
|
- spoke 意見**不得觸碰「做不做」**。若它寫出終止/阻擋類的結論,丟棄該結論、
|