@tamng0905/builder-essential-skills 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/.claude/agents/lead-builder.md +55 -0
  2. package/.claude/agents/lead-reviewer.md +39 -0
  3. package/README.md +276 -0
  4. package/assets/readme-hero.png +0 -0
  5. package/assets/skill-cards/async-learning-teacher.svg +7 -0
  6. package/assets/skill-cards/code-standards.svg +7 -0
  7. package/assets/skill-cards/lead-research.svg +7 -0
  8. package/assets/skill-cards/lead.svg +7 -0
  9. package/assets/skill-cards/orwell-writing.svg +7 -0
  10. package/assets/skill-cards/session-profiler.svg +7 -0
  11. package/assets/skill-cards/validate-market.svg +7 -0
  12. package/assets/skill-cards/write-blog.svg +7 -0
  13. package/bin/builder-essential-skills.js +134 -0
  14. package/install.ps1 +58 -0
  15. package/install.sh +63 -0
  16. package/package.json +24 -0
  17. package/skills/async-learning-teacher/README.md +50 -0
  18. package/skills/async-learning-teacher/SKILL.md +192 -0
  19. package/skills/async-learning-teacher/agents/openai.yaml +4 -0
  20. package/skills/code-standards/README.md +27 -0
  21. package/skills/code-standards/SKILL.md +51 -0
  22. package/skills/lead/README.md +29 -0
  23. package/skills/lead/SKILL.md +222 -0
  24. package/skills/lead/config.py +359 -0
  25. package/skills/lead/dispatch.md +342 -0
  26. package/skills/lead/loop.md +103 -0
  27. package/skills/lead/models.json +34 -0
  28. package/skills/lead/research.md +76 -0
  29. package/skills/lead/status.ps1 +114 -0
  30. package/skills/lead/status.sh +110 -0
  31. package/skills/lead/watchdog.ps1 +90 -0
  32. package/skills/lead/watchdog.sh +88 -0
  33. package/skills/lead-research/README.md +27 -0
  34. package/skills/lead-research/SKILL.md +157 -0
  35. package/skills/lead-research/tactics.md +150 -0
  36. package/skills/orwell-writing/README.md +27 -0
  37. package/skills/orwell-writing/SKILL.md +44 -0
  38. package/skills/orwell-writing/agents/openai.yaml +4 -0
  39. package/skills/session-profiler/README.md +25 -0
  40. package/skills/session-profiler/SKILL.md +103 -0
  41. package/skills/session-profiler/agents/openai.yaml +4 -0
  42. package/skills/session-profiler/scripts/requirements.txt +2 -0
  43. package/skills/session-profiler/scripts/session_profiler/__init__.py +5 -0
  44. package/skills/session-profiler/scripts/session_profiler/analyses.py +246 -0
  45. package/skills/session-profiler/scripts/session_profiler/cli.py +77 -0
  46. package/skills/session-profiler/scripts/session_profiler/dataset.py +369 -0
  47. package/skills/session-profiler/scripts/session_profiler/discover.py +191 -0
  48. package/skills/session-profiler/scripts/session_profiler/toc.py +74 -0
  49. package/skills/session-profiler/scripts/session_profiler/trace.py +146 -0
  50. package/skills/session-profiler/scripts/sp +20 -0
  51. package/skills/session-profiler/scripts/tests/codex_home/sessions/2026/01/01/rollout-child.jsonl +7 -0
  52. package/skills/session-profiler/scripts/tests/codex_home/sessions/2026/01/01/rollout-root.jsonl +10 -0
  53. package/skills/session-profiler/scripts/tests/fixture/main/subagents/agent-abc.jsonl +2 -0
  54. package/skills/session-profiler/scripts/tests/fixture/main/subagents/agent-abc.meta.json +1 -0
  55. package/skills/session-profiler/scripts/tests/fixture/main.jsonl +7 -0
  56. package/skills/session-profiler/scripts/tests/test_fixture.py +58 -0
  57. package/skills/validate-market/README.md +27 -0
  58. package/skills/validate-market/SKILL.md +202 -0
  59. package/skills/write-blog/README.md +27 -0
  60. package/skills/write-blog/SKILL.md +142 -0
  61. package/skills/write-blog/check.sh +52 -0
  62. package/skills/write-blog/reference/angles.md +51 -0
  63. package/skills/write-blog/reference/craft.md +94 -0
  64. package/skills/write-blog/reference/seo.md +116 -0
  65. package/skills/write-blog/reference/voice.md +64 -0
  66. package/skills/write-blog/reference/wiring.md +113 -0
@@ -0,0 +1,342 @@
1
+ # Dispatch reference
2
+
3
+ Dispatch turns a frozen slice into fresh builder, reviewer, or critic work. The
4
+ Lead chooses the job shape, the model tier, the worktree, and the report path;
5
+ the subagent receives a self-contained task and returns raw evidence only.
6
+
7
+ ## Model routing
8
+
9
+ Every role — `lead`, `builder`, `reviewer`, `researcher`, `scout`, `critic` — is
10
+ an independent slot resolved by `skills/lead/config.py`. The "submodel" of a role
11
+ is its reasoning effort. Do not hand-parse config; run the resolver and use what
12
+ it prints:
13
+
14
+ ```bash
15
+ python skills/lead/config.py # table of the effective routing
16
+ python skills/lead/config.py --role builder # one role, with its command
17
+ python skills/lead/config.py --check # verify each provider CLI is on PATH
18
+ ```
19
+
20
+ Resolution order per role: repo `.lead/config`, then `~/.lead/config`, then the
21
+ shipped defaults in `config.py`. A role value is `<provider>/<model>[:<effort>]`,
22
+ a user-defined `alias`, or the sentinel `inherit-lead`. The provider registry
23
+ (`skills/lead/models.json`, extendable via `.lead/models.json`) maps a provider's
24
+ model aliases to real model ids and prints the exact CLI command — this is the
25
+ single place a model-generation change is reviewed. Full grammar: `MODELS.md`.
26
+
27
+ Defaults (see `config.py`): `lead = claude/fable:xhigh` (the leading model),
28
+ `reviewer = inherit-lead`, `builder = codex/best:xhigh`, `researcher =
29
+ codex/best:high`, `scout = codex/best:low`, `critic = claude/fable:high`.
30
+
31
+ Rules that always hold:
32
+
33
+ - **Tier is fixed at decomposition** by config plus dispatch rules; a job failure
34
+ never moves it. A failure is a diagnosis task, not a retry-at-a-stronger-model
35
+ task (see `loop.md` "## Failure ladder").
36
+ - **Codex-first builder fallback.** If the resolved builder's Codex CLI is not on
37
+ PATH at preflight (`config.py --check` flags this), fall back to
38
+ `claude/sonnet:high` and write one tracking-issue comment naming requested vs
39
+ substituted. Never hard-fail on model availability alone.
40
+ - **Dispatch rules** (`when <task-class> -> <spec>`) route recipe-like work to a
41
+ cheaper tier or ambiguous work to a deeper one; the resolver lists them. A
42
+ matching rule is a judgment aid — record which rule you used and override with a
43
+ reason on the issue when needed.
44
+ - **Cross-family review** for high-stakes slices reduces shared blind spots. When
45
+ both CLIs are installed, prefer the reviewer be a different family than the
46
+ builder, and record the direction in the verdict comment.
47
+
48
+ ## Per-harness delegation
49
+
50
+ | | Claude Code | Codex |
51
+ |---|---|---|
52
+ | Builder | Agent tool with `.claude/agents/lead-builder.md`; `disallowedTools` denies git commit/push; `isolation: worktree`; run in background; model passed per invocation from the resolver. Verify `git worktree list` after each spawn; never run two Claude-backend builders concurrently without verified separate worktrees. Never pre-create a worktree for a Claude-backend job. | `codex exec` per the resolver's builder command; the Lead owns worktree creation via git. |
53
+ | Reviewer | Agent tool with `.claude/agents/lead-reviewer.md`; read-only tools plus a shell for check commands; lead tier via `model: inherit` or a per-invocation model. | Fresh `codex exec --sandbox read-only` with the fixed reviewer template. |
54
+ | Watchdog | Background `watchdog.sh` / `watchdog.ps1` whose exit wakes the Lead; the LLM fallback template only when the harness cannot wake on a process exit. | Same script watchdog. |
55
+ | Critic | One fresh read-only subagent over the whole decomposition, pre-freeze. | Same. |
56
+
57
+ Job and reviewer reports must name which executor (Bash or PowerShell) ran each
58
+ check command; some sandboxes strip one shell.
59
+
60
+ ## Codex backend from a Claude Lead
61
+
62
+ When the Lead is Claude Code and the builder backend is Codex, write the builder
63
+ block to a file first, then pass it via stdin (`-`) — big prompt blocks contain
64
+ quotes that shells can mangle. Take the exact per-role command from
65
+ `config.py --role builder`; the effort pin is the only thing that changes for a
66
+ tier-down.
67
+
68
+ Single job in the current checkout:
69
+
70
+ ```bash
71
+ codex exec -C <repo-root> --sandbox workspace-write \
72
+ -m <model-id> -c model_reasoning_effort="<effort>" \
73
+ --json -o .lead/last-run.md \
74
+ - < .lead/dispatch-block.md
75
+ ```
76
+
77
+ For 2–5 jobs, the Lead owns the worktree and runs them in parallel:
78
+
79
+ ```bash
80
+ git -C <repo-root> worktree add .lead/wt/<slice>-<NN> -b job/<slice>-<NN> <freeze-sha>
81
+
82
+ codex exec -C <repo-root>/.lead/wt/<slice>-<NN> --sandbox workspace-write \
83
+ -m <model-id> -c model_reasoning_effort="<effort>" \
84
+ --json -o .lead/wt/<slice>-<NN>.last-run.md \
85
+ - < .lead/wt/<slice>-<NN>.block.md
86
+ ```
87
+
88
+ A worktree's `.git` is sandbox-protected; builders cannot commit or touch shared
89
+ history from any job.
90
+
91
+ ## Integration commands
92
+
93
+ Integration is Lead-only, after per-job post-flight passes. For Claude-backend
94
+ jobs, commit inside the harness's auto-created worktree, then merge that branch:
95
+
96
+ ```bash
97
+ git -C <repo-root> checkout -b slice/<name> <freeze-sha>
98
+ git -C <repo-root>/.lead/wt/<slice>-<NN> add -A
99
+ git -C <repo-root>/.lead/wt/<slice>-<NN> commit -m "job <NN>: <what>"
100
+ git -C <repo-root> merge --no-ff job/<slice>-<NN>
101
+ <rerun the check commands>
102
+ git -C <repo-root> worktree remove .lead/wt/<slice>-<NN>
103
+ git -C <repo-root> branch -d job/<slice>-<NN>
104
+ ```
105
+
106
+ A merge conflict means the plan was not disjoint. Kill the conflicting job and
107
+ re-slice; never hand-resolve builder conflicts.
108
+
109
+ ## Reviewer template
110
+
111
+ Send this as-is except for the placeholders. Add no slice-specific prose,
112
+ encouragement, summaries, or interpretation. Intent context is pointer-only.
113
+
114
+ <!-- lead-reviewer-template:start -->
115
+ ```text
116
+ Frozen check file path: docs/checks/<slice>.md
117
+ Freeze commit SHA: <freeze-sha>
118
+ Branch to review: <branch>
119
+ Spec pointer: <spec path named by the frozen check>
120
+ Job report: docs/jobs/<issue-slug>-01.md
121
+ Rulings file: docs/jobs/<issue-slug>-rulings.md (absent = no post-freeze rulings)
122
+
123
+ You are a fresh, read-only reviewer. You did not build this job. Flag only gaps
124
+ that affect correctness, the stated requirements, or documented project
125
+ invariants -- cite file:line evidence for every finding. No stylistic preferences.
126
+
127
+ Tree audit: any tracked-file modification during review discards the verdict as
128
+ INVALID.
129
+
130
+ Verdict format:
131
+ - Checks integrity: PASS | FAIL | INVALID
132
+ Raw evidence: git diff <freeze-sha>..HEAD -- docs/checks/
133
+ - Diff vs intent: PASS | FAIL | INVALID
134
+ Raw evidence: file:line evidence from the diff and the frozen check/spec text
135
+ - Per check:
136
+ - <check id>: PASS | FAIL | INVALID
137
+ Command: <exact command from the frozen check>
138
+ Executor: <Bash | PowerShell>
139
+ Raw evidence: verbatim stdout/stderr and exit code
140
+ - Slice verdict: PASS | FAIL | INVALID
141
+ Decisive reason: <one sentence tied to raw evidence>
142
+ ```
143
+ <!-- lead-reviewer-template:end -->
144
+
145
+ Passing checks with wrong code still fails: the diff-vs-intent verdict is not
146
+ optional. INVALID means "not measured the way the check specifies" — unmeasured
147
+ never equals passed.
148
+
149
+ ## Critic template
150
+
151
+ One pre-freeze pass over the whole decomposition. Send as-is except placeholders.
152
+
153
+ <!-- lead-critic-template:start -->
154
+ ```text
155
+ Draft check file path: docs/checks/<slice>.md
156
+ Branch: <branch>
157
+ Issue bodies: <pasted issue bodies for this plan>
158
+
159
+ Task: try to falsify this plan. Execute each check command against the current
160
+ tree, verify every referenced path/SHA/pointer resolves, and attack each
161
+ acceptance criterion and issue body for contradictions and non-falsifiability --
162
+ including patterns that collide with repo realities (e.g. a grep pattern matching
163
+ the repo's own name). For every file a job deletes or renames, grep the repo for
164
+ references and confirm the owning job's boundary covers them or a dependency edge
165
+ orders the fix. For every NEW artifact path a job will create, run
166
+ `git check-ignore <path>` and flag the plan if it is ignored.
167
+
168
+ Defect report format:
169
+ - <check id or clause>: FALSIFIED | HOLDS
170
+ Evidence: <command run and verbatim output, or file:line>
171
+ - Plan findings: <delete/rename reference and ignored-new-path findings, or none>
172
+ - Assumptions not evidenced in the repo: <list or none>
173
+ ```
174
+ <!-- lead-critic-template:end -->
175
+
176
+ ## Issue conventions
177
+
178
+ Claim is a Lead action, never a builder action: the Lead is the single
179
+ dispatcher and assigns exactly one issue per job immediately before spawning its
180
+ builder. On current backends, builders usually cannot post to issues (Codex has
181
+ no network; a Claude subagent may lose its shell), so `MIRROR: LEAD` is the
182
+ normal mode — the Lead mirrors status at event boundaries it already occupies.
183
+
184
+ ```bash
185
+ gh issue edit <n> --add-assignee "@me" # Lead claims, before dispatch
186
+ gh issue comment <n> --body "PHASE 0: <disagreements, or what I checked>"
187
+ gh issue comment <n> --body "BLOCKED: <exact blocker> + <what I tried>"
188
+ gh issue comment <n> --body "STATUS: <the report's exact status line>"
189
+ gh issue comment <n> --body "RULING: <decision> - <one line why>"
190
+ gh issue comment <n> --body "ANSWER: <blocker answer>"
191
+ gh issue comment <n> --body "VERDICT: PASS|FAIL|INVALID - <decisive reason>"
192
+ gh issue comment <tracking-issue-n> --body "DIGEST: <batched escalations + run summary>"
193
+ ```
194
+
195
+ Comments land at least 1 minute apart, each under 65,000 characters, never one
196
+ per commit (GitHub secondary rate limits). A running builder does NOT re-read
197
+ issue comments mid-job — the issue is the durable log, not a channel it polls; an
198
+ answer reaches a builder only through a fresh respawn's spawn context.
199
+
200
+ ## Watchdog dispatch
201
+
202
+ The Lead writes one watchdog config JSON per wave, then launches the platform
203
+ script as a background process (`watchdog.sh` on POSIX, `watchdog.ps1` on
204
+ Windows):
205
+
206
+ ```json
207
+ {
208
+ "sweep_sec": 120,
209
+ "stall_after_min": 10,
210
+ "jobs": [
211
+ { "id": "issue-31", "events_file": "<path>", "report_path": "<path>",
212
+ "worktree": "<path>", "duration_hint_min": 0 }
213
+ ]
214
+ }
215
+ ```
216
+
217
+ The watchdog detects mechanically and never kills, nudges, or judges. It exits
218
+ with typed evidence; the Lead rules on it:
219
+
220
+ | Exit | Prefix | Meaning |
221
+ |---|---|---|
222
+ | 0 | `WATCHDOG: ALL_DONE` | every job report exists, with path + byte-size evidence |
223
+ | 2 | `WATCHDOG: INTEGRATED` | a worktree or events file vanished because the Lead integrated it mid-sweep |
224
+ | 3 | `WATCHDOG: STALL` | file growth and process activity both stopped past `stall_after_min` plus any duration hint |
225
+ | 4 | `WATCHDOG: REPEAT` | the last four parsed command events were identical and need an intentional-vs-stuck ruling |
226
+
227
+ A job is DONE only when its report's final non-blank line starts with `STATUS:`.
228
+ Report existence alone is not done. Liveness is output-file growth plus
229
+ process-tree activity, weighed against any duration hint — never wall-clock alone.
230
+
231
+ ## Monitor fallback template
232
+
233
+ Use only when the backend cannot wake the Lead from a background process exit; it
234
+ counts against the concurrency cap while running.
235
+
236
+ <!-- lead-monitor-fallback-template:start -->
237
+ ```text
238
+ You are the detection-only fallback monitor for this dispatch wave. You never
239
+ kill, nudge, or decide -- you only observe and report evidence.
240
+
241
+ In-flight jobs:
242
+ - Issue #<n>, events <path>, report docs/jobs/<issue-slug>-01.md,
243
+ worktree <path>, duration hint <hint or none>. (one line per job)
244
+
245
+ Sweep every ~10 minutes. For each job, check events/report byte growth, process
246
+ activity by command-line/worktree match, and repeated identical commands in the
247
+ tail. A quiet events file on a single sweep is normal model thinking.
248
+
249
+ Quiet exit is allowed ONLY when, for every job, you list the report path and byte
250
+ size as evidence. If a worktree or events file vanished because the Lead
251
+ integrated the job mid-sweep, exit INTEGRATED and list the vanished path. If you
252
+ cannot verify something from this sandbox, state what you cannot verify rather
253
+ than assuming the job is done. Any stall or repeat concern exits immediately with
254
+ the job id, minutes since last growth, process evidence, and a tail excerpt.
255
+ ```
256
+ <!-- lead-monitor-fallback-template:end -->
257
+
258
+ ## Respawn-with-answer template
259
+
260
+ Respawn-over-resume is the default recovery: a fresh builder spawns into the same
261
+ issue's job. The respawn block is built from four pieces — the original issue
262
+ body (unchanged), the Lead's answer or ruling (posted as an issue comment first,
263
+ copied verbatim into the spawn context), what the previous session completed
264
+ (read from its job report and the worktree's actual `git status`/`git diff`,
265
+ never assumed), and the unchanged boundaries.
266
+
267
+ ```text
268
+ You are resuming issue #<n>. Do not redo completed edits; working-tree edits
269
+ survived unless the command output below proves otherwise.
270
+
271
+ Previous session completed (from docs/jobs/<issue-slug>-01.md and worktree state):
272
+ <summary of file:line evidence>.
273
+
274
+ Lead's answer/ruling (also posted on issue #<n>):
275
+ <answer, diagnosis, or rescue root cause>.
276
+
277
+ Required route-around (sandbox-hang cases only):
278
+ - Run exactly: <command with in-workspace temp/cache paths>.
279
+ - Run check commands sequentially only.
280
+
281
+ Boundaries remain:
282
+ - MAY TOUCH: <files>
283
+ - MUST NOT TOUCH: <files>
284
+ - Report path: docs/jobs/<issue-slug>-01.md
285
+ - End with exactly one STATUS line.
286
+ ```
287
+
288
+ ## Builder block template
289
+
290
+ ```text
291
+ Execute the spec below. Operating rules:
292
+
293
+ PHASE 0 - Before any code: reply with your plan and EVERY disagreement you have
294
+ with this spec, with reasons, citing real files in this repo. Silent compliance
295
+ is a failure. Silent scope additions are a failure. If you have no disagreements,
296
+ state what you checked before concluding the spec is sound. Verify the named
297
+ APIs/formats/versions against the live dependencies before planning around them.
298
+
299
+ PHASE 1 - The files under docs/checks/ are read-only at all times; editing them
300
+ fails the slice regardless of results.
301
+
302
+ PHASE 2 - Build YOUR JOB ONLY: exactly the files listed in BOUNDARIES. Files
303
+ outside your job belong outside your authority - touching them fails your job. No
304
+ placeholder implementations - search the codebase before implementing; full
305
+ implementations only. No silent fallbacks or success-shaped defaults - never
306
+ swallow an error to make output look right. No unrequested backwards-
307
+ compatibility shims. Fail loudly, with context. Exception: fallbacks or compat
308
+ code are allowed only when the spec explicitly requests them. Verify your work by
309
+ running the job's check commands and record the verbatim output. Do NOT commit -
310
+ the Lead commits and merges after verification. Do NOT delete lock files or
311
+ escalate privileges if a git command fails; record the exact error and continue.
312
+
313
+ SANDBOX POLICY - All temp/basetemp/cache paths MUST be inside the workspace
314
+ (.lead/tmp/<purpose>); never system temp. Run check commands SEQUENTIALLY. The
315
+ spec may declare duration hints for known-long commands; they are context, not
316
+ kill ceilings. If a command appears stalled - no output growth and no process
317
+ activity well past its hint - record the exact command and stop the job; the
318
+ watchdog and Lead own stall handling. A filesystem/sandbox error on a path is
319
+ environmental: record it and route around it - never retry the same path.
320
+
321
+ When done, write your job report to docs/jobs/<issue-slug>-01.md with RAW results
322
+ only - tables, numbers, command output - no interpretation. Every status claim
323
+ must be backed by a command result from this run. Mirror your final STATUS line
324
+ as a comment on your issue when gh is available; otherwise write "MIRROR: LEAD"
325
+ in the report and continue. End the report with exactly one status line:
326
+ STATUS: COMPLETE | COMPLETE_WITH_CONCERNS (list them) | BLOCKED (exact blocker + what you tried).
327
+ Verdicts belong to the reviewer and the human. Persist until your job is handled
328
+ end to end.
329
+
330
+ === OBJECTIVE (and why) ===
331
+ ...
332
+ === OUTPUT FORMAT ===
333
+ ...
334
+ === TOOL GUIDANCE (verification commands; verify-against-reality list) ===
335
+ ...
336
+ === BOUNDARIES (may touch / must not touch / out of scope) ===
337
+ ...
338
+ === DISAGREEMENT RULINGS (from last session) ===
339
+ ...
340
+ === ACCEPTANCE CHECKS (frozen at docs/checks/<slice>.md - read-only) ===
341
+ ...
342
+ ```
@@ -0,0 +1,103 @@
1
+ # Factory-loop reference
2
+
3
+ The loop is one Lead session that runs the factory to completion after approval.
4
+ GitHub issues carry coordination state; git carries specs and frozen checks. The
5
+ Lead dispatches the ready issues, sleeps, and wakes only on an event.
6
+
7
+ Parallel rules: reviewers dispatch immediately and run concurrently for every
8
+ DONE, never queued behind another review; the ready-issue frontier recomputes on
9
+ every merge, not at wave boundaries; independent bookkeeping batches into
10
+ parallel calls; merges, synthesis, and the critic pass stay serial by design.
11
+
12
+ ## Factory block procedure
13
+
14
+ 1. **Dispatch the ready issues.** Compute the ready set (every issue whose
15
+ blockers are closed), up to 5 builder jobs plus one watchdog (see `dispatch.md`
16
+ "## Watchdog dispatch"). Check `docs/STOP` and `docs/PAUSE` before every wave.
17
+ 2. **Sleep.** Zero Lead work between dispatch and the next event — no polling.
18
+ 3. **Wake on exactly one event:**
19
+ - **Job DONE.** Send the fixed reviewer template to one fresh reviewer; record
20
+ the verdict as an issue comment; merge on PASS, diagnose on FAIL.
21
+ - **Job BLOCKED.** A blocker comment is a completion event. Read it, rule an
22
+ answer, and respawn a fresh builder with the answer in its spawn context
23
+ (`dispatch.md` "## Respawn-with-answer template"). A running job never
24
+ re-reads its own comments — the spawn context is the only delivery channel.
25
+ - **Watchdog ANOMALY.** Read the evidence and rule one of: healthy-long-run
26
+ (relaunch the watchdog, sleep again), needs a nudge or answer, or wedged
27
+ (kill the job, discard its worktree, respawn from the frozen check with a
28
+ route-around).
29
+ 4. **Recompute the ready set.** Closing an issue may unblock others.
30
+ 5. **Repeat** until no issues remain open, then post the end-of-run digest.
31
+
32
+ ## Watchdog protocol
33
+
34
+ Launch the script watchdog at wave dispatch; its typed exit wakes the Lead.
35
+
36
+ - Exit 0 `ALL_DONE` -> proceed to the review backlog for every report listed by
37
+ path and byte size.
38
+ - Exit 2 `INTEGRATED` -> benign mid-sweep integration; relaunch if jobs remain.
39
+ - Exit 3 `STALL` -> run the rescue ladder: inspect the named job, kill stuck
40
+ children if needed, discard a wedged worktree, and respawn from the frozen
41
+ check with a route-around.
42
+ - Exit 4 `REPEAT` -> rule intentional-vs-stuck before acting; deliberate polling
43
+ loops are a known false positive.
44
+
45
+ Backends without background-exit notifications use the LLM fallback template in
46
+ `dispatch.md`; it keeps the same detection-only boundary and per-job evidence.
47
+
48
+ ## Verdict comments
49
+
50
+ Judgment is recorded on the issue, not in a file. One comment per reviewed job
51
+ carries: per-check PASS/FAIL/INVALID, a checks-integrity verdict, a
52
+ diff-vs-intent verdict, the slice call, and the decisive reason tied to raw
53
+ evidence (exact `gh` command in `dispatch.md` "## Issue conventions"). The
54
+ reviewer's intent context is exactly the frozen check file, the spec, the job
55
+ report, and `docs/jobs/<issue-slug>-rulings.md`. That rulings file is Lead-owned,
56
+ append-only, and committed before reviewer dispatch. No verdict comment on an
57
+ issue means the next block must not build on it as accepted; you may re-run review
58
+ with a fresh reviewer if evidence is missing, but may not fill in a verdict from
59
+ memory. The issue closes on merge.
60
+
61
+ ## Failure ladder
62
+
63
+ First FAIL on an issue: diagnose from the reviewer's evidence (not the full
64
+ diff), optionally fan out researchers (`research.md`) to inform the diagnosis,
65
+ fix the input — issue text, missing context, or a forbidden-pattern note — and
66
+ respawn a fresh builder at the same tier. Tier is set once, at decomposition, and
67
+ never changes because a job failed. Second FAIL on the same issue after an
68
+ intervention: re-decompose the issue or escalate to the digest. A merge conflict
69
+ is a decomposition failure, not a build failure: kill the conflicting job and
70
+ re-slice; never hand-resolve builder conflicts.
71
+
72
+ ## Escalation digest
73
+
74
+ Batched on the tracking issue instead of interleaved per-job noise: completed and
75
+ failed jobs with verdicts; open blockers and the answers given; decisions the
76
+ approved spec genuinely does not answer. Ask-the-human items are batched here
77
+ unless a hard stop requires an immediate stop.
78
+
79
+ ## Hard stops
80
+
81
+ | Situation | Hard stop |
82
+ |---|---|
83
+ | `docs/STOP` exists | Stop before dispatching the next wave. |
84
+ | `docs/PAUSE` exists | Finish in-flight jobs; dispatch nothing new; ask the human. |
85
+ | No verdict comment for completed work | Do not build on it as accepted. |
86
+ | Builder touched `docs/checks/` | Automatic FAIL for that job. |
87
+ | Merge conflict | Decomposition failure: kill the job, re-slice. |
88
+ | Second FAIL on the same issue | Re-decompose or escalate to the digest. |
89
+ | Two consecutive KILLs | Stop the factory and ask the human. |
90
+ | Watchdog reports an anomaly | Rule on it before any further dispatch on that job. |
91
+ | Blocker collides with a recorded assumption | Ask the human; a spec decision surfacing late. |
92
+ | Session context degrades | End the session; the next session grounds from the tracker and git. |
93
+ | Scope grows beyond the approved spec | Stop the factory. |
94
+ | High-stakes issue | Add cross-model review before CONTINUE. |
95
+
96
+ ## Context discipline
97
+
98
+ - Delegate heavy reading to reviewer, watchdog, or builder subagents; the Lead
99
+ stays thin and never reads a full diff directly.
100
+ - The tracker and git are the memory: specs, frozen checks, verdict comments, and
101
+ job reports carry state across sessions, not the conversation.
102
+ - Compact proactively when the harness supports it. Ending a degraded session is
103
+ free because the tracker and git are the memory.
@@ -0,0 +1,34 @@
1
+ {
2
+ "//": "Provider registry for techlead-loop model routing. Edit this ONE file when a model generation ships or a CLI flag changes. `.lead/models.json` (repo) or `~/.lead/models.json` (user) can add or override providers without touching the skill.",
3
+ "providers": {
4
+ "claude": {
5
+ "cli": "claude",
6
+ "kind": "claude",
7
+ "default_model": "fable",
8
+ "default_effort": "xhigh",
9
+ "efforts": ["low", "medium", "high", "xhigh", "max"],
10
+ "models": {
11
+ "fable": "claude-fable-5",
12
+ "opus": "claude-opus-4-8",
13
+ "sonnet": "claude-sonnet-5",
14
+ "haiku": "claude-haiku-4-5"
15
+ },
16
+ "tier_down": {
17
+ "fable": "sonnet",
18
+ "opus": "sonnet",
19
+ "sonnet": "haiku"
20
+ }
21
+ },
22
+ "codex": {
23
+ "cli": "codex",
24
+ "kind": "codex",
25
+ "default_model": "best",
26
+ "default_effort": "xhigh",
27
+ "efforts": ["low", "medium", "high", "xhigh"],
28
+ "models": {
29
+ "best": "gpt-5.5"
30
+ },
31
+ "tier_down": {}
32
+ }
33
+ }
34
+ }
@@ -0,0 +1,76 @@
1
+ # Inline research fan-out (for the build loop)
2
+
3
+ Read this only when a research trigger fires inside the factory (an oddity, a
4
+ failure diagnosis, a load-bearing fact the spec depends on). The fan-out uses the
5
+ `researcher` role as parallel web-research subagents — read-only, live search, on
6
+ the flat-rate subscription — and the Lead keeps all judgment: it verifies the
7
+ load-bearing claims and writes the spec itself. For discovery-scale research, use
8
+ the separate `/lead-research` skill; this is the slice-scale version.
9
+
10
+ ## Fan out
11
+
12
+ Resolve the researcher model with `python skills/lead/config.py --role
13
+ researcher`. Decompose the question into 3–5 narrow, NON-OVERLAPPING questions —
14
+ cover different angles, not one angle five times. Typical split: official
15
+ docs/reference; changelog / breaking changes; community failure reports;
16
+ alternatives/comparisons; security/operational constraints.
17
+
18
+ One fresh researcher per question, all launched in parallel in the background.
19
+ Take the exact command from the resolver; for Codex it looks like:
20
+
21
+ ```bash
22
+ codex exec -C <repo-root> --sandbox read-only -c web_search="live" \
23
+ -m <model-id> -c model_reasoning_effort="<effort>" \
24
+ -o .lead/research/<NN>-<topic>.md \
25
+ - < .lead/research/<NN>-<topic>.prompt.md
26
+ ```
27
+
28
+ Write each research block to a `.prompt.md` file and pass it via stdin (`-`),
29
+ never as a shell argument — quote-mangling shells make the CLI hang on stdin.
30
+ Launch ONE canary researcher and confirm it starts cleanly before fanning out. If
31
+ the resolved researcher is a Claude row or Codex is unavailable, run the fan-out
32
+ as read-only Claude subagents with web search — the block below works verbatim.
33
+
34
+ - `--sandbox read-only`: researchers never write to the repo.
35
+ - Effort is coverage-tier (`high`, not `xhigh`); synthesis happens on the Lead's
36
+ side.
37
+ - Scope each researcher to ≤5 subjects and put hard context rules in the block
38
+ (snippet over page; quote ≤2 sentences; stop the moment you can answer) — a
39
+ researcher that fills its context window dies without writing its output.
40
+ Bisect and re-dispatch a dead researcher; do not re-run it as-is.
41
+
42
+ ## Research block template
43
+
44
+ ```text
45
+ You are a web research agent. Answer ONE question. Do not write code, do not make
46
+ recommendations - judgment belongs to the Lead who reads your output.
47
+
48
+ QUESTION: <one narrow question>
49
+
50
+ OUTPUT FORMAT - a markdown report, <= ~2,500 tokens total:
51
+ - Findings as bullets. EVERY finding carries: a source tag (e.g. [S3]), source
52
+ date (if shown), the exact figure or a short direct quote, and a confidence tag
53
+ (high = primary source / med = reputable secondary / low = single blog or forum).
54
+ - Prefer primary sources (official docs, changelogs, release notes, source code)
55
+ over blog posts. Record exact version numbers and dates.
56
+ - When sources disagree, report the disagreement - do not resolve it.
57
+ - If you cannot find evidence, write NOT FOUND - never infer or fill gaps from
58
+ prior knowledge without flagging it.
59
+ - End with a numbered source list - every source URL appears EXACTLY ONCE - then
60
+ the 2-3 findings most likely to change an implementation decision.
61
+ ```
62
+
63
+ ## Gather (Lead — your work, not a subagent's)
64
+
65
+ 1. Read every findings file in `.lead/research/`.
66
+ 2. Identify the **load-bearing claims** — the facts the spec will depend on (an
67
+ API shape, a version constraint, a limit, a deprecation). Adversarially verify
68
+ each against a second independent source or the live dependency itself.
69
+ Discard single-source low-confidence claims or mark them as open questions.
70
+ 3. Write `docs/spec/<slice>.md`: problem, decision + why, requirements, non-goals,
71
+ verified facts **with citations**, open questions for the human. You write it;
72
+ researchers gather, you judge.
73
+ 4. Commit the spec. Raw findings stay in `.lead/research/` (gitignored) — only the
74
+ distilled, cited spec is repo memory.
75
+ 5. The slice spec references this spec instead of restating it; the builder's
76
+ PHASE 0 is expected to challenge the spec's claims.
@@ -0,0 +1,114 @@
1
+ param([string]$RepoRoot = ".")
2
+
3
+ # Read-only status tree for a techlead-loop run (Windows). Reads run artifacts
4
+ # plus `gh`; never a new state store. Callers print the output verbatim.
5
+
6
+ $root = (Resolve-Path -LiteralPath $RepoRoot).Path
7
+ if (-not (Test-Path -LiteralPath $root)) { Write-Output "unreadable repo: $RepoRoot"; exit 1 }
8
+
9
+ $useColor = ($Host.UI.RawUI -ne $null) -and (-not $env:NO_COLOR)
10
+ function Glyph($ch, $code) { if ($useColor) { "`e[$($code)m$ch`e[0m" } else { $ch } }
11
+ $gMerged = Glyph ([char]0x2713) 32
12
+ $gReview = Glyph ([char]0x25D0) 36
13
+ $gBlocked = Glyph "!" 31
14
+ $gReported = Glyph ([char]0x25A3) 35
15
+ $gBuilding = Glyph ([char]0x25CF) 34
16
+ $gQueued = Glyph ([char]0x2298) 33
17
+ $gReady = Glyph ([char]0x25CB) 37
18
+
19
+ function NewestSpec {
20
+ $specDir = Join-Path $root "docs/spec"
21
+ if (-not (Test-Path -LiteralPath $specDir)) { return "unknown" }
22
+ $newest = Get-ChildItem -LiteralPath $specDir -Filter *.md -File -ErrorAction SilentlyContinue |
23
+ Sort-Object LastWriteTimeUtc -Descending | Select-Object -First 1
24
+ if ($newest) { return $newest.Name } else { return "unknown" }
25
+ }
26
+ function TailText($Path) {
27
+ if (-not (Test-Path -LiteralPath $Path)) { return "" }
28
+ $bytes = [System.IO.File]::ReadAllBytes($Path)
29
+ $len = [Math]::Min(4096, $bytes.Length)
30
+ $tail = $bytes[($bytes.Length - $len)..($bytes.Length - 1)]
31
+ return [System.Text.Encoding]::UTF8.GetString($tail)
32
+ }
33
+ function StatusLine($Path) {
34
+ $s = ""
35
+ foreach ($line in ((TailText $Path) -split "`r?`n")) {
36
+ if ($line -match '^\xEF\xBB\xBF?STATUS:') { $s = ($line -replace '^\xEF\xBB\xBF?STATUS:\s*', '') }
37
+ elseif ($line -match '^STATUS:') { $s = ($line -replace '^STATUS:\s*', '') }
38
+ }
39
+ return $s
40
+ }
41
+ function Slugify($s) { (($s.ToLower() -replace '[^a-z0-9]', '-') -replace '-+', '-').Trim('-') }
42
+ function ReportPath($slug) {
43
+ $inWt = Join-Path $root ".lead/wt/$slug-01/docs/jobs/$slug-01.md"
44
+ if (Test-Path -LiteralPath $inWt) { return $inWt }
45
+ return (Join-Path $root "docs/jobs/$slug-01.md")
46
+ }
47
+ function Phase($slug, $state, $blockers) {
48
+ if ($state -eq "CLOSED") { return "$gMerged MERGED" }
49
+ if ($state -eq "OPEN" -and $blockers) { return "$gQueued QUEUED" }
50
+ $rep = ReportPath $slug
51
+ $review = Get-ChildItem -LiteralPath (Join-Path $root ".lead/wt") -Filter "$slug-01.review*.md" -File -ErrorAction SilentlyContinue | Select-Object -First 1
52
+ if ((Test-Path -LiteralPath $rep) -and $review) { return "$gReview REVIEWING" }
53
+ if ((StatusLine $rep) -like "BLOCKED*") { return "$gBlocked BLOCKED" }
54
+ if (Test-Path -LiteralPath $rep) { return "$gReported REPORTED" }
55
+ if (Test-Path -LiteralPath (Join-Path $root ".lead/wt/$slug-01")) { return "$gBuilding BUILDING" }
56
+ return "$gReady READY"
57
+ }
58
+ function TrackerLines {
59
+ $jq = '. as $all | ([ $all[] | select(.parent != null) | .parent.number ] | unique) as $pnums | ([ $all[] | select(.state == "OPEN") | select(.number as $n | $pnums | index($n)) ] | map(.number) | max) as $t | if $t == null then "NOOPENRUN" else ("TRACK\t\($t)", ($all[] | select(.parent != null and .parent.number == $t) | [ "SUB", (.number|tostring), .state, ((.blockedBy.nodes // []) | map(select(.state == "OPEN") | (.number|tostring)) | join(",")), .title ] | @tsv)) end'
60
+ if ($env:STATUS_GH_STUB -and (Test-Path -LiteralPath $env:STATUS_GH_STUB)) { return Get-Content -LiteralPath $env:STATUS_GH_STUB }
61
+ if (-not (Get-Command gh -ErrorAction SilentlyContinue)) { return $null }
62
+ Push-Location $root
63
+ try { gh issue list --state all --limit 200 --json number,title,state,parent,blockedBy --jq $jq } finally { Pop-Location }
64
+ }
65
+
66
+ $branch = "unknown"
67
+ if (Test-Path -LiteralPath (Join-Path $root ".git")) {
68
+ $b = git -C $root branch --show-current 2>$null
69
+ if ($b) { $branch = $b.Trim() }
70
+ }
71
+ $trackerTsv = TrackerLines
72
+ $tracker = ($null -ne $trackerTsv)
73
+ $tracking = $null
74
+ if ($tracker) {
75
+ foreach ($row in $trackerTsv) {
76
+ $cols = $row -split "`t"
77
+ if ($cols[0] -eq "TRACK") { $tracking = $cols[1] }
78
+ }
79
+ }
80
+ $slugDirs = Get-ChildItem -LiteralPath (Join-Path $root ".lead/wt") -Directory -Filter "*-01" -ErrorAction SilentlyContinue |
81
+ ForEach-Object { $_.Name -replace '-01$', '' } | Sort-Object -Unique
82
+
83
+ if ((-not $tracker -or -not $tracking) -and -not $slugDirs) {
84
+ Write-Output "NO ACTIVE FACTORY RUN"
85
+ Write-Output "spec: $(NewestSpec)"
86
+ exit 0
87
+ }
88
+ Write-Output "STATUS TREE spec: $(NewestSpec) branch: $branch"
89
+ if ($tracker -and $tracking) { Write-Output "tracker: #$tracking" }
90
+ elseif ($tracker) { Write-Output "tracker: no open run" }
91
+ else { Write-Output "tracker: unavailable (local view)" }
92
+ Write-Output "LEAD: local view"
93
+ $cfg = (Get-ChildItem -LiteralPath (Join-Path $root ".lead/tmp") -Filter "wd-*.json" -File -ErrorAction SilentlyContinue | Measure-Object).Count
94
+ $proc = if (Get-CimInstance Win32_Process -ErrorAction SilentlyContinue | Where-Object { $_.CommandLine -match 'watchdog\.(ps1|sh)' }) { "True" } else { "False" }
95
+ Write-Output "WATCHDOG: process=$proc config=$cfg"
96
+
97
+ if ($tracker -and $tracking) {
98
+ foreach ($row in $trackerTsv) {
99
+ $cols = $row -split "`t"
100
+ if ($cols[0] -ne "SUB") { continue }
101
+ $num = $cols[1]; $state = $cols[2]; $blockers = $cols[3]; $title = $cols[4]
102
+ $slug = Slugify $title
103
+ $p = (Phase $slug $state $blockers) -split ' ', 2
104
+ $extra = if ($p[1] -eq "QUEUED") { " blocked-by: $blockers" } else { "" }
105
+ Write-Output "$($p[0]) #$num $title .lead/wt/$slug-01$extra"
106
+ }
107
+ } else {
108
+ foreach ($slug in $slugDirs) {
109
+ $p = (Phase $slug "" "") -split ' ', 2
110
+ if ($p[1] -in @("BUILDING", "BLOCKED", "REVIEWING", "REPORTED")) {
111
+ Write-Output "$($p[0]) $slug .lead/wt/$slug-01"
112
+ }
113
+ }
114
+ }