tldr-experts 0.9.2 → 0.11.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 (50) hide show
  1. package/CHANGELOG.md +286 -0
  2. package/README.md +6 -2
  3. package/dist/hooks/answer-capture.js +297 -25
  4. package/dist/hooks/budget-gate.js +7 -10
  5. package/dist/hooks/{chunk-hg43zexg.js → chunk-1c43349s.js} +65 -51
  6. package/dist/hooks/{chunk-x3zy7zrn.js → chunk-6c84kjd7.js} +1 -1
  7. package/dist/hooks/{chunk-2y3kwfrx.js → chunk-6ws778wq.js} +1 -1
  8. package/dist/hooks/{chunk-fv66tdsm.js → chunk-9v45j71e.js} +23 -8
  9. package/dist/hooks/chunk-bk7rkdzw.js +103 -0
  10. package/dist/hooks/{chunk-4w03q2kj.js → chunk-d20x2aew.js} +27 -19
  11. package/dist/hooks/{chunk-rk1nm39z.js → chunk-ecwes7kd.js} +1 -1
  12. package/dist/hooks/{chunk-f6484ykg.js → chunk-kqvbzyc0.js} +24 -10
  13. package/dist/hooks/{chunk-y3cxqvnx.js → chunk-r1efatxk.js} +411 -10
  14. package/dist/hooks/{chunk-cf4az0j5.js → chunk-stw0dxna.js} +1 -1
  15. package/dist/hooks/{chunk-8awygmqw.js → chunk-tg7ppsxr.js} +4 -1
  16. package/dist/hooks/{chunk-vyvjbbmb.js → chunk-wzzywf6c.js} +32 -2
  17. package/dist/hooks/claim-sources.js +5 -5
  18. package/dist/hooks/dod-gate.js +8 -99
  19. package/dist/hooks/no-reask.js +8 -9
  20. package/dist/hooks/session-start.js +38 -123
  21. package/dist/hooks/statusline.js +6 -9
  22. package/dist/tldrx.js +3503 -1648
  23. package/package.json +1 -1
  24. package/plugin/.claude-plugin/plugin.json +1 -1
  25. package/stages/build/stage.yml +14 -0
  26. package/stages/how/stage.yml +6 -0
  27. package/stages/plan/stage.yml +6 -0
  28. package/stages/watch/stage.yml +6 -0
  29. package/stages/what/stage.yml +6 -0
  30. package/templates/experts/stack/dotnet.md +4 -3
  31. package/templates/experts/stack/javascript.md +4 -3
  32. package/templates/experts/stack/python.md +4 -3
  33. package/templates/experts/stack/typescript.md +4 -3
  34. package/templates/workspace.yml +6 -0
  35. package/workflows/bugfix.yml +7 -0
  36. package/workflows/docs.yml +7 -0
  37. package/workflows/feature.yml +7 -0
  38. package/workflows/hotfix.yml +7 -0
  39. package/workflows/integration.yml +7 -0
  40. package/workflows/migration.yml +7 -0
  41. package/workflows/performance.yml +7 -0
  42. package/workflows/prototype.yml +7 -0
  43. package/workflows/refactor.yml +7 -0
  44. package/workflows/retro.yml +7 -0
  45. package/workflows/security-patch.yml +7 -0
  46. package/workflows/spike.yml +7 -0
  47. package/workflows/upgrade.yml +7 -0
  48. package/dist/hooks/chunk-39zh2e44.js +0 -97
  49. package/dist/hooks/chunk-5w5eg00k.js +0 -136
  50. package/dist/hooks/chunk-rrkdfk7s.js +0 -30
package/CHANGELOG.md CHANGED
@@ -1,6 +1,292 @@
1
1
  # Changelog
2
2
 
3
3
 
4
+ ## 0.11.0 — 2026-09-08
5
+
6
+ ### Fixed
7
+
8
+ - **An answered decision now says who decided it, and what it binds to.** `tldrx answer`
9
+ wrote a fact with no attribution and no repo scope, so a superseded owner call and an
10
+ unattended driver default read identically in `facts.yml` and in every prompt built from
11
+ it. `--decided-by <owner|driver>` (last one wins, like any ordinary flag) and repeatable
12
+ `--repo <name>` land on the fact `answer` writes — `--decided-by` outside the closed set,
13
+ or `--repo` naming no workspace repo, is a usage refusal (exit 1) with nothing written, and
14
+ `--repo` passed twice scopes once. With neither flag the fact says "not stated," never a
15
+ guessed owner. `Fact.decided_by` is additive on `facts.yml`; `Fact.repos` already existed
16
+ and is required, not additive. (#169)
17
+ - **Two signed facts that disagree produce a question, without an agent choosing to
18
+ notice.** The contradiction check now runs on every `answer`, scores the
19
+ new fact against the live ones in the same `area`, and — on a hit — RAISES an advisory
20
+ question (`asked_by: tldrx`, a new `advisory:` metadata key that opts a block out of
21
+ every reader that COUNTS open questions — the auto gate, `next`'s `awaiting_answer`
22
+ branch, `skip_if` and `tldrx status` — without hiding it from the readers that LIST
23
+ them, `tldrx questions` among them) rather than refusing the answer. The
24
+ new fact keeps `conflicts_with: [<id>]` naming the fact it contradicts (additive,
25
+ empty-omitted, unquoted) — which one is right is left to the question it raises, nothing in
26
+ the mechanism decides a "loser." "Not detected" never reads as "checked and agreed" — the
27
+ check is lexical, and honestly cannot catch what its own transcript produced here: three
28
+ differently-titled answers whose contents actually disagree. The new `fact.conflict_raised`
29
+ event (`{fact, conflicts_with, score, q, raised}`) joins the closed `EVENT_TYPES` enum.
30
+ (#169)
31
+ - **A defect in a file no story declared has a sanctioned remedy and a visible home.**
32
+ Reaching it used to mean a hand edit to a story's `touches:` while the boundary card
33
+ described exactly the verb that edit needed and the CLI forbade it. Separately, and by
34
+ owner decision REPORT ONLY — no new gate condition, no new refusal, no new exit code — a
35
+ Build finding whose file matches no story's declared surface now reaches the Build
36
+ handoff, the PR body and the boundary card (only when a boundary trigger already draws
37
+ one), named as unowned rather than silently absorbed by whichever story happened to run
38
+ last. (#171)
39
+
40
+ - **The developer bundle carries `result_schema` too, so both halves of one handshake make the
41
+ same promise.** Measured on disk in a real run: `.agent/<story>/pending.json` had no
42
+ `result_schema` while the reviewer bundle one directory down had one. The reviewer prompt says
43
+ in as many words to read the envelope shape out of the bundle and never from memory; the
44
+ developer had nothing to read it out of, so a host guessed the shape by copying a sibling
45
+ story's `result.json`, and a reviewer later "corrected" it from the other file. A developer
46
+ bundle — the Build story's, and every single-agent stage's — now carries the
47
+ `{outputs, questions_asked, notes}` envelope the spawned half is handed through
48
+ `claude --json-schema`, plus the `cost_usd` and `session_id` a host may declare and
49
+ `readResult` reads back. It is DERIVED from that schema rather than retyped, so a change to
50
+ the envelope cannot reach the spawn without reaching the bundle. The one thing the two halves
51
+ do not share is how strictly the schema is read, and that is now written down instead of
52
+ implied: a reviewer envelope is refused on its form, a developer envelope is coerced, and
53
+ `--commit --check` is what reports the difference.
54
+
55
+ ### Added
56
+
57
+ - **`tldrx story widen <id> <path>... --note "<why>"`** — the verb the boundary card had
58
+ been pointing at while the CLI forbade the hand edit it described. Records
59
+ `story.touches_widened` (`{story, paths, note, before, after}`, joining the closed event
60
+ enum) and rewrites `touches:` through the same validated write every other command uses.
61
+ Refused (exit 2, nothing written) for a `done` story — naming `reopen --for-fix` as the
62
+ remedy — a `..` anywhere in a path (a substring test, not a segment one), a path already declared, an unknown story, or a plan the
63
+ story doesn't have; an unresolved run is exit 3 (not found), and `--for-fix` is not a flag
64
+ of `widen` itself and passing it is a usage refusal (exit 1). (#171)
65
+ - **`tldrx budget grant <amount> --fact <id>`** — a ceiling that answers to a recorded
66
+ authorization instead of a bare number. Writes `authorized_usd` at run or phase scope, and
67
+ `authorized_by`/`authorized_at` always at the run level, citing the fact behind it (not
68
+ duplicated under a second key) — `--phase` scopes the amount, never the citation. Fires
69
+ `budget.granted` (`{amount_usd, fact, phase, note, ceiling_usd, previous_usd}`, joining the
70
+ closed event enum) — a second grant replaces the amount and the event records what it
71
+ replaced, `null` on the first grant; a non-positive amount is refused (exit 1, nothing
72
+ written), by the VALIDATOR as well as by the verb, so a hand-edited `authorized_usd: 0` —
73
+ which `grantFor` would otherwise read as a real $0 grant that blocks every later raise —
74
+ is a schema error rather than a record that quietly governs nothing. Absence is untouched:
75
+ no key still means no grant recorded, never `$0`. `on_grant_exceed` (default `warn`, distinct from and never confused with
76
+ `on_exceed`) says what a later `budget raise` past the grant does; `budget show` renders
77
+ the grant back and stays silent
78
+ when none is recorded. `--fact`/`--phase`/`--on-exceed` are refused (exit 1) on
79
+ `budget show` and `budget raise`, where recording a policy is not their job. All four keys
80
+ are additive on `budget.yml` — a file written before this change still loads and means no
81
+ grant. `DASHBOARD_MODEL_VERSION` is unchanged (3): `BudgetModel`/`BudgetPhaseModel` widen,
82
+ no existing field's meaning moved. (#170)
83
+ - **`tldrx cost --stories`** — what a story cost against the ceiling its spawn was given,
84
+ one row per story, ceilings keyed off each `agent.spawned`'s `max_budget_usd` and money
85
+ keyed off the envelope's own metered `cost_usd`. A story with an unmetered turn among its
86
+ metered ones is named a lower bound, never handed the unqualified "inside the ceiling"
87
+ verdict; a story with no spawned ceiling is reported as absent, not as a zero; a run with
88
+ no story spawns says so instead of printing an empty table. The same arithmetic now
89
+ appends an over-ceiling clause to the Build handoff's existing cost line when the stories
90
+ behind it ran over. (#170)
91
+ - **A run close, `run cancel`, `approve` and the Build handoff header all say how many of a
92
+ run's decisions name a decider.** One tally (`owner` / `driver` / not-stated), computed
93
+ once and read by all four surfaces; no sentence at all when the run recorded no facts,
94
+ never a confident zero. (#169)
95
+
96
+ - **`workspace.yml` gains an optional `test_fast:` command — the developer iterates on it, and
97
+ the suite runs once at the Definition of Done.** A story's ```dod block must be byte-equal to
98
+ a `workspace.yml` command, and the developer prompt hands the sub-agent that same list with
99
+ "these commands are the only ones you may run". Between them, the only test instrument the
100
+ framework offered was the whole suite. Measured this week on three real workspaces: in one of
101
+ them that suite is 11,929 tests over 855 files, and a developer iterating a story ran it six
102
+ to ten times, on top of the two or three the Definition of Done itself pays for (preflight
103
+ once per run, cached; then once per attempt). The waste was never the gate — it was the
104
+ iteration loop, for which there was no faster instrument. `test_fast` is that instrument, and
105
+ it is deliberately not evidence: it is declared, so the developer may run it, and a ```dod
106
+ line equal to it is REFUSED with a sentence that names the slot rather than the generic "not
107
+ one of workspace.yml's commands", which would be false about a command the file plainly
108
+ declares. `tldrx init` does not guess it — no manifest says which subset of a suite is the
109
+ fast one, and a synthesised answer is exactly the conventional wisdom `command_probes:` exists
110
+ to keep out of this file — so the emitted `workspace.yml` carries the slot commented out with
111
+ a line saying what it is for, and nothing probes a slot nobody declared. When the workspace
112
+ declares one, the developer prompt says both halves: iterate on the fast command, and run the
113
+ declared full command once before you stop, because the gate re-runs it after. When it does
114
+ not, the prompt is byte-identical to the one before this existed. Additive: `version:` stays
115
+ `1`, and every workspace written without the slot loads and behaves exactly as it did.
116
+ - **`tldrx next --commit --check` rehearses the commit and writes nothing, so a bad envelope
117
+ is caught while the turn is still open.** Measured on a real workspace this week: two reviews
118
+ were refused at `tldrx next --commit --review` because a `[src: …]` citation inside a `refuted`
119
+ finding was not the last thing on its line. Both refusals were correct; both arrived after the
120
+ turn had been paid for, and the host's answer was to ban `refuted` from the next thirty briefs
121
+ — a protocol feature switched off to dodge a late validator, which is the most expensive
122
+ possible outcome for a rule the framework got right. The reviewer cannot check itself: its
123
+ tools are `Read`, `Grep`, `Glob` and `Bash(git diff *)`, so there is no door to run a validator
124
+ through, and its prompt already states the end-of-line rule with a refused and an accepted
125
+ example generated from the reader's own patterns. What was missing was a way for the host to
126
+ ask "is this readable?" before saying the turn is done. `--check` validates the prepared
127
+ bundle's `result.json` through the SAME reader `--commit` uses — no second implementation of
128
+ the grammar, the dispositions or the verdict enum — prints every refusal verbatim with the
129
+ offending line, and exits `0` when `--commit` would read the envelope and `1` when it would
130
+ not. It takes no lock, moves no cursor, records no event and spends no attempt; the tests
131
+ compare `run.yml`, the story file and `events.jsonl` as bytes either side of the call. On a
132
+ developer bundle the reader is deliberately tolerant — a missing `outputs` is read as `[]`, a
133
+ non-string `notes` as `""` — so `--check` exits `0` and NAMES what is about to be coerced
134
+ rather than inventing a refusal the framework does not make. That naming goes down to the
135
+ ELEMENT, because the coercion that hides best is the one inside an array: `outputs` and
136
+ `questions_asked` are declared `items: {type: "string"}` and the reader silently FILTERS every
137
+ element that is not one, so `["good", 42, null, "also-good"]` is an array, passes any
138
+ whole-field type test, and still reaches the run as two entries. `--check` names each dropped
139
+ element by index and by the JSON of its value, through the same one-line predicate the reader
140
+ filters on — called, not restated, so the check cannot name a set the reader does not drop.
141
+ Nothing citable is lost in the bargain: a dropped element is by definition not a string, and a
142
+ `[src: …]` citation is a token inside one.
143
+
144
+ ### Changed
145
+
146
+ - **The shipped stage and workflow money literals are labelled `[assumption]`**, and
147
+ deliberately NOT recalibrated: `tldrx init` copies no `stages/` into `.tldrx/`, so moving
148
+ one moves the ceiling of every workspace that never wrote an override, and this repo holds
149
+ no corpus to derive a new number from. `triage.budget_basis` (closed set: `model-guess` /
150
+ `owner-grant` / `preset`; additive on `RunFile`'s `triage:`, absent means nothing) records
151
+ which of the three produced a triage's numbers — `tldrx seed apply` always writes
152
+ `model-guess` today. `tldrx cost --stories` is the command that would produce the corpus a
153
+ recalibration needs. (#170)
154
+
155
+ - **The Build stage ships `parallel: 2`, so a wave runs two stories at a time out of the box.**
156
+ `--parallel N` shipped in 0.3.1 and nothing used it: no shipped workflow and no shipped
157
+ stage file set it, so every workspace ran one story at a time by inheriting a code constant
158
+ nobody chose. Measured across three real workspaces over one week and 23 runs: runs of 34-43 h
159
+ wall clock whose per-story medians were 0.6-1.35 h — the wall clock was serialization, not work.
160
+ The stories of one wave are independent by construction (`waves.yml` puts every `depends_on` in
161
+ an EARLIER wave, and `validatePlan` refuses a plan that does not), so this is a schedule change
162
+ and not a correctness one; merges into the epic still happen serially, in the wave's listed
163
+ order, and so do the reviewers, whose merge base moves under them otherwise. Two rather than
164
+ more because one of those workspaces hit OOM kills at a wider fan-out — raise it per run with
165
+ `--parallel N`, or for good with `parallel: N` in your own `.tldrx/stages/build/stage.yml`.
166
+ `DEFAULT_PARALLEL` in code stays 1: the opinion belongs in the stage file an operator can read
167
+ and override, and the last resort stays the value that surprises nobody. The one thing
168
+ `parallel:` does not change is the `--prepare`/`--commit` handshake, which is per story and
169
+ stays sequential — that side cannot know how many sub-agents the host session will run. (#176)
170
+ - **The "can this test fail?" check now names its instrument: one test file, not the whole
171
+ declared suite.** The drive mandate asked for the one check a reviewer cannot do — break the
172
+ line a new test covers and watch it go red — and the four stack packs asked the same question
173
+ under `## Checks`, telling the reader to "re-run the test command declared in
174
+ `.tldrx/workspace.yml`". That command IS the full suite by construction: a story's Definition
175
+ of Done is byte-equal to a `workspace.yml` command. So the words nobody read as "full suite"
176
+ bought exactly that, once per mutation — measured over a week of unattended runs on three real
177
+ workspaces, five mutations in a story meant five extra full suites, six to ten suite runs per
178
+ story, against a suite of 11,929 tests over 855 files in one of those workspaces. The check
179
+ keeps earning its place (it caught a test that only passed because base64 hid a raw id); what
180
+ was expensive was the instrument. The mandate and all four packs now scope the mutation re-run
181
+ to the single test file that covers the broken line, and say where the declared command
182
+ belongs: once, at the Definition of Done. The empty-slot clause is untouched — a workspace
183
+ that declares no test command is still named rather than quietly passed.
184
+
185
+ ## 0.10.0 — 2026-09-07
186
+
187
+ ### Fixed
188
+
189
+ - **A refused Definition-of-Done command is no longer recorded as `exit 126`.** When the
190
+ sandbox declined to run a `dod` or base command — a bare shell metacharacter with no
191
+ shell open to interpret it, an unsplittable pipeline — `runStoryDod` wrote
192
+ `{exitCode: 126}` for it, a fabricated exit code that told the ledger, the reviewer
193
+ prompt and bundle, the dashboard and the ship gate that the command RAN and failed with
194
+ 126, rather than that it never ran at all. `DodResult` grows `status?: "ran" | "refused"`
195
+ (absent stays `ran`, so every pre-existing record loads unchanged) and
196
+ `refusedBecause?: string`; a refused row carries no `exitCode` anywhere it is read or
197
+ rendered. `dodFailureReason` is the one derivation of the sentence a reader prints for a
198
+ refused command versus a failed one. `parsePreflight` tolerates exactly this one new hole
199
+ — a refused row has no `exit_code` and is never read as green — and a fourth golden build
200
+ scenario (`refused`) pins the shape. `PreflightRowModel.exitCode` widens to
201
+ `number | null`; `DASHBOARD_MODEL_VERSION` does not bump (a widened type and an added
202
+ field, no existing field's meaning moved). (#165)
203
+ - **The reviewer no longer diffs an empty range.** The reviewer's diff base was always
204
+ `epicBranch`'s current tip, but by the time review runs the story's own commits are
205
+ already merged onto that branch — so the reviewer diffed a range that had shrunk to
206
+ nothing, or silently picked up work from a story that landed after. `epic_base` (the full
207
+ 40-hex sha, captured immediately before the merge) is now recorded additively on
208
+ `task.done` and in the review bundle, omitted rather than guessed when unknown, and
209
+ recovered on resume from the ledger or the bundle; `reviewDiffCommand(diffBase,
210
+ epicBranch, branch)` is the one place the fallback to `epicBranch` lives, so a record with
211
+ no base still falls back to the branch and says so rather than diffing nothing. Seven
212
+ golden prompt/event artifacts moved — a prompt normaliser learned to scrub verified shas
213
+ first, so the sha itself never becomes a golden byte. (#166)
214
+ - **A costless turn that declared only a provider token split is no longer counted silent.**
215
+ Both spend-basis feeders — the handoff's cost line and the dashboard's spend model —
216
+ summed a task's host `tokens` scalar only, so a turn that reported a real
217
+ `input_tokens`/`output_tokens` split but never set the host field read as zero, tripping
218
+ `costlessTokens` and landing on `basis: "absent"` for a turn that was fully measured. One
219
+ leaf, `turnTokens(task)` (host `tokens` when declared — present as a finite number,
220
+ including an explicit `0` — else the input+output split when both are positive, else
221
+ `null`), is now read by both feeders; `costlessTokens` widens to
222
+ "host-declared or provider-reported", and an explicit `tokens: 0` still reads as a
223
+ declaration, not an absence. The host-token sum that feeds the `absent` sentence's own
224
+ count keeps summing the raw scalar only, so a provider split can never inflate what
225
+ "declared host tokens" means. The dashboard's own `RunTask` projection — a hand-tolerant
226
+ duplicate of `RunFile.ts`'s type, not a re-export, on the identical seam — gained the same
227
+ two fields it was silently dropping; without that half, the dashboard side of this fix
228
+ would not have fired at all. `DASHBOARD_MODEL_VERSION` unchanged. (#159)
229
+ - **A reviewer turn's token split reaches its `run.yml` row.** 0.9.1 wired the provider's
230
+ input/output split into the Build executor's own task rows; the reviewer's two spawn call
231
+ sites never got the same two fields, so a reviewer turn kept `run.yml`'s per-task tokens
232
+ blank even when the provider reported them — the sibling of the gap #159 closes for spend
233
+ basis. `formatRetry` and `recordReview`'s task structs both carry
234
+ `input_tokens`/`output_tokens` now, read at the same call sites the build path already
235
+ used; a host-envelope review (no provider usage to read) is left alone. Three golden
236
+ run-tasks artifacts moved. (#173)
237
+ - **The dirty-tree refusal prints the commands, and the reason it gives is true.** The
238
+ message said stashing would "carry the mess forward" through `git worktree add`, and told
239
+ you to "commit or stash" with no command to run — but the #41 base pre-flight runs the
240
+ gate commands in the repo's OWN checkout, not a worktree, so an uncommitted product change
241
+ sits inside the very measurement that decides whether a red DoD is the story's fault or
242
+ the base's; that was the real reason, and neither the docstring nor the message said it.
243
+ The refusal now prints the two literal commands (`git -C <dir> stash push -u -m "tldrx
244
+ <runId> foreign work"` / `git -C <dir> stash pop`, the run id in the stash message so a
245
+ resumed run's mess stays traceable) and stashes nothing itself; the docstring states the
246
+ true mechanism. (#164)
247
+
248
+ ### Added
249
+
250
+ - **`command_probes:` — what `tldrx init` actually ran, not what it guessed.**
251
+ `workspace.yml`'s `commands:` block came from pattern-matching a repo's files — a
252
+ `package.json` script name, a Makefile target — and was never executed, so a stack pack,
253
+ the dashboard and the base pre-flight all treated a detected command as verified when
254
+ "verified" had never been checked. `tldrx init` now probes each detected
255
+ `build`/`test`/`lint`/`typecheck` command once (`run` is excluded by name — starting a
256
+ long-running process is not a probe) and records `command_probes:` rows per repo:
257
+ `{status, verified, exit_code, at, reason}`, one status vocabulary for the whole surface
258
+ so a genuine `exit 127` (found, ran, failed) and a command that never started — either
259
+ `unspawnable` (ENOENT/EACCES) or `not-probed` (a bare metacharacter needing a shell this
260
+ probe does not open) — are different, honestly labelled rows instead of the same
261
+ fabricated 127. `--no-probe` skips entirely and records the skip as its own reason
262
+ (`skipped: --no-probe`); the base pre-flight cites a probe only when its `status` is
263
+ `failed` — the one status that means the command ran and exited non-zero. `tldrx learn`'s
264
+ sandboxed walkthrough passes `--no-probe` to the `init` it runs; `tldrx map` never requests
265
+ a probe at all — it omits `detectWorkspace`'s `probe` option entirely, as before. Probing is
266
+ a real spawn, and `init`'s own `--help` "Deterministic and offline" claim is corrected to
267
+ say so.
268
+ Additive on
269
+ `WorkspaceRepoDocument`; every pre-#168 `workspace.yml` still validates. (#168)
270
+
271
+ ### Changed
272
+
273
+ - **`tldrx ship`'s PR body is written for a PR, not forwarded from the last handoff
274
+ verbatim.** The body was the raw `04-build/handoff.md` text — a build log addressed to
275
+ the next Build turn, headed by phase names and gate jargon nobody opening the PR asked
276
+ for. `renderShipBody` is now the one renderer: what shipped (the handoff's `done`
277
+ findings), what did not (its unknowns), the fix-list findings still open, and the full
278
+ handoff moved inside a collapsed `<details>` block rather than dropped. Dry-run and the
279
+ real create path share the one rendered body file, so what you preview is what posts. And
280
+ the state refusal has an allowed move at last: a story at `status: done` EXCUSES the state
281
+ paths its own `touches:` declared, so a story written to edit `.tldrx/workspace.yml` —
282
+ adding a repo, declaring a command — can finally be shipped, where before #102's refusal
283
+ had no way through at all. A story at `review` or `blocked` excuses nothing: a declaration
284
+ with no verdict behind it is a plan, not a fact. The excuse is matched per `(repo, path)`,
285
+ since `touches:` is relative to the story's own `repo:`, and the remedy names only the
286
+ paths still REFUSED — never the blanket `tldrx-work .tldrx`, which would revert the very
287
+ edit printed three lines above it as excused. It exits the money/gate family
288
+ (`EXIT_GATE_REFUSED`), not a module-local code. (#167)
289
+
4
290
  ## 0.9.2 — 2026-09-07
5
291
 
6
292
  ### Changed
package/README.md CHANGED
@@ -163,8 +163,10 @@ its own refusal. See [10 Unattended mode](docs/guide/10-unattended-mode.md).
163
163
  | `security-patch` | auto | auto | — | human | human |
164
164
  | `migration` | auto | auto | auto | human | human |
165
165
 
166
- `--parallel <n>` on `next` / `run auto` builds that many of a wave's stories at once
167
- (merges still land in the wave's listed order; default 1 is unchanged).
166
+ `--parallel <n>` on `next` / `run auto` builds that many of a wave's stories at once. The
167
+ shipped Build stage declares `parallel: 2`, so a workspace that overrides nothing already runs
168
+ two lanes; the code fallback stays 1, for a stage file that says nothing. Merges still land in
169
+ the wave's listed order whatever the number is.
168
170
 
169
171
  A scope with `—` under `plan` does not run the Plan phase, and Build writes the one story that
170
172
  decision implies (`04-build/implicit-plan.yml`) from your What handoff rather than refusing;
@@ -314,6 +316,8 @@ back on the registry is 0.3.0.
314
316
 
315
317
  | Version | Date | Status | Contains |
316
318
  |---|---|---|---|
319
+ | 0.11.0 | 2026-09-08 | `beta` | governance that leaves a record: every answer says who decided it and what repo it binds, an advisory contradiction check raises a question that never stops a run and states its own limit, the close and the Build handoff count decided vs not-stated; `tldrx story widen` is the sanctioned way to grow a story's surface (a done story needs `reopen --for-fix` first) and a defect no story owns is named in the handoff, the PR body and the boundary card instead of absorbed; a budget grant is a recorded number in `budget.yml` that survives every writer, `budget raise` warns or refuses against it, presets are labelled as the assumptions they are, `triage.budget_basis` says where a figure came from and `tldrx cost --stories` measures each story against the ceiling its spawn was given, honest about lower bounds |
320
+ | 0.10.0 | 2026-09-07 | `beta` | the mechanical blockers measured in real runs, fixed: a refused DoD command is recorded as refused (never a fabricated exit 126) in the event, the handoff, the review and retro logs, and the readers that used to recover it as green; the dirty-tree refusal prints the exact stash and pop commands and its true reason; the reviewer diffs the epic as it was before the merge (`epic_base` recorded on the story and the bundle) instead of an empty range; `tldrx init` probes each declared command once and records `command_probes` beside a `commands:` allowlist it never guesses; `tldrx ship` opens a PR whose body describes the change and its known defects, with the handoff folded underneath, and a state refusal that honours settled stories' declared touches; reviewer turns carry their token split and the spend basis reads it, so a whole provider no longer reads as absent |
317
321
  | 0.9.2 | 2026-09-07 | `beta` | the Build executor decomposed: 4,351 lines became an orchestrator plus eight modules under src/core/build/ (review ledger and phase cost, money caps, the DoD runner with its preflight cache, worktrees and epic-branch claims, the reviewer bundle and review round) — a pure refactor proven byte-for-byte by a golden guard over prompts, ordered events, run.yml rows and exit codes across three scenarios; no behaviour changed, every remaining hardening fix now lands in a file a reviewer can hold |
318
322
  | 0.9.1 | 2026-09-06 | `beta` | records and money that do not lie: a turn without a provider USD figure is unmetered, never a metered $0.00; a refusal no longer discards the turn's cost (banked first, deduped only against marked rows); an event over the 4096-byte cap spills its text beside the run and names it instead of losing every row of the invocation; preflight rows carry `command_hash` and their own `checked_at`, so a cached red is re-probed when the command changed, after 30 minutes, or under --prepare; the fix list writes back git's canonical 40-hex sha; `tldrx facts add` exists with owner/driver attribution rendered in prompts; run.yml rows carry the provider's token split only when reported |
319
323
  | 0.9.0 | 2026-09-06 | `beta` | opt-in stack packs: four language pack bodies and thirteen framework overlays detected from manifests, interrogative by default (Defaults yield to any repo signal, Checks are questions with `verify:` hints), behind one per-project switch (`tldrx expert packs enable`) that materialises into `.tldrx/experts/<lang>-stack/` without touching trained knowledge; the Build reviewer gets `## Stack checks (the repo's own conventions win)`; every stage names the project's `.claude/skills` and the developer may invoke them; workspace.yml records overlays with evidence and skills with a tracked flag, `version: 1` unchanged |