tldr-experts 0.3.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 (75) hide show
  1. package/CHANGELOG.md +913 -0
  2. package/LICENSE +21 -0
  3. package/README.md +174 -0
  4. package/dist/hooks/answer-capture.js +174 -0
  5. package/dist/hooks/budget-gate.js +173 -0
  6. package/dist/hooks/chunk-0bt6yb2q.js +88 -0
  7. package/dist/hooks/chunk-1zwcxd3f.js +66 -0
  8. package/dist/hooks/chunk-9gb21660.js +66 -0
  9. package/dist/hooks/chunk-a8p2rc94.js +20 -0
  10. package/dist/hooks/chunk-ae6bkfs5.js +0 -0
  11. package/dist/hooks/chunk-azctppjh.js +198 -0
  12. package/dist/hooks/chunk-g395gk7e.js +503 -0
  13. package/dist/hooks/chunk-j234zf0t.js +339 -0
  14. package/dist/hooks/chunk-kw4tffzf.js +139 -0
  15. package/dist/hooks/chunk-p274ckxv.js +7435 -0
  16. package/dist/hooks/chunk-sdjnnmzz.js +497 -0
  17. package/dist/hooks/chunk-t56k6146.js +14 -0
  18. package/dist/hooks/chunk-t8tdv11p.js +35 -0
  19. package/dist/hooks/chunk-x98qs959.js +302 -0
  20. package/dist/hooks/chunk-y0jdr3et.js +627 -0
  21. package/dist/hooks/claim-sources.js +72 -0
  22. package/dist/hooks/dod-gate.js +198 -0
  23. package/dist/hooks/no-reask.js +67 -0
  24. package/dist/hooks/session-start.js +1348 -0
  25. package/dist/hooks/statusline.js +116 -0
  26. package/dist/tldrx.js +34219 -0
  27. package/env.yml +79 -0
  28. package/package.json +60 -0
  29. package/plugin/.claude-plugin/plugin.json +9 -0
  30. package/plugin/README.md +113 -0
  31. package/plugin/agents/README.md +22 -0
  32. package/plugin/hooks/hooks.json +107 -0
  33. package/plugin/skills/tldrx/SKILL.md +170 -0
  34. package/stages/build/stage.md +70 -0
  35. package/stages/build/stage.yml +54 -0
  36. package/stages/how/stage.md +89 -0
  37. package/stages/how/stage.yml +53 -0
  38. package/stages/plan/stage.md +89 -0
  39. package/stages/plan/stage.yml +48 -0
  40. package/stages/watch/stage.md +85 -0
  41. package/stages/watch/stage.yml +61 -0
  42. package/stages/what/stage.md +89 -0
  43. package/stages/what/stage.yml +61 -0
  44. package/templates/budget.yml +17 -0
  45. package/templates/competencies.yml +14 -0
  46. package/templates/env.yml +19 -0
  47. package/templates/epic.md +38 -0
  48. package/templates/expert.md +51 -0
  49. package/templates/experts/architect.md +77 -0
  50. package/templates/experts/delivery.md +76 -0
  51. package/templates/experts/developer.md +78 -0
  52. package/templates/experts/operations.md +74 -0
  53. package/templates/experts/product.md +78 -0
  54. package/templates/facts.yml +18 -0
  55. package/templates/handoff.md +63 -0
  56. package/templates/process.yml +33 -0
  57. package/templates/questions.md +63 -0
  58. package/templates/run.yml +24 -0
  59. package/templates/story.md +55 -0
  60. package/templates/watcher.md +68 -0
  61. package/templates/waves.yml +17 -0
  62. package/templates/workspace.yml +30 -0
  63. package/workflows/bugfix.yml +26 -0
  64. package/workflows/docs.yml +23 -0
  65. package/workflows/feature.yml +22 -0
  66. package/workflows/hotfix.yml +24 -0
  67. package/workflows/integration.yml +24 -0
  68. package/workflows/migration.yml +24 -0
  69. package/workflows/performance.yml +23 -0
  70. package/workflows/prototype.yml +24 -0
  71. package/workflows/refactor.yml +24 -0
  72. package/workflows/retro.yml +20 -0
  73. package/workflows/security-patch.yml +23 -0
  74. package/workflows/spike.yml +23 -0
  75. package/workflows/upgrade.yml +23 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,913 @@
1
+ # Changelog
2
+
3
+ ## 0.3.0 — 2026-08-30
4
+
5
+ Every measurement below was taken on a real workspace on 2026-08-29 unless another date is
6
+ given. A claim with no measurement behind it is marked `[assumption]` where it appears in the
7
+ code.
8
+
9
+ ### Added
10
+
11
+ **Commands**
12
+
13
+ - `tldrx status [--json]` — one report of everything in the workspace waiting on a human, in
14
+ the order the sources block each other: open init questions, every `split.yml` still
15
+ `status: proposed`, every open run with the exact command it needs, and every expert a stage
16
+ will load with zero evidence. `--json` gives `{root, pending, items[], advice[]}`; `items`
17
+ are the blockers, `advice` blocks nothing. Deterministic and read-only; exits `0` whatever
18
+ it finds, `3` only when there is no `.tldrx/` at all. Runs are dependency-aware: one whose
19
+ `triage.depends_on` sibling is not `done` shows `blocked by <slug>` and is offered no
20
+ command, and the first runnable one is marked `← next`. A run folder that does not validate
21
+ gets its own item rather than vanishing.
22
+ - `tldrx expert train <name> --area <a> [--mode light|full]` — training runs. It used to print
23
+ a prompt and exit `64`. A deterministic pre-pass picks the files (map domains, graphify
24
+ communities, a bounded keyword grep; capped at 40 files / 96 KB with everything over the cap
25
+ named as "not read"); one sub-agent writes `knowledge/<area>.md`; the framework re-reads it
26
+ off disk and validates it with the same parser the `claim-sources` hook uses; evidence is
27
+ **derived** from the citations, never asserted. `--mode full` adds a second sub-agent mining
28
+ `tldrx-work/**/{handoff,retro}.md` — Claude Code transcripts are deliberately out of scope,
29
+ they carry no citation anything can re-resolve. `--max-usd` (default `$2.00`), `--model`,
30
+ `--effort`, `--yolo`, `--prepare`/`--commit`, `--print-prompt`.
31
+ - `tldrx expert recompute [<name>] [--json]` — recomputes `areas[].level` from the evidence
32
+ already on disk. Only the headless/`--commit` path ever wrote a level, so a human who pasted
33
+ the `--print-prompt` prompt into their own session ended with `level: 0` while the formula
34
+ computed 5, and nothing could settle it. Idempotent; touches neither `status` nor
35
+ `last_trained`; spawns nothing.
36
+ - `tldrx install --claude` — writes the facilitator into a real `.claude/` without the plugin
37
+ and without `init`: the skill file (marker `<!-- tldrx-managed -->`, `disable-model-invocation`
38
+ intact) plus a merge of two `settings.json` keys — the six hooks as eight handlers on four
39
+ events, each `tldrx hook <name>`, and `statusLine`. `--project` (default; refuses outside a
40
+ git repo) `--user` `--skill-only` `--no-hooks` `--no-statusline` `--force-statusline`
41
+ `--uninstall` `--dry-run`.
42
+ - `tldrx hook <name>` and `tldrx statusline` — run one hook script (`dist/hooks/<name>.js`, or
43
+ `src/hooks/<name>.ts` in a source checkout), passing stdin, stdout, stderr and the exit code
44
+ through unchanged. That is what lets a *committed* `settings.json` name a hook without an
45
+ absolute path. The plugin keeps `${CLAUDE_PLUGIN_ROOT}`, because it has to work for someone
46
+ who cloned the repo and installed nothing.
47
+ - `tldrx interview [--run <id>] [--init] [--yes-to-defaults]` — the Interview step in a
48
+ terminal, recording through the same `src/core/answers/` path as `tldrx answer` and the
49
+ `answer-capture` hook. It never answers for you: end of input, `s` and `q` all leave the
50
+ question `status: open`, and a letter the question does not offer is reported and skipped.
51
+ Piped stdin is one answer per line.
52
+ - `tldrx run auto [<run>]` — the headless loop: `next` until a human gate or open question
53
+ (`4`), a failure (`5`), a budget refusal (`2`), `--until <stage>` reached or the run finished
54
+ (`0`). Holds no state, so killing it leaves a run `tldrx next` picks up unchanged.
55
+ `--max-usd` is a ceiling on the LOOP's total spend, checked *between* stages.
56
+ - `tldrx run unlock [<run>] [--force]` and `tldrx run cancel [<run>] --note <t> [--force]` —
57
+ the two ways out of a stuck run, with new `run.unlocked` / `run.cancelled` events and an
58
+ additive `cancelled: {by, at, note}` on `run.yml` (which is what lets a FAILED run be closed
59
+ without overwriting the failure on its stages). Nothing is deleted; `tldrx replay` still
60
+ reads the whole thing.
61
+ - `tldrx cost [<run>] [--all] [--json]` — what was actually spent, read off `agent.result`
62
+ events and nothing else. Per attempt, per stage, per run. All four token counters, both
63
+ prompt-cache halves included.
64
+ - `tldrx run estimate [<run>] [--json]` — the only command that guesses, and it says so. The
65
+ input half is measured (the same prompt assembly and ledger `next` uses); the output half is
66
+ the median output tokens of past attempts at that stage id, and with no history it prints no
67
+ estimate. Prices and context windows live in one dated `[assumption]` file,
68
+ `src/core/budget/modelPrices.ts`.
69
+ - `tldrx questions lint [--run <id>] [--fix] [--area <a>]` — names every `questions.md` block
70
+ the §2.7 parser cannot see and exits `2`. `--fix` converts the prose form
71
+ (`### Qn — …` / `**Answer:**`) into the grammar **without changing a word**; it does not
72
+ invent the `[src: …]` token §2.7 wants, it lists the blocks that still need one.
73
+ - `tldrx reject --stage <phase>/<stage> --note <t>` — revokes an approval already given,
74
+ whoever signed it. The cursor moves back, one new `gate.revoked` event carries `signed_by`,
75
+ and later stages that had run are marked `stale: true` (additive, cleared when the stage
76
+ re-runs). It is the one verb that may reopen a FINISHED run.
77
+ - `tldrx seed triage <path>` — free, offline, no LLM: `inventory.md` + `inventory.json` with
78
+ per-document tokens, headings, cross-links, `Status:`, open markers, and a **code-derived**
79
+ flag set when ≥ 8 distinct path-like tokens it cites resolve to real files. Threshold:
80
+ `--threshold-tokens`, else the new optional `seed_triage.threshold_tokens` in
81
+ `workspace.yml`, else 20,000. Both directions of that heuristic were measured on a real
82
+ design folder (31 files, ~66k tokens): a 152 KB legacy inventory document cites **294**
83
+ distinct path-like tokens and **0** resolve — that repo is a rewrite and those paths belong to
84
+ the system it replaced — so it is *not* flagged, where a rule that counted citations instead
85
+ of resolving them would have called it code-derived and been wrong; `ADR-D005-ORDERING.md`,
86
+ written against the current tree, cites 12 and **8 resolve**, so it *is*. An earlier reading
87
+ the same afternoon said 24 files / ~44k tokens: the folder gained seven ADRs between the two
88
+ runs, which is why the verdict is printed rather than remembered.
89
+ - `tldrx seed triage <path> --propose` — ONE sub-agent (effort `low`, `--max-usd 1.00`) that
90
+ proposes a split and **never creates a run**. The answer is validated against *this*
91
+ workspace before a byte is written; failure is whole (exit `5`, no `split.yml`, raw answer
92
+ kept).
93
+ - `tldrx seed answer <split.yml> <Qid> "<text>"` — records a decision beside the question. The
94
+ key is human-owned: the propose schema still refuses it, so a model can never write one.
95
+ - `tldrx seed apply <split.yml> [--dry-run]` — the human gate. Refuses anything not
96
+ `status: proposed`, revalidates the file you were invited to edit, and creates the runs in
97
+ topological order through the same `createRun` that `run new` calls.
98
+ - `tldrx tickets sync|status` — the optional ticket mirror (off unless `process.yml` names a
99
+ `ticket_tool.kind`). GitHub through the `gh` CLI so no token is handled here; Jira through
100
+ REST v3 with `JIRA_BASE_URL`/`JIRA_EMAIL`/`JIRA_API_TOKEN`, a missing one being exit `1`
101
+ naming all three *before* `03-plan/` is read. Both providers take their transport as an
102
+ argument, so **no test in this repo makes an outbound call or spawns `gh`**. New
103
+ `ticket.synced` event (`stage: null`, `cost_usd: 0` — no model ran, and a mirror is not a
104
+ stage).
105
+ - `tldrx <command> --help` now answers the question: a one-line description, the positional
106
+ arguments, every flag with its meaning and — where the set is closed — its values, one or two
107
+ real invocations, and the exit codes that command can return with what each means. One
108
+ registry, `src/cli/helpText.ts`, read by the renderer, the argv guard and a drift test.
109
+ Closed sets are imported from where they are enforced (`EFFORT_LEVELS`, `UI_MODES`, the
110
+ `workflows/*.yml` stems on disk), so a help screen cannot offer a value the validator
111
+ refuses. `tldrx --help` gains the same exit table as a legend.
112
+
113
+ **Gates, money and safety**
114
+
115
+ - **Gate policy is data.** `workflows/<scope>.yml` carries `gates:` (stage id → `human|auto`);
116
+ `tldrx run new … --gates <a,b|all|none>` overrides it and **names the HUMAN gates**; the
117
+ resolved map is frozen into `run.yml` as an additive `gates_policy:`. Absence means `human`
118
+ everywhere. Shipped defaults keep at least one human gate in every scope.
119
+ - **An `auto` gate closes only when it can show its work** — five conditions, all measured off
120
+ files that already exist, all evaluated even after one fails: checks pass, no open question
121
+ in the phase, spend inside the stage and phase ceilings, the stage did not end `failed`, and
122
+ the §2.8 claim-sources validator reports nothing (zero refused **and** zero unverified). The
123
+ §2.8 check runs whether or not the stage listed it under `checks:`. Approval goes through the
124
+ SAME path a person's does, lands `by: auto`, and writes a note carrying every value. No new
125
+ event type was invented: `gate.approved` gained `by`.
126
+ - `effort:` in `stage.yml` and `--effort <low|medium|high|xhigh|max>` on the command line — the
127
+ cost lever `--max-budget-usd` is not, because the budget flag can only end a turn already in
128
+ flight. Shipped stage defaults, all `[assumption]`: what `medium`, how `high`, plan `medium`,
129
+ build `high`, watch `low`. Recorded on `agent.spawned`/`agent.result` and every
130
+ `training.jsonl` line, so cost-per-effort becomes measurable rather than arguable.
131
+ - **A context ledger, and `prompt_max_bytes` as a refusal.** `--prepare` and `--dry-run` print
132
+ bytes per section and `pending.json` carries the same under `context:`. Over
133
+ `prompt_max_bytes` (default 160 KB, `--prompt-max-bytes` to override) the stage exits `2`
134
+ before anything spawns, naming the biggest sections and the key that shrinks each. The
135
+ model's context window is only ever a stderr warning at 80% — it and the bytes-per-token
136
+ ratio are both `[assumption]`, and refusing on two stacked assumptions would block work the
137
+ framework could have done.
138
+ - **`max_reads` — the brake `--max-budget-usd` is not.** Completed `Read`/`Glob`/`Grep` calls
139
+ are counted off the stream that is already arriving (no second model call, no extra tokens)
140
+ and the process tree is killed at the ceiling: 120 for what/how/plan, 200 build, 60 watch;
141
+ `--max-reads` to override. The attempt records `stopped_by: max_reads`, written only when a
142
+ cap bit, and the live view shows `reads 37/120`.
143
+ - `tldrx next --commit --cost-usd <n> [--tokens <n>]` — so a host session can declare what its
144
+ own sub-agent cost. Without it the task is `cost_usd: null, metered: false`.
145
+ - New `budget.raised` event with before/after for both ceilings, the actor and an optional
146
+ `--note`; `--take-from <phase>` moves money instead of adding it, refusing to cut a donor
147
+ below what it has already spent.
148
+ - `map.refreshed` is finally emitted — providers, document count, repo count — recorded against
149
+ the newest OPEN run, and the command says which. It had been in the §2.9 enum and the replay
150
+ renderer since v0 with nothing emitting it.
151
+ - `src/cli/signals.ts` — SIGINT/SIGTERM kill the sub-agent's whole process tree, record a
152
+ partial `agent.result` carrying `cost_usd: null` and `stopped_by: "signal"`, demote `running`
153
+ → `ready`, release the lock, restore the cursor and exit `130`. A second signal exits at
154
+ once. A `--prepare` bundle with nothing spawned is left alone.
155
+ - **`.tldrx/.lock`, a workspace lock held across read-modify-write.** Two processes appending to
156
+ `facts.yml` each computed `max(id) + 1`, each got `F001`, and the second save erased the
157
+ first fact outright. `FactsStore.update` holds the lock across load → append → save, and
158
+ `run new` holds it across its whole creation.
159
+ - Two new waiting kinds, `running` and `prepared`, so a stage killed between `--prepare` and
160
+ `--commit` is no longer reported as `ready` and re-spawned. `tldrx next` refuses to re-spawn
161
+ over a bundle (exit `2`) naming all three ways out; `--discard-pending` is the explicit "bin
162
+ it and run it again". Phases with an executor are exempt — Build stays `running` across
163
+ cycles by design.
164
+
165
+ **Experts**
166
+
167
+ - **A stage prompt now carries what its experts LEARNED, not only who they are.** Each loaded
168
+ expert contributes its `expert.md` body, its **star chart** (one line per area, computed from
169
+ evidence) and its **knowledge files**, most-recently-trained first by the file's own
170
+ `trained_at`, never an mtime. The prompt states that those `[src: …]` tokens already resolved
171
+ when the knowledge was accepted and may be reused verbatim as evidence. Measured on a fixture:
172
+ **1,493 bytes → 4,758 bytes**, with both areas' findings inlined.
173
+ - **A third expert-loading rule.** `experts:` and `stack_experts:` could not reach an expert
174
+ `init` seeded from this workspace's own folders. Now a `kind: domain` expert whose
175
+ front-matter `repos:` or `## Domain` paths intersect the run's repos or cited paths is loaded
176
+ too — path matches ranked first, capped at 8, deduped, deterministic. A `repos:` match only
177
+ counts in a workspace declaring two or more repos, because in a single-repo workspace it
178
+ selects everybody. Rank is a score: a direct path match is 10, a path within 2 hops in
179
+ `graphify-out/<repo>/graph.json` is 1, scores add, and score 0 means body only. The graph walk
180
+ is undirected, bounded, and degrades to an empty set on a missing or unparseable graph
181
+ (measured: 298 neighbour paths in 14 ms over a 5,091,949-byte graph).
182
+ - **`tldrx init` seeds five ROLE experts** — `product`, `architect`, `delivery`, `developer`,
183
+ `operations`, the names the shipped stage files have always listed and of which `init` seeded
184
+ only the first. A role expert's subject is the workflow, so its body ships as an editable
185
+ `templates/experts/<role>.md`, copied in once and yours after that. `kind: role` keeps it out
186
+ of the domain-match rule. Seeding is additive: an existing workspace gains the four missing
187
+ folders and keeps every `expert.md` it had, byte-for-byte. `tldrx expert create <name> --role
188
+ <slug>` writes the same seed on demand, and says which template it used.
189
+ - `.tldrx/experts/<name>/training.jsonl` (spec §2.6.1) — the §2.9 envelope with `run` replaced
190
+ by `expert` and `stage` by `area`, because training outlives every run. A REFUSED run still
191
+ writes its `agent.result`: money spent is recorded whether or not the knowledge was kept.
192
+ - `kind: test` is a first-class evidence kind at weight 1.0, the same as `code` — a test read or
193
+ run is a direct observation of behaviour.
194
+ - `tldrx expert list` gains total **evidence** and per-area **levels** columns, an ASCII star
195
+ chart, and a `loaded by: what (named), how (stack)` line per expert derived from the same
196
+ selection rule `next` runs — so "trained and never loaded" stops being invisible. `--json`
197
+ gains `evidence_count`.
198
+ - `tldrx next --prepare` prints one line per loaded expert with its bytes, plus `truncated` and
199
+ `NOT LOADED` where they apply, and `pending.json` gains an `experts:` array with the same
200
+ numbers, the reason each loaded and its knowledge file paths. An expert with zero evidence
201
+ anywhere earns one **stderr** note naming its train command — never a block.
202
+
203
+ **Knowledge that has to be worth something**
204
+
205
+ - **A citation must sustain its claim, not only resolve.** Making every `src` resolvable is a
206
+ check on the citation and says nothing about the sentence. An **execution claim** now needs a
207
+ command src — `execution claim needs a '$ <cmd> → exit <n>' src, not a file line` — because a
208
+ real `knowledge/aparece-api.md` asserted `dotnet build` exit 0, "measured, exit code captured
209
+ unpiped", citing `.tldrx/workspace.yml:19`: the line that *declares* the command. It claimed
210
+ "78/78 passed, exit 0" citing a line of the test script. Every citation resolved; none was
211
+ evidence anything ran. The rule reads prose paragraphs as well as bullets, because that header
212
+ IS a paragraph and its tokens sit mid-line where a line-anchored parser never looks. Measured
213
+ after: 7 refusals on that file, 1 on `aparece-platform`, 0 on the third.
214
+ - **Three warnings that cost a citation its evidence row without rejecting the file:**
215
+ `paraphrase` (the bullet is ≥ 90% a verbatim substring of the ±3-line neighbourhood of the
216
+ line it cites), `outside domain` (the path is outside the expert's own `## Domain` — and the
217
+ expert whose domain does contain it is named), and `duplicate src` (already on record for this
218
+ expert). None of them is a lie; they are ways of being worth nothing, and the honest response
219
+ is a level that does not move. Measured on the real corpus: 57 outside-domain and 7 duplicate
220
+ warnings across 248 bullets.
221
+ - **`## Sources` earns nothing.** It was 41 of 107 bullets in one real knowledge file and 18 of
222
+ 56 in another, every one re-citing a source cited above it. It is still validated like any
223
+ other section; it just derives no evidence, and `countFindings` stops counting it.
224
+ - **A bullet may carry its own confidence** — ending in `(measured)` / `(inferred)` /
225
+ `(assumed)`, or leading with `*measured* —`, the other spelling the real corpus uses — parsed
226
+ onto the evidence row as `confidence:`. Both spellings are stripped before the execution rule
227
+ matches: inside the annotation the word is a LABEL, and refusing a file for obeying §2.3's own
228
+ "say which of measured / inferred / assumed each claim is" would be the rule being wrong.
229
+ - **`tldrx expert list` warns on a shared citation** — `warning: shared citation <file:line> by
230
+ <a>,<b> — check for contradiction`, on stderr, when two experts cite one line with bullets
231
+ whose normalised texts differ. 16 files on the real workspace were cited by two trained
232
+ experts each and nothing compared what the two said. It resolves nothing on purpose: deciding
233
+ which expert is right is not something a deterministic tool can do.
234
+ - **The Build executor writes `retro.md` as the run goes.** Role experts train from
235
+ `tldrx-work/<run>/**/{handoff,retro}.md` and nothing else, and all five sat at level 0 because
236
+ `retro.md` existed only when a human typed `tldrx retro`. Build now appends `## Build
237
+ feedback` as each story settles — every reviewer `changes` verdict and finding, every DoD
238
+ command that failed on the first attempt with its exit code, every merge conflict, and (read
239
+ back off `events.jsonl`, since they happen between invocations) every gate rejected and every
240
+ approval revoked, with its note and what it staled. Deterministic, deduped verbatim, every
241
+ bullet carrying a `[src: …]` into the review log or the events line. `tldrx retro` carries the
242
+ section forward instead of overwriting it.
243
+
244
+ **Dashboard**
245
+
246
+ - Redesigned, and it now draws in the browser: the model rides inline in a
247
+ `<script type="application/json">` and every view is rendered client-side, so `--static` is
248
+ the same document with no server behind it. Five views behind a hash route — runs, run
249
+ detail, experts, watchers, how-to. `render.ts` stays TypeScript against `DashboardModel`, so
250
+ `tsc --strict` is still on the markup.
251
+ - Citations are first-class: `[src: …]` renders as a reference chip and `[assumption]` /
252
+ `[inference]` as a flag, marked in the DOM so the model's `handoffHtml` is styled and never
253
+ re-parsed.
254
+ - `runs[].waiting` is the CLI's own `{kind, message, questions}` from one shared derivation
255
+ (`src/core/run/waiting.ts`), and `dependsOn` / `blockedBy` / `runnable` / `order` / `chains`
256
+ come from the same `triage.depends_on` resolver `tldrx status` uses
257
+ (`src/core/run/dependencies.ts`). Two screens, one answer.
258
+ - ORDER is the default sort; the first runnable run wears the same `← next` marker the CLI
259
+ prints; chains draw as root-to-leaf PATHS, so every arrow is a real edge. An attention line
260
+ mirrors `tldrx status`.
261
+ - Stage rows carry `gatePolicy`, the execution path gains a `signed by` column, and the section
262
+ counts them the way the CLI does.
263
+ - The server checks `Host`. A request that does not name `127.0.0.1`, `localhost`, `::1` or the
264
+ host it was told to bind gets 403 before the method check — binding loopback is necessary,
265
+ not sufficient, when a page on a name the attacker owns can point a browser at 127.0.0.1.
266
+ Only the name is compared, never the port, so `ssh -L` and container port maps keep working.
267
+
268
+ **Build**
269
+
270
+ - Story branches are `story/<run-id>/<story-id>` and their worktrees
271
+ `.tldrx/worktrees/<repo>/<run-id>-<story-id>`, so two runs of one plan cannot collide.
272
+ - An `epic/<slug>` branch this run did not cut is refused (exit `2`, nothing spent) unless
273
+ `tldrx next --reuse-epic`. What the run cut or adopted is recorded as `build.epic_branch`.
274
+
275
+ ### Changed
276
+
277
+ - **The level ladder weighs findings, not files.** `W = Σ (recency × weight)` with
278
+ `recency = max(0.25, 1 - ageDays/365)`, `weight = code 1.0 · run 1.0 · test 1.0 · answer 0.8 ·
279
+ doc 0.5`, **× 2** when the row is `cross: true` (a finding tying two or more distinct files
280
+ together) and **× 0.5** when `confidence: assumed`. Thresholds `[0.5, 1.5, 3, 6, 20]` (the
281
+ fifth was 12), then three caps in order: no `kind: run` row ⇒ `min(level, 3)`; level 5 needs
282
+ ≥ 2 distinct kinds, else 4; `level ≤ count(distinct src)`. Three things changed here at once,
283
+ and **levels move: run `tldrx expert recompute` after upgrading.**
284
+ - *Reading alone now caps at 3.* Measured: an expert holding 15 `code` + 2 `test` rows, all
285
+ written the same afternoon by one reading session with no command ever executed, computed
286
+ **5/5**. Reading is evidence that code *says* something; only a run is evidence that it
287
+ *does* it. A `run` row is necessary, not sufficient: one alone is `W = 1.0`, level 1.
288
+ - *Recency decays continuously and the 180-day staleness cap is gone.* It was a cliff: an
289
+ expert trained on day 179 and the same expert on day 181 knew identical things and the
290
+ ladder reported 4 and 2. Knowledge fades; it does not expire on a Tuesday. One continuous
291
+ factor, floored at 0.25 so a year-old reading is worth a quarter of a fresh one rather than
292
+ nothing, replaces both the four-band table and the cap.
293
+ - *A cross-file finding counts double, an `assumed` one half.* `cross:` and `confidence:` are
294
+ additive `evidence[]` fields derived from the bullet, never asserted; a row written before
295
+ they existed carries neither and computes as it always did. A model can re-derive anything
296
+ one file says by reading it; what it cannot re-derive is the relationship between two.
297
+ - **The training prompt asks for value, not coverage.** It used to say "Citing the same file
298
+ twelve times is worth one row; reading twelve files is worth twelve" — an accurate description
299
+ of the old formula, and a Goodhart instruction. Both prompts (the spawned one and
300
+ `--print-prompt`) now carry the same criterion word for word: a finding is something a model
301
+ could not re-derive by reading that one file once — cross-file contradictions, dead paths,
302
+ defaults that differ from their docstrings, absences written as a negative claim, measured
303
+ commands. Restating a docstring is not a finding.
304
+ - **Light mode's file selection is bounded by `## Domain`.** Only files inside the expert's
305
+ declared folders are scored, read or inlined; every file inside them is a candidate even when
306
+ it greps for nothing. Bounding the input is cheaper than warning about the output.
307
+ - **One shared knowledge budget, declared inputs first.** `inputs_max_bytes` (default 96 KB) is
308
+ spent on every declared input in declaration order; the loaded experts then share
309
+ `knowledge_max_bytes` (default 48 KB) **in total**, split by rank, never one budget each. The
310
+ retired `expert_knowledge_bytes` is still read, as the same total — a per-expert cap scales
311
+ with a number nobody set, which is how 64 KB became 83,523 measured bytes. Truncation cuts at
312
+ an H2 boundary and appends `… N more findings in …`; a file whose first section already blows
313
+ the budget is named, not half-inlined. Measured on one real stage: `01-what/what` prepared a
314
+ **159,575-byte** prompt — 45% declared inputs, 52% expert bodies and knowledge, eight of its
315
+ nine experts loaded because they shared a repo with the run and not one had read a file the
316
+ run cited — and the 64 KB seed budget had dropped `ADR-D013-DELIVERY-ZONE-GEOMETRY.md`
317
+ (5,863 B) whole, the sixth of the six decisions the run existed to settle, while 70,923 B of
318
+ unrequested knowledge went in untouched. The same prompt is now **85,676 bytes**, loads two
319
+ experts, and contains ADR-D013 in full.
320
+ - **The prompt is ordered for the cache**: `stage.md`, expert blocks, `## Inputs`,
321
+ `## Previous attempt` — most stable to least. Measured, two real `claude` 2.1.251 calls, the
322
+ same 40,715-byte prompt, separate processes and sessions: call 1 wrote 37,059 cache tokens
323
+ and cost **$0.074982**; call 2 read all 37,059 back and cost **$0.004550** — 16.5x less.
324
+ `cache_creation_input_tokens` and `cache_read_input_tokens` are now parsed, published on the
325
+ `cost` event and written to `agent.result`.
326
+ - **Attempt 2 gets the refused draft.** The declared outputs that exist on disk are inlined
327
+ under `### Previous attempt — edit, do not restart`, capped at 32 KB shared across them.
328
+ Before this, a stage rejected over one missing section paid full price to rewrite four
329
+ documents from a blank page.
330
+ - **`claude` is spawned with `--output-format stream-json --verbose`** and the JSONL is parsed
331
+ as it arrives. Verified against one real measured call: `--verbose` is required (without it
332
+ `stream-json` in print mode refuses before spending anything) and `--json-schema` coexists
333
+ with it, the last `result` event carrying `structured_output` exactly as the single-blob
334
+ format did. `resolveResultDoc` reads EITHER format, so an older `claude` still works.
335
+ - **A progress view, on by default, on the four commands that make you wait** — `next`,
336
+ `run auto`, `expert train`, `seed triage --propose`. `--ui scene|compact|plain|off` or
337
+ `TLDRX_UI`, `auto` by default. Every summary is derived from bytes the sub-agent was already
338
+ sending: no second model call, no summary agent. **stdout is never written to** — asserted
339
+ end-to-end by comparing stdout with the view on and off — and the cursor is restored on a
340
+ normal exit, a thrown error and Ctrl-C. `--prepare`, `--commit` and `--dry-run` spawn nothing
341
+ and show nothing.
342
+ - **Ambiguity is refused rather than guessed.** With more than one open run and no explicit id,
343
+ every run-targeting command exits `2` and lists the candidates. Selectors: positional `<run>`
344
+ on `next` and `run status`, `--run <id>` on the rest. `tldrx run status` with several open
345
+ prints a table and exits `0`; `--json` returns `{ "runs": [...] }`, and the single-run shape
346
+ is unchanged when exactly one is open. Hooks never block on the ambiguity — a refusal
347
+ reaching a `PreToolUse` decision would stop work the human never asked to stop — and the
348
+ status line appends `(+N open)`.
349
+ - **An unknown flag is refused instead of ignored.** `tldrx status --nope` exited `0` having
350
+ run with its defaults after being told something it dropped. The guard lives in the
351
+ dispatcher, driven by the help registry, so it covers the commands that never used
352
+ `parseArgs` too, and it scans argv the way `parseArgs` does so a VALUE that looks like a flag
353
+ is not mistaken for one. `hook` and `statusline` forward their argv and are judged not at all.
354
+ - **`--json` is supported or it is an error.** It had three behaviours: supported,
355
+ accepted-and-ignored (`doctor`, `watch list`, `tickets status`) and refused (`map --check`).
356
+ `doctor --json` and `watch list --json` now print the data they already had structured;
357
+ everywhere else it is exit `1` with `--json is not supported by <cmd>`. `doctor`'s
358
+ `mcp: null` means NOT PROBED — "no servers" is a different claim.
359
+ - **An unknown COMMAND exits `1`, not `64`.** `64` means "on the roadmap, not built", which a
360
+ mistyped word has no business claiming. It is reserved and currently unreachable, and that is
361
+ asserted.
362
+ - **`tldrx status` stops counting advice as work.** A freshly initialised workspace printed
363
+ seven numbered items, five of them seeded experts at level 0 repeating the same sentence.
364
+ They collapse into one uncounted line under the blockers, with the trainable ones named and
365
+ one runnable example. `WorkspaceStatus.items` now means BLOCKERS and `pending` counts only
366
+ those; `--json` keeps `items` shape-identical and adds `advice` beside it.
367
+ - **`tldrx replay`'s usage stops requiring an id it does not require.** With no id it narrates
368
+ the newest run and refuses (exit `2`) only when several are open.
369
+ - **`tldrx tickets sync` previews by default; `--apply` writes.** It is the only verb that
370
+ reaches a third party, and a destructive default on the one networked command is backwards.
371
+ `--provider` no longer switches on a workspace set to `ticket_tool.kind: none` — it picks
372
+ between configured providers, and the config is not a flag's to override.
373
+ - **`ticket_tool.sync` means something**: `mirror-out` pushes and reads nothing back;
374
+ `two-way` also pulls each issue's status string, **verbatim**, into `external_status:` and
375
+ into nothing else. The name is generous — the second direction is one opaque string into one
376
+ front-matter key.
377
+ - **`version: 1` is the key; `schema_version:` is deprecated for one release.** The spec said
378
+ `version: 1` from the first draft and `tldrx init` has always written it, yet seven skeleton
379
+ validators demanded `schema_version` and seven templates printed `schema_version: 0` — so the
380
+ shipped validators rejected the tool's own output (5 of 5 real files failed on that key; 0 do
381
+ now). One `requireVersion` is the rule everywhere. A file still on the old key LOADS, prints
382
+ `<file>: schema_version is deprecated — say version: 1` on stderr once per process, and is
383
+ listed by `tldrx doctor`. `templates/*.yml` and `env.yml` now say `version: 1`.
384
+ - **The DoD gate no longer runs an un-allowlisted command.** `runDodCommand` handed the model's
385
+ own string to `/bin/sh -c` and the hook ships enabled by default with a 960 s timeout: a
386
+ story saying `dod: rm -rf ~` ran it the moment someone marked the story done. A command must
387
+ now be byte-equal to a `workspace.yml` command and is spawned argv-split with **no shell**; a
388
+ bare metacharacter refuses. An EMPTY `commands:` now refuses every dod entry instead of
389
+ permitting anything.
390
+ - **`budget-gate` covers every spender and fails CLOSED.** It matched `^(claude -p|tldrx next)`
391
+ only, so `run auto` — the one command that can spend a whole run in one invocation — plus
392
+ `expert train` and `seed triage --propose` walked straight past it. All five are matched and
393
+ priced, and an unreadable `run.yml`/`budget.yml` now denies and names the file: "cannot read
394
+ the budget" is not "the budget is fine". It still allows silently outside a workspace.
395
+ - **`--yolo` no longer reaches the reviewer.** `--dangerously-skip-permissions` was passed to
396
+ the read-only reviewer sub-agent, which was the one whose read-only-ness was the point. The
397
+ developer still gets it; that one is meant to write.
398
+ - **Unmetered is not zero.** An in-session `--commit` with no declared cost recorded `$0.00`,
399
+ so a ledger could read "$0.00 spent" after real money had gone. Such a task is now
400
+ `cost_usd: null` + `metered: false` (a null cost without the flag is a schema error). Sums
401
+ treat it as nothing, and every report says so: `budget show` and `run status` render
402
+ `unmetered (in-session)` and call `spent` a LOWER BOUND.
403
+ - **Build cannot charge 2.5x its phase, and Watch's floor cannot exceed its ceiling.** Build's
404
+ shares are divided by the worst case — stories × attempts × (1 + reviewer share) — instead of
405
+ by story count. Watch refuses BEFORE spawning when N features cannot each get the $0.25 spawn
406
+ floor inside the ceiling, naming the `budget raise` that fits.
407
+ - `run.yml` and `budget.yml` are written temp + `rename`, and `budget.yml` ceilings are re-read
408
+ from disk before every write unless this store deliberately changed them — a `budget raise`
409
+ that landed while a stage was in flight used to be silently reverted when that stage saved.
410
+ - `EventLog.read` skips an unparseable line instead of throwing. `events.jsonl` is appended line
411
+ by line, so a process killed mid-write leaves half an object on the last line, and `replay`
412
+ reported "events.jsonl could not be read" for a ledger whose first four hundred lines were
413
+ good. Skips are counted, not swallowed: `readAll()` returns `{events, lines, skipped}` and the
414
+ note is printed once per file. Line numbers now come from the reader, so a torn line no longer
415
+ shifts every `L<n>` after it.
416
+ - A job stopped halfway now looks stopped halfway: `seed apply` writes `status: applying` before
417
+ the loop and grows `created_runs` after each run; `seed triage --propose` writes its `.agent/`
418
+ bundle in headless mode too; `expert train` writes `knowledge/<area>.md.partial` and renames
419
+ it onto the real name only after the file validates (`.md.partial` never matches `*.md`, so
420
+ nothing half-written can be inlined).
421
+ - `tldrx init` and `.tldrx/init-questions.md` both point at `tldrx interview --init`. Measured:
422
+ the `answer-capture` hook returns early unless the path has a `tldrx-work` segment, so a
423
+ hand-typed answer in the init questions file records no fact, logs no event, and never reaches
424
+ the code that writes `process.yml`. The `[Answer]:` slot text stays — `captureAnswers` and
425
+ `tldrx answer` read and write it.
426
+ - `tldrx interview --init` applies the two process answers to `.tldrx/process.yml`
427
+ (`methodology`, `ticket_tool.kind`, and for GitHub the `owner/repo` from the git remote), and
428
+ ends with one line saying which happened. The two process questions are reordered so option
429
+ **A** is "None" for both, which makes `--yes-to-defaults` a real default there rather than a
430
+ guess. It remains a guess on the ownership and dead-code questions, so it stays a human's
431
+ flag.
432
+ - `tldrx tickets status` validates `process.yml` **before** the no-run check, so a broken
433
+ adapter config is reported as a config error rather than as "no run".
434
+ - The SessionStart hook appends up to three lines of the `tldrx status` report after the three
435
+ it always printed, so a session opening on work that is NOT a run — a proposed split,
436
+ unanswered setup questions, untrained experts — is no longer greeted with silence. Nothing
437
+ pending AND no run is still no output at all.
438
+ - `--seed` is repeatable: several are merged, deduped and re-sorted, with the 50-file cap
439
+ applied to the merged set rather than per argument. One occurrence is byte-for-byte what it
440
+ always was. `run new --seed` also prints one **stderr** note over the threshold or over 10
441
+ files, naming `tldrx seed triage`.
442
+ - `run.yml` gains an optional `triage: {split, depends_on}` block, written only by `seed apply`
443
+ and absent everywhere else.
444
+ - The `/tldrx` skill is "status → guide": step 1 is always `tldrx status --json`, then it walks
445
+ the items one at a time, asking when the decision is the human's and acting when the step is
446
+ mechanical, re-running `tldrx status` after each. 199 → 149 lines.
447
+ - Package `tldr-experts` now installs two commands, `tldrx` and `tldr-experts`. (Unscoped
448
+ `tldrx` as a package name is refused by npm's similarity rule; 0.0.1–0.2.0 were unpublished
449
+ on 2026-08-29 and their numbers can never be reused, so this is the first version back on the
450
+ registry.) README gains a release table with defined status tags, and npm/CI badges.
451
+ - `.claude/settings.json.bak-tldrx-*` is gitignored — `install --claude` backs the file up
452
+ before merging into it, and that backup was the one thing the framework writes that nothing
453
+ ignored.
454
+
455
+ - **Documented, from measurement: `--max-budget-usd` is a stop, not a cap.** `--max-usd 1.5`
456
+ over two repos: the sub-agent was killed with `error_max_budget_usd` ("Reached maximum budget
457
+ ($1.5)") **after** `total_cost_usd: 5.15325`, on a single turn (597 s, 105,698 cache-creation
458
+ + 60,548 output tokens, 1M-context model). The flag ends a run once a turn's cost is known; it
459
+ cannot end a turn already in flight. Budgets gate before and reconcile after, and overshoot is
460
+ bounded by one turn. The pipeline itself held: a non-zero `claude` exit is a failed run, so
461
+ nothing reached `competencies.yml`, the $5.15 was recorded in `training.jsonl`, and the
462
+ knowledge file the agent had already finished writing was **quarantined** — it would in fact
463
+ have validated (111 sourced items, 21 distinct files, level 5), which is exactly why leaving
464
+ it where an accepted one lives would have been the dangerous outcome.
465
+ - **Documented, from measurement: nested `claude -p` works; the ceiling was the constraint.** A
466
+ cold session pays ~10–26k cache-creation tokens before its first reply, so any
467
+ `--max-budget-usd` under about $0.25 fails as `error_max_budget_usd` before work starts. That
468
+ is why every spawn has a $0.25 floor and refuses below it. `--prepare`/`--commit` survives
469
+ because it is *cheaper* and because it works where spawning is disallowed — not because
470
+ spawning fails.
471
+
472
+ ### Fixed
473
+
474
+ - **`claim-sources` verified six `src` kinds it used to wave through.** Measured probe: a
475
+ handoff citing `[src: F999]`, `[src: Q42]`, `[src: graph:i-made-this-up]` and
476
+ `[src: absent:ops/backup.yml]` to assert "we removed the auth check from /admin" validated
477
+ CLEAN, closed its own auto gate and advanced the cursor — `resolveSrc` returned `ok` by
478
+ default for six of eight kinds. Now `F<n>` must be a live (non-retired) row in `facts.yml`,
479
+ `Q<n>` a question this run actually asked, `graph:<node>` a node in the graph or a token named
480
+ in `.tldrx/map/`, and `absent:` may only source a NEGATIVE claim (`## Unknowns` is exempt —
481
+ that heading IS the negation). A third outcome, **`unverified`**, sits between ok and refused
482
+ for what cannot be checked offline; it never fails a stage, it stops an auto gate.
483
+ - **A `[src: …]` wrapped in backticks is a citation, not a missing one.** `TRAILING_TOKEN_RE`
484
+ was anchored to end-of-line, so `` `[src: x]` ``, `[src: x].` and `([src: x])` all read as
485
+ unsourced. A real user's first `tldrx next` was refused with "9 unsourced bullet(s)" when all
486
+ nine carried citations — $0.40 spent to be told the work had no evidence. Closing quotes,
487
+ brackets and terminal punctuation after the `]` are now ignored; words after it still are not.
488
+ A line that TRIED to cite gets `malformed citation on line N`, not `unsourced bullet` — the
489
+ two need different advice.
490
+ - **An auto gate can no longer be closed by silence.** Three ways it could be: condition 5
491
+ counted only refusals, so an unverifiable citation passed (it is now zero refused AND zero
492
+ unverified); a stage that declared `questions.md` as an output and wrote one the §2.7 parser
493
+ could not read had "0 open questions" recorded as satisfied — a real stage wrote
494
+ `### Q1 — …` / `**Answer:**`, copied faithfully from this repo's own `templates/questions.md`,
495
+ and the gate signed itself over four unanswered questions (unreadable or empty now falls to
496
+ the human gate naming the ids it could not see, and `next --commit` refuses the same file with
497
+ exit `5`); and `templates/questions.md` IS the grammar now, with one worked example, inlined
498
+ into every `stage.md` that may write questions.
499
+ - **An approval could not be revoked.** `approve()` moves the cursor in the same transaction
500
+ that signs the gate, and `reject` only ever looked at the cursor, so the probe above met
501
+ `REJECT REFUSED: nothing to reject`. A machine that can sign but cannot be overruled is not a
502
+ gate — hence `reject --stage` above.
503
+ - **`--print-prompt` told everyone they had no repos.** `expert train … --print-prompt` handed
504
+ `loadWorkspaceFile` the `.tldrx/` directory, but that function joins `.tldrx/workspace.yml`
505
+ onto its argument — so it looked for `<root>/.tldrx/.tldrx/workspace.yml`, threw, and a bare
506
+ `catch` turned the failure into an empty list. Every printed prompt on every real workspace
507
+ said "none declared … run `tldrx init` first". It names them now, `tldrx map` and
508
+ `tldrx expert` agree about what that function takes, and a genuine read failure prints a
509
+ warning on stderr instead of disappearing. The headless path never had the bug.
510
+ - **`kind: test` was dropped without a word.** The evidence kinds were `code`, `run`, `doc`,
511
+ `answer`; the train prompt said to write `{kind, src, at}` and never said which kinds exist; a
512
+ session wrote two `kind: test` rows and both vanished on read, so `expert list` printed 15
513
+ evidence over a file holding 17. `test` is a first-class kind now, the prompt lists all five
514
+ with a one-line meaning each (rendered from a total record, so adding a kind without
515
+ explaining it will not compile), and an unrecognised kind is never silent again — `expert
516
+ list` (stderr, so it survives `--json`), the dashboard model and training's merge all report
517
+ `N evidence row(s) ignored — unknown kind '<x>'`.
518
+ - **Light-mode training was structurally incapable of exceeding level 3.** `codeEvidence`
519
+ dropped every `cmd` ref on the floor, so a sub-agent that ran the suite and cited
520
+ `[src: $ npm test → exit 0]` earned nothing for it — and the new run cap then held it at 3
521
+ whatever it measured. One `run` row per distinct command **and exit code** now. The training
522
+ prompt also said "do not run anything" while `allowedTools` already granted a
523
+ `Bash(<command>)` per `workspace.yml` command; it now names those commands verbatim from the
524
+ same list that becomes the grant, forbids everything else, and says that citing
525
+ `[src: $ <cmd> → exit <n>]` is the only way the expert earns a `run` row.
526
+ - **An evidence `src` is validated against its `kind`, both directions.** Nothing checked
527
+ before, so `{kind: run, src: "the tests pass"}` counted as a run — one row's difference
528
+ between level 3 and level 4. The §2.8 grammar decides now, through the same `classifySrc` the
529
+ `claim-sources` hook uses. Reading warns and drops (a `competencies.yml` may have been
530
+ hand-edited); writing refuses outright, because everything reaching it was derived by the
531
+ framework from a file the framework already validated.
532
+ - **The stage files named experts that `init` never seeds.** Measured on a real workspace whose
533
+ `.tldrx/experts/` held `product`, `dotnet-stack` and seven domain experts: four of the five
534
+ role names resolved to nothing and every How, Plan, Build and Watch run printed
535
+ `expert <name> — NOT LOADED`. Fixed by seeding the five roles; a stage naming an expert that
536
+ genuinely does not exist still says so.
537
+ - **A run nobody has started is `ready`, not "waiting at a gate".** The dashboard model derived
538
+ `pendingGate` from the stage gate objects — the first whose `gate.status` is `pending`, which
539
+ on a fresh run is every stage, because `pending` is the value the field is born with.
540
+ Measured on eight freshly applied runs: the page drew **8 red "waiting on a human" cards**
541
+ while `tldrx run status --json` said `waiting: {kind: "ready"}` for all eight. **0 cards
542
+ after.** `pendingGate`/`pendingQuestion` survive one release as documented aliases.
543
+ - Only a gate, a question or a failure raises an alert on the page: `ready` reads
544
+ "ready — `tldrx next <id>`" in the WAITING ON column instead of the blank "nothing" that made
545
+ a startable run look finished, and a run waiting behind a sibling raises nothing at all.
546
+ - Re-rendering the dashboard keeps the reader's place: handoff panel ids are derived from run +
547
+ phase rather than render order, so a `reload` restores the open panel and the scroll offset.
548
+ Verified live — scroll, route, open panel and all 180 citation marks survived.
549
+ - A dashboard path row is marked "waiting" only when the run is actually stopped at that stage
550
+ (every downstream gate also reads `pending`, which had painted four rows of five as an alert),
551
+ and the competency radar's viewBox was widened and its axis labels clipped to fit — a
552
+ 24-character area name used to overflow the chart.
553
+ - **An ADR's status is read from the document, not from the cached inventory**, and `statusOf`
554
+ recognises a bulleted status line. Measured: thirteen ADRs, every one of them writing
555
+ `- Status: proposed — owner decision pending`, and the inventory reported `adrStatus: null`
556
+ for all thirteen. The field whose whole job is "is this document still current" answered "no
557
+ idea" for the commonest form there is.
558
+ - **A recycled pid made a `.lock` permanent.** `kill(pid, 0)` said alive forever, `next` exited
559
+ `2` forever, and the fix was knowing to delete a gitignored file by hand — hence
560
+ `run unlock`. And `cancelled` was a status in the schema with nothing that could write it, so
561
+ a run you had given up on stayed open forever and made every id-less command ambiguous —
562
+ hence `run cancel`.
563
+ - **Ctrl-C did not reach the sub-agent.** There was no signal handler on the run path at all.
564
+ A sub-agent is spawned detached (a timeout needs a process group to kill), so the terminal's
565
+ Ctrl-C never reached it: it kept running with `ppid 1`, kept billing against its
566
+ `--max-budget-usd`, and because a stage's cost is only written after the spawn returns, not a
567
+ cent of it appeared in `events.jsonl`. `dashboard` and `watch` keep their own exit-0 shutdown.
568
+ - **Four runs of one plan all cut `story/S1`.** The second found the branch already there,
569
+ `git worktree add` checked it out as it stood, and one run's commits landed on another's
570
+ branch — and the fourth reused the third's LIVE worktree, so two sub-agents were editing the
571
+ same files at the same time. Neither name can collide now.
572
+ - **`bun dist/tldrx.js` printed `·` where `node dist/tldrx.js` printed `·`.** The bundle was
573
+ correct UTF-8 all along; the `// @bun` header `bun build` emits tells the Bun *runtime*
574
+ "already transpiled, load raw", and on bun 1.3.14 that path decodes the file
575
+ byte-per-character. `scripts/build.ts` strips the marker from every emitted file, and `cmp` on
576
+ the two runs is exit 0 — measured cost, ~4 ms per Bun start over ten `--version` runs. A test
577
+ runs the built bundle under both and compares bytes.
578
+ - `bun test` no longer prints `fatal: Needed a single revision` twice: `execFileSync` inherits
579
+ the child's stderr, and the two assertions that prove Build cut no branch leaked git's
580
+ complaint into every run. The assertions are unchanged and still rest on the exit code.
581
+ - **The `/tldrx` skill's "PRE-ALPHA — some commands are still stubs and exit 64" warning was
582
+ false** (`grep -rn "implemented: false" src/cli/commands/` comes back empty). It says alpha
583
+ now, points at `tldrx --help` as the authority, and writes down three things a real session
584
+ spent five minutes reverse-engineering out of `dist/tldrx.js`. The skill and the README also
585
+ say plainly that `.tldrx/` and `tldrx-work/` are **committed**, and that `init` gitignores
586
+ exactly five machine-local paths.
587
+ - README and ROADMAP stop claiming a release that has not happened: both `npm i -g` lines say
588
+ so and keep the commands, and ROADMAP's four "shipped in 0.3.0" become "on main, unreleased
589
+ (0.3.0 pending tag)". `dashboard` is off the README's "refuses on ambiguity" list — it draws
590
+ every run in the workspace, so it has no single run to be ambiguous about.
591
+
592
+ ### Removed
593
+
594
+ - **`gate.requires:` — from all five shipped stage files and from `StageGate`.** Nineteen
595
+ acceptance sentences that no gate enforced and no agent ever read: `normaliseGate`
596
+ (`src/core/run/workflowPreset.ts:216-231`) takes `.type`, `validateStage`
597
+ (`src/core/schemas/stage.ts:72-76`) checks `gate.type`, and the prompt ships `stage.md`, never
598
+ `stage.yml`. Removed from the type only — the validator never inspected the key, so a stage
599
+ library that still declares it keeps validating. The enforcement that IS real is `checks:`.
600
+ - **The placeholders `domain` and `stack` from the shipped `experts:` lists.** Neither was ever
601
+ an expert NAME: `stack_experts: true` already loads `<lang>-stack` for the run's repos, and a
602
+ `kind: domain` expert is picked by the paths the run cites. A forked or older stage file that
603
+ still lists them keeps working and gets ONE note — `experts: domain/stack are selected by
604
+ rule, not by name` — instead of a NOT LOADED line on every stage of every run. That line is
605
+ the one that matters when a real name is misspelled, and an operator who sees it every time
606
+ stops reading it.
607
+
608
+ ## 0.2.0 — 2026-08-29
609
+
610
+ ### The Build phase executes
611
+
612
+ `tldrx next` on `04-build` used to do what every other phase does: assemble one
613
+ prompt, spawn one sub-agent, validate its files. That is the wrong shape for a
614
+ phase whose work is a dozen agents in a dozen worktrees, so `04-build` now selects
615
+ a **wave executor**.
616
+
617
+ - **`waves.yml` is the schedule.** Wave by wave, story by story: resolve the
618
+ story's repo from `workspace.yml`, ensure `epic/<slug>` exists off the repo's
619
+ `default_branch`, open a worktree at `.tldrx/worktrees/<repo>/<story-id>` on
620
+ `story/<id>`, and spawn ONE developer sub-agent with its cwd inside it.
621
+ - **Done means proven.** After the agent, the facilitator re-runs the story's
622
+ fenced ```dod block **in that worktree** — the same runner `dod-gate` uses — and
623
+ every command must exit 0. Then it commits anything the agent left uncommitted
624
+ as `feat(<story-id>): <title>`, merges `story/<id>` into the epic with
625
+ `--no-ff`, and hands the diff to a **read-only reviewer** (`Read`, `Grep`,
626
+ `Glob`, `Bash(git diff *)`). A story reaches `done` only on DoD green **and** an
627
+ approval, and its `evidence:` is written from what was measured: `$ <cmd> →
628
+ exit 0` per command, the commit sha, the review path.
629
+ - **A failure costs one story, not the wave.** A red DoD or a merge conflict
630
+ blocks that story — the merge is aborted so the epic branch stays usable, and
631
+ the conflicting paths are recorded as its evidence — and the wave carries on.
632
+ - **A reviewer's `changes` requeues the story once**, with the review rendered
633
+ under `## Previous attempt` in the next prompt. A second `changes` blocks it.
634
+ - **Nothing ships.** No `git push` is run and no allowance grants one; no epic is
635
+ merged into a default branch. The phase ends at a human gate that lists the epic
636
+ branches ready to merge, per repo.
637
+ - **Safety.** A repo with uncommitted changes is refused **before** anything is
638
+ cut, naming the files and the fix (exit `2`, the stage stays `ready`).
639
+ `--dry-run` is refused outright: branches and commits are not revertible by a
640
+ flag. Worktrees are removed when a story reaches `done` or `blocked`, unless
641
+ `--keep-worktrees`.
642
+ - **`04-build/handoff.md` is generated, not asked for.** The executor holds the
643
+ exit codes and the merge results, so it writes the four §2.8 sections itself:
644
+ Findings cite `04-build/log/<story-id>.md`, the Evidence ledger is the dod
645
+ commands as `[src: $ <cmd> → exit <n>]`. One review log per story, always —
646
+ including a story blocked before a reviewer ran, so every citation resolves.
647
+ - **`run status` grows a Build line:** `04-build W1 [S1 done, S2 review]
648
+ W2 [S3 todo]` with per-story cost, read from the story files and the ledger.
649
+ A one-stage phase holding a dozen sub-agents cannot say anything with a
650
+ stage-level progress bar.
651
+ - **In-session:** `--prepare` bundles the NEXT pending story into
652
+ `.agent/<stage>/<story-id>/` (one story per cycle) and `--commit` continues that
653
+ story's pipeline from the DoD step.
654
+
655
+ ### The executor plug-in point
656
+
657
+ `src/core/facilitator/executors/` is a map from **phase id** to executor; a phase
658
+ with no entry keeps the single-agent path. Everything either side — the lock, the
659
+ cursor, the budget gate, `run.yml`'s tasks, the outputs re-read off disk, the
660
+ checks and the gate — stays in `runNext.ts`, because an executor that could move
661
+ the cursor would be a second facilitator. An executor may force a human gate
662
+ (Build does) and may refuse without failing the stage (a dirty repo).
663
+
664
+ ### Money
665
+
666
+ A stage's budget is split by the sub-agents an executor actually runs:
667
+ `min(stage budget ÷ stories, per_agent_max_usd, --max-usd)` for a developer, a
668
+ quarter of that share for its reviewer. The budget gate guards *starting* a stage,
669
+ so a mid-pipeline `--prepare` cycle is not charged the whole estimate again.
670
+ ### `tldrx dashboard` — a live, read-only local server
671
+
672
+ `dashboard` without `--static` used to exit `64`. It now serves.
673
+
674
+ - **Three GET routes on `127.0.0.1`**, default port `4477` (`--port <n>`,
675
+ `--port 0` for any free one, `--open` to launch a browser): `/` is the page,
676
+ `/model.json` is the model it was drawn from, `/events` is a Server-Sent Events
677
+ stream. Nothing else is answered, and anything that is not a `GET` gets `405` —
678
+ a dashboard that can change state is a second source of truth competing with
679
+ the files (concept §12).
680
+ - **A watcher over `.tldrx/**` and `tldrx-work/**`**, debounced 300 ms, pushes a
681
+ `reload` event; the page re-fetches the model and redraws. Recursive `fs.watch`
682
+ where the platform has it, an mtime sweep where it does not — an untested
683
+ fallback is a dashboard that quietly stops being live, so both paths are
684
+ covered by the same test.
685
+ - **`node:http` and `node:fs`, nothing else.** No framework, no runtime
686
+ dependency, and the built `dist/tldrx.js` serves under plain `node` — proven by
687
+ a test that runs it, not by inspection.
688
+ - Ctrl-C closes the listener and the watcher and exits `0`. A directory with no
689
+ `.tldrx/` gets a page that says which two commands fix that, and fills itself in
690
+ when one of them is run.
691
+
692
+ ### Model and renderer are now separate things
693
+
694
+ The rendering layer is meant to be replaced by a designer, so it stopped being
695
+ entangled with the reading layer.
696
+
697
+ - **`src/core/dashboard/model.ts` produces one plain JSON `DashboardModel`** from
698
+ the files — runs, execution path, handoffs, open questions, experts with levels
699
+ recomputed from evidence, the FAQ as data, and the Plan's stories/epics/waves
700
+ when a run has written them. It survives a JSON round trip unchanged, and a test
701
+ pins the field NAMES rather than the markup. Documented field by field in
702
+ `docs/dashboard-model.md`.
703
+ - **`src/core/dashboard/render.ts` is the only markup in the product.** The static
704
+ export renders on the server; the live page redraws in the browser — with the
705
+ *same functions*, serialised into the page by `clientRenderer()`. A test
706
+ evaluates that serialised source in an empty scope and demands byte-identical
707
+ output, so a template function that closes over a module constant fails there
708
+ instead of as a blank page in someone's browser.
709
+ - `--static` is unchanged in what it shows, and gained the plan block.
710
+ ### Watch: one watcher card per shipped feature
711
+
712
+ The phase that answers "how would anyone know this still works next month?" — and
713
+ refuses to answer it aspirationally.
714
+
715
+ - **`05-watch/watchers/<feature>.md`** (spec §2.16, `templates/watcher.md`): front
716
+ matter (`version`, `id`, `epic`, `title`, `stories`, `repos`, `status`) plus
717
+ `## Signal` · `## Where` · `## Healthy baseline` · `## Looks broken when` ·
718
+ `## Query` (fenced, copy-paste) · `## Sources`. Every list item in the first four
719
+ ends with a `[src: …]` token, checked by the **same parser `claim-sources` uses**.
720
+ - **`status` is computed, never claimed.** `verified` only when no `absent:` source
721
+ remains under `## Signal`; otherwise `draft`, and the card says what to
722
+ instrument. The executor re-reads the card off disk and rewrites the line, so a
723
+ sub-agent that stamps its own work `verified` is overruled.
724
+ - **The Watch executor** (`src/core/facilitator/executors/`, a map from phase id to
725
+ executor). A deterministic pre-pass groups **done** stories by epic — one feature
726
+ per epic, named after the epic's branch slug. Then one sub-agent per feature,
727
+ handed that epic's done stories, the **read-only diff of its branch against each
728
+ repo's `default_branch`** (through the runtime seam; nothing checks out or
729
+ fetches), the `observability`/`deploy` facts and the repos' `gotchas.md`, and
730
+ nothing else. The diff is what landed; `touches:` was written before the code
731
+ existed. `05-watch/handoff.md` is then written deterministically from the cards.
732
+ - **No done stories is a result, not an error.** The stage completes, spawns
733
+ nothing, spends nothing, and its handoff reads `- none [src: absent:03-plan/stories]`.
734
+ - **`--prepare`/`--commit` is per feature**: one
735
+ `.agent/<stage>/<feature>/{prompt.md,pending.json,result.json}` each.
736
+ - **`tldrx watch list [--run <id>]`** — feature, status and Signal line per card.
737
+ **`tldrx watch check <feature>`** — re-resolves one card's citations and
738
+ re-computes its status, and **exits 1 when either fails**, so CI can see it. It
739
+ catches both ways a card rots: the code moved under a citation, or somebody
740
+ hand-edited `draft` to `verified`.
741
+
742
+
743
+ ## 0.1.0 — 2026-08-29
744
+
745
+ ### Greenfield: a project with no code yet
746
+
747
+ Measured on a temp repo holding only `requirements.md` (2026-08-29): `init` seeded
748
+ zero experts, and `run new` had no way to be handed the document — so the What
749
+ stage would have ideated from nothing.
750
+
751
+ - **`tldrx init` names the case.** A single repo with zero code files is recorded
752
+ as `mode: greenfield` in `workspace.yml`, and `map/<repo>/architecture.md` says
753
+ so with an `absent:` source instead of describing an empty tree as an
754
+ architecture. "Code file" is one rule, by extension, shared with the map
755
+ (`src/core/detect/codeFiles.ts`).
756
+ - **`init` always seeds a `product` expert** — the What stage names one, so a
757
+ workspace without it handed that stage a prompt with no expert body at all.
758
+ - **`init --stack ts,dotnet,python,go,rust,…`** seeds a `<lang>-stack` expert per
759
+ declared language when there is no manifest to detect one from. Without it the
760
+ greenfield interview asks *"Which stack will this project use?"* (fixed list plus
761
+ free text) and *"Which single document is the source of requirements?"*.
762
+
763
+ ### `tldrx run new --seed <file|dir>` — import any document
764
+
765
+ - Takes one `.md`/`.txt` file or a directory of them (recursive, sorted, ≤50 files,
766
+ ≤2 MB each; anything larger is skipped **and named**). PDFs and Word files are
767
+ out of scope and say so. Distinct from `--from`, which stays AI-DLC-specific;
768
+ passing both is an error.
769
+ - **Copies nothing.** The originals stay where the team keeps them and every claim
770
+ cites them as `[src: <path>:<line>]`, workspace-relative.
771
+ - Writes `01-what/seed-index.md` (documents, sizes, skips, warnings) and
772
+ `01-what/handoff.md` whose Findings are every heading, bullet and paragraph of
773
+ the seed. Unknowns are deterministic: the What outputs
774
+ (`intent`/`scope`/`success-metrics`/`open-questions`) that no seed heading
775
+ matches.
776
+ - The seed documents are added to the What stage's **declared inputs** in
777
+ `run.yml`, so `tldrx next` inlines their content into the prompt. `stage.yml`
778
+ opts in with `seed: true` (§2.3 `inputs.seed` is accepted too); over the 64 KB
779
+ inline budget the index plus a labelled prefix is inlined and the prompt says
780
+ what was cut. Input count is capped at §2.3's 20.
781
+
782
+ **Plan/Build schemas (spec §2.13–§2.15).** `03-plan/stories/<id>.md`, `03-plan/epics/<id>.md` and
783
+ `03-plan/waves.yml` now have a shape, templates and validators — the last of spec §7's schema open items.
784
+
785
+ - Story front matter: `id` `epic` `title` `repo` `status` `depends_on` `touches` `acceptance` `test_plan` `evidence`,
786
+ plus the fenced `` ```dod `` block. **Every dod command must equal a `.tldrx/workspace.yml` command verbatim** — a story
787
+ is data, and data does not get to invent a shell command. **`status: done` requires `evidence`**: done means proven.
788
+ - Epic front matter: `id` `title` `repos` `stories` `branch: epic/<slug>` `status`. A story belongs to exactly one
789
+ epic, and the story ↔ epic reference must agree in both directions.
790
+ - `waves.yml`: `waves: [{id: W1, stories: [S1, S2]}, …]`, ids ascending because file order is execution order, and the
791
+ rule the shape cannot enforce alone — **every story's `depends_on` must be in an earlier wave**. A dependency inside
792
+ the *same* wave is an error, not a warning: those two stories would go to parallel agents that overwrite each other.
793
+ - New `plan` gate check reads all three together at the Plan gate (`tldrx approve`), which is the only place the
794
+ cross-file rules can be checked. `dod-gate` keeps its line scanner — a gate that only ran when the front matter
795
+ parsed would let a malformed story write `status: done` unchecked — but now shares one `` ```dod `` parser with the schema.
796
+ - Templates: `templates/story.md`, `templates/epic.md`, `templates/waves.yml`.
797
+
798
+ **Budget UX.** Measured in the pilot: a retry was refused twice because the phase ceiling had been sized for exactly
799
+ one attempt, and nothing put "what is left" next to "what the next stage costs".
800
+
801
+ - `tldrx budget show [<run>] [--json]` — a phase table of ceiling, spent, remaining, the next stage and its own
802
+ estimate, and whether `tldrx next` would be blocked there.
803
+ - `tldrx budget raise <phase> <usd> [--take-from <phase>]` — the one sanctioned edit to `budget.yml`, validated before
804
+ it writes: Σ phase ceilings ≤ run ceiling holds on the way out, a `--take-from` donor can never be cut below what it
805
+ has already spent, and the output says out loud whether the run ceiling grew or the money merely moved.
806
+ - Both `budget.blocked` messages (the hook and `tldrx next`) now name **the exact command**, with the shortfall
807
+ computed and rounded **up** to the cent, instead of naming the field to hand-edit.
808
+ - `run status` shows per-attempt cost for the cursor stage — `attempts: 2 · $1.39 + $1.21` — read from `agent.result`
809
+ events. A stage's total `cost_usd` cannot tell one $2.60 attempt from two $1.30 ones, and only the second says
810
+ whether a retry fits.
811
+
812
+ **Checked sections must contain items (spec §2.8).** Findings / Decisions / Unknowns / Evidence ledger must each hold
813
+ at least one list item; a section that is present but carries only prose is now a validation error. A genuinely empty
814
+ section is written `- none [src: absent:<what was looked at>]`. This closes the way an unsourced claim used to get
815
+ written anyway: a paragraph carries no bullet for the checker to look at, so "no unknowns that we can see" validated
816
+ clean. The parser, the `claim-sources` hook, the facilitator's post-stage check and `tldrx approve` share one parser
817
+ and all four now refuse it. The Evidence ledger in the shipped stage templates is a list rather than a table (a table
818
+ holds no list items), and both deterministic renderers — the `--from` distill and the `--seed` import — write
819
+ `- none [src: absent:…]` where they used to write an italic paragraph, so a `run new` cannot produce a handoff its own
820
+ validator would reject. **Outstanding:** `stages/what/stage.md` still ships the table and the old `## Rules` block; it
821
+ was owned by a concurrent branch when this landed.
822
+
823
+ All notable changes to tldr-experts. Dates are the day the work landed on `main`.
824
+
825
+ ## 0.0.2 — 2026-08-29
826
+
827
+ Fixes found by the first real pilot (scavtopia, `--from` an AI-DLC intent, What stage):
828
+
829
+ - `claim-sources`: bare `path:line` sources now resolve against the workspace root, then the run directory, then a repo dir (a run-relative `01-what/intent.md:12` was reported as missing).
830
+ - Handoff parser: wrapped bullets (token on an indented continuation line) and ordered items (`1.` / `1)` at column 0) are validated like `- ` bullets; the facilitator check, `approve` and the hook share one parser.
831
+ - `next` retries a `failed` cursor stage instead of walking past it; `run status` renders failed stages; `reject --note` works from `failed` and the note reaches the next prompt under `## Previous attempt`.
832
+ - `run new --from` no longer duplicates facts on re-import.
833
+ - `--root` on every run-scoped command; `<cmd> --help` works without a workspace.
834
+ - Stage prompts carry the citation grammar and the no-re-ask rule.
835
+ - Hook bundles split into one shared chunk (`dist/` 2.4 MB → 0.9 MB); CI build step is honest; tag-driven trusted-publishing release workflow.
836
+
837
+ ## 0.0.1 — 2026-08-29
838
+
839
+ First published version. Pre-alpha: the v0 loop runs end to end, and the parts
840
+ that do not exist yet exit `64` and say so rather than pretending.
841
+
842
+ ### What is implemented
843
+
844
+ - **`tldrx init`** — detects repos, stack, package manager, default branch and
845
+ build/test commands from the filesystem and `git` alone (no LLM, no network);
846
+ writes `.tldrx/workspace.yml`, the code map under `.tldrx/map/**`, the init
847
+ handoff, an interview containing only the real gaps, seeded experts at level 0,
848
+ `conventions/`, `process.yml` and an empty `facts.yml`, plus a marked block in
849
+ `.gitignore` and `CLAUDE.md`. Re-running keeps everything a human authored.
850
+ - **`tldrx map --refresh | --check`** — rebuilds the map, or resolves every
851
+ `[src: …]` citation in it against the filesystem and exits `1` naming the ones
852
+ that no longer land. Providers: `graphify` when it is on PATH, otherwise a
853
+ static provider (file tree, manifests, 90-day git churn).
854
+ - **`tldrx doctor`** — runs every check in `env.yml` and prints the table. Exit
855
+ `0` only when every required tool meets its `min_version`. `--mcp` adds live
856
+ MCP health checks.
857
+ - **The run lifecycle** — `run new <slug>` seeds `tldrx-work/<yymmdd>-<slug>/`
858
+ from a scope preset and its stage files, with per-phase budget ceilings scaled
859
+ to `--budget`; `run status` renders the execution path, the money and what the
860
+ run is waiting on; `answer`, `approve` and `reject` are the human half.
861
+ `approve` re-runs the stage's declared checks against what is on disk before it
862
+ will advance anything.
863
+ - **`run new --from <dir>`** — the AI-DLC distill: reads a listed set of files
864
+ from an intent folder, turns each bullet into a sourced Finding and each
865
+ answered question into a fact, drops the unanswered, and turns a claim that
866
+ contradicts a non-retired fact into a question rather than overwriting it.
867
+ Deterministic; no model runs.
868
+ - **`tldrx next`, in two execution modes** — headless (`tldrx next` spawns
869
+ `claude -p` itself) and in-session (`--prepare` writes the prompt bundle, the
870
+ host Claude Code session dispatches its own sub-agent, `--commit` picks it up).
871
+ From "re-read the declared outputs off disk" onwards both are the same code:
872
+ same validation, same checks, same cost roll-up, same gate. `--dry-run` keeps
873
+ the handoff and reverts the rest.
874
+ - **Six hooks** — `claim-sources` (a handoff bullet without a resolvable `[src:]`
875
+ is denied), `no-re-ask` (a question `facts.yml` already answers is denied),
876
+ `answer-capture` (records the answer, the fact and the event), `dod-gate`
877
+ (re-runs a story's `dod` block; the one hook that fails closed), `budget-gate`
878
+ and `session-start`. Everything but `dod-gate` fails open.
879
+ - **`tldrx expert list | create | train --print-prompt`** — experts are files.
880
+ `list` recomputes every level from evidence before printing it and warns when
881
+ the stored number disagrees.
882
+ - **`tldrx replay`** — `events.jsonl` rendered as a stakeholder narrative.
883
+ - **`tldrx retro [--apply]`** — five deterministic heuristics over the event log,
884
+ each citing the line that proves it; `--apply` appends the proposals to
885
+ `practices.md`, idempotently.
886
+ - **`tldrx dashboard --static`** — one self-contained `index.html`, no external
887
+ request in any `src`/`href`.
888
+ - **Runtime: Node ≥ 20 or Bun.** Every capability that differs between the two
889
+ lives behind `src/core/runtime/`; a test asserts no `Bun.` call site survives
890
+ outside it. The published bundle inlines its one devDependency, so an install
891
+ resolves zero runtime dependencies.
892
+
893
+ ### What is NOT implemented
894
+
895
+ - **Build and Watch phase execution.** The stages, their contracts and the story
896
+ and wave file shapes exist; running them does not. Story/epic/`waves.yml`
897
+ schemas are still an open decision.
898
+ - **Expert training.** `expert train` prints the prompt to paste and exits `64`
899
+ without `--print-prompt`. Running the training loop, and writing the resulting
900
+ `knowledge/*.md` and levels back, is v1.1.
901
+ - **The live dashboard.** `dashboard` without `--static` exits `64`. The watching
902
+ server is v1.
903
+ - **The ticket adapter.** No Jira or GitHub integration ships. Direction
904
+ (mirror-only vs two-way) and which one lands first are undecided.
905
+ - **Parallel execution.** v0 runs one stage at a time, one task at a time.
906
+
907
+ ### Notes
908
+
909
+ - `tldrx <command> --help` prints usage and exits `0` without needing a
910
+ workspace. `--root <path>` works on every command that touches one.
911
+ - A `failed` stage is not progress and not the end: `run status` renders it as a
912
+ failure, `tldrx next` retries it, and `reject --note` sends it back to `ready`
913
+ with the note fed into the next prompt.