tldr-experts 0.30.0 → 0.31.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 (35) hide show
  1. package/CHANGELOG.md +198 -0
  2. package/README.md +1 -0
  3. package/dist/hooks/answer-capture.js +9 -8
  4. package/dist/hooks/budget-gate.js +9 -9
  5. package/dist/hooks/{chunk-fbmj1bk6.js → chunk-04kpbhvk.js} +3 -3
  6. package/dist/hooks/{chunk-pne7afnf.js → chunk-27v2rwa9.js} +1 -1
  7. package/dist/hooks/{chunk-przta0a0.js → chunk-3r0hhgcf.js} +7 -6
  8. package/dist/hooks/{chunk-857jhjmb.js → chunk-5kv2vtha.js} +1 -1
  9. package/dist/hooks/{chunk-7xascvee.js → chunk-72et1xzx.js} +1 -1
  10. package/dist/hooks/{chunk-0d4csx4s.js → chunk-8ds2ret8.js} +1 -1
  11. package/dist/hooks/{chunk-qmp5p77c.js → chunk-9bd6b3xf.js} +1 -0
  12. package/dist/hooks/chunk-ae6bkfs5.js +0 -0
  13. package/dist/hooks/{chunk-77x6v0gg.js → chunk-akemhezh.js} +3 -2
  14. package/dist/hooks/{chunk-gr83pq7x.js → chunk-f9d2efhb.js} +3 -3
  15. package/dist/hooks/{chunk-zpch8e2j.js → chunk-me0kzc6k.js} +2 -2
  16. package/dist/hooks/{chunk-mg3aj0sa.js → chunk-q1ge3nex.js} +1 -1
  17. package/dist/hooks/{chunk-yanmz0vz.js → chunk-x1f4dxwk.js} +1 -1
  18. package/dist/hooks/{chunk-g4d2v4jc.js → chunk-yyhwf3bp.js} +1 -1
  19. package/dist/hooks/{chunk-z9hv47ps.js → chunk-zyz0fbz5.js} +1 -1
  20. package/dist/hooks/claim-sources.js +5 -5
  21. package/dist/hooks/dod-gate.js +7 -7
  22. package/dist/hooks/no-reask.js +8 -8
  23. package/dist/hooks/session-start.js +13 -12
  24. package/dist/hooks/statusline.js +9 -8
  25. package/dist/tldrx.js +1234 -827
  26. package/package.json +1 -1
  27. package/plugin/.claude-plugin/plugin.json +1 -1
  28. package/stages/how/stage.md +0 -1
  29. package/stages/how/stage.yml +31 -2
  30. package/workflows/bugfix.yml +5 -1
  31. package/workflows/feature.yml +5 -1
  32. package/workflows/integration.yml +5 -1
  33. package/workflows/performance.yml +5 -1
  34. package/workflows/refactor.yml +5 -1
  35. package/workflows/upgrade.yml +5 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,203 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.31.0 — 2026-09-16
4
+
5
+ ### Added
6
+
7
+ - **A `[src:]` citation on a Watch card that only has a punctuation slip — the marker missing its
8
+ space, a token sitting mid-sentence, an ASCII `->` in place of the real arrow — is repaired
9
+ locally, for $0.00, before the card is judged, instead of failing the stage over formatting a
10
+ script already knew how to fix (#345, watch-card half). Measured over two field audits: 10 of 54
11
+ failed tasks across 22 runs in one workspace were malformed watch cards — caught only after a
12
+ full paid turn wrote the card, by the same `parseWatcherCard` this repair now runs ahead of.
13
+ `repairSrcSyntax` (`src/core/text/srcToken.ts`) only accepts a fix it can VERIFY
14
+ re-parses clean (mirrors `parseYamlRepairing`'s own rule) and only touches the three rules that
15
+ are pure syntax — `marker-spelling`, `trailing-position`, `cmd-arrow` — never a rule that would
16
+ need an invented line number, path or fact id (AGENTS.md §7). The repair is written back to disk
17
+ whether or not the card ends up valid, and it is never silent: named in the stage's own report
18
+ when it clears the card, and in the failure reason when a genuine defect remains — the file on
19
+ disk already carries the fix, so a retry's `previousCard` prompt (`watchPrompt.ts`) shows a writer
20
+ only the part a script could not repair (owner decision 2026-09-15: auto-repair is visible, never
21
+ silent).
22
+
23
+ - **`how` is skipped when the seed already declares its own technical solution, and `plan` reads
24
+ that section instead (#346).** Owner decision, measured against three samples where `how`
25
+ reworked a seed that already carried the answer: 11.4-45% of a run's spend and 43-53% of its
26
+ own bytes went unread downstream. The trigger is one exact, documented marker — an H2 `##
27
+ Solution` or `## Technical approach` in a seed document — never inferred from prose:
28
+ `src/core/facilitator/seedSolution.ts` reuses `seed/seedClaims.ts`'s fence-aware heading reader
29
+ rather than a second regex, and exposes it to the existing `skip_if` grammar as a new
30
+ `seed_solution` predicate (`skipIf.ts`, `(stories|repos|questions|seed_solution)<op>N`). Since no
31
+ shipped `workflows/*.yml` uses the §2.4 record shape a workflow-level `skip_if` needs, a stage's
32
+ own top-level `skip_if:` in `stage.yml` is now read as its built-in default when the workflow
33
+ names none (`stageSpec.ts`'s `overlay`, a workflow's own `skip_if` for that stage id still wins);
34
+ the shipped `stages/how/stage.yml` sets `skip_if: "seed_solution==1"` this way. The skip is
35
+ recorded exactly like any other `skip_if` (`stage.skipped`, named on the run), and — because this
36
+ is the one skip that would otherwise leave `plan` reading a `02-how/design.md` that silently
37
+ looks empty — `tldrx next` additionally materialises that file from the seed's declared section
38
+ verbatim, cited back to the seed, the moment the skip fires.
39
+
40
+ - **A failed sub-agent turn now carries a named `failure_kind`** — `timeout`, `rate_limit`
41
+ (reusing #298's own quota-frame detection), `process_killed`, `empty_result`, `non_zero_exit`,
42
+ `result_error`, `malformed_result`, or `unclassified` (with the raw signal still in `error`) —
43
+ on the task row and the `agent.result` event, additive and absent on every ordinary/successful
44
+ turn and on a read-cap kill (`stopped_by` already names that one). `result_error` is
45
+ `non_zero_exit`'s exitCode-0 sibling (pre-merge review, 2026-09-15): the result document itself
46
+ reports `is_error: true` with a named reason while the process exited `0`, which used to read
47
+ `non_zero_exit` — a label contradicting its own exit code. Measured, one field audit (workspace
48
+ B, 2026-09-15): 24 of 54 failed tasks in that sample were an undifferentiated "timeout / generic
49
+ error" — the largest single bucket, unnamed until now. Classification only: no new retry
50
+ budgets, and nothing decides a retry off this field (#348, the family of #341/#298).
51
+
52
+ ### Changed
53
+
54
+ - **`watch` now follows `build`'s gate policy in every shipped scope where `build` is already
55
+ `auto` — `feature`, `bugfix`, `integration`, `refactor` and `performance` (#349, owner decision
56
+ 2026-09-16).** Every one of these scopes shipped `build: auto` with `watch: human` regardless,
57
+ so a run that trusted Build's own auto-gate still stopped a person at the very next stage over
58
+ a transcription job with a validator behind it. The reasoning is field evidence, not a guess:
59
+ auto-gating `what`/`how`/`plan` in a real workspace since 2026-09-12 produced zero measured
60
+ auto-gate incidents across 26 runs (77 `gate.rejected`, 100% human, 0 auto-rejections). The
61
+ mechanism is unchanged — `evaluateAutoGate` (`src/core/run/autoGate.ts`) already closes any
62
+ stage's gate the same way once its policy is `auto`, and a self-signed `watch` gate is
63
+ announced through the exact same `gate.requested`/`gate.approved`/`stage.done` events an
64
+ auto-signed `what`/`how`/`plan` gate already is — this does not widen the notify-window gap
65
+ tracked separately in #250, it only extends the existing behavior to one more stage. `upgrade`
66
+ is the one shipped scope excluded: `what`/`plan`/`build` are already all `auto` there, and
67
+ following `build` would leave it with no human gate at all, which spec.md's "no all-auto
68
+ preset" guarantee forbids — its `watch` stays `human`. `hotfix`/`security-patch`/`migration`
69
+ are unaffected: their `build` gate is `human`, so the "build already signed" condition never
70
+ applies. The tutorial's attended chapter (`tldrx learn`, chapter 7) ran its `next --commit` on
71
+ a `feature`-scope run expecting the Watch gate to still stop it (`expectExit: [4]`) — now that
72
+ `watch` self-signs there, the chapter sets it back to `human` explicitly first
73
+ (`run gates set watch:human --note …`), because the lesson is the attended human handoff, not
74
+ the shipped default.
75
+
76
+ - **`how`'s default model is `sonnet`, not `opus`** (#346). Owner decision, measured across a
77
+ single-run analysis and two workspace audits: `how` cost $10.40-$269.90 per sample, 11.4-45% of
78
+ the run's total spend. A workspace or a single invocation that wants the strong model back
79
+ overrides it exactly as before — `.tldrx/stages/how/stage.yml` with `model: opus`, or
80
+ `tldrx next --model opus` / `tldrx run auto --model opus` for one invocation.
81
+
82
+ - **`how` stops generating `risks.md`** (#350). Owner decision 2026-09-16 — "stop generating,"
83
+ not "generate and drop from the bundle" — because nothing downstream ever read it: measured
84
+ on a real run, `risks.md` (8,025 bytes) was excluded by `stages/plan/stage.yml`'s own
85
+ `inputs:` (only `design.md`, `contracts.md`, `test-strategy.md`) and a repo-wide grep found no
86
+ other reader. `handoff.md` and `questions.md` are unchanged — they carry no bundle content
87
+ either, but they are load-bearing elsewhere (the auto gate, `claim-sources`, `boundary`'s
88
+ `SURFACE_HANDOFFS`, the dashboard). `validateOutputs` only ever checks the DECLARED output
89
+ list, so a run directory written before this change that still has `02-how/risks.md` on disk
90
+ is unaffected — the file is simply not asked for anymore, never flagged as missing or
91
+ re-demanded on resume.
92
+
93
+ ### Fixed
94
+
95
+ - **A headless turn whose parent `tldrx` process is SIGKILL-class killed mid-spawn now banks its
96
+ cost as unmetered rather than nowhere at all (#337, the remaining half of #246).** #246's field
97
+ case: a `run auto` process died while blocked in `spawnAgent`'s `await` (`runNext.ts`); the
98
+ orphaned `claude` child kept running to completion, but `run.yml`'s `budget.spent_usd` stayed
99
+ `0.00` forever, because `demoteStaleRunning` — the code that finds the dead `.lock` and puts the
100
+ stage back to `ready` — wrote no task row for that attempt at all, not even an unmetered one. A
101
+ confident `0.00` for money a provider may really have charged is the one answer AGENTS.md §7
102
+ rules out. `demoteStaleRunning` now reads `startedHeadless` (`run/lastStart.ts`, the one
103
+ derivation of which mode opened a stage's last turn — already used by the Ctrl-C path) and, for
104
+ a genuinely orphaned HEADLESS spawn (never a `--prepare` bundle waiting for a human, and never a
105
+ Build executor phase, both of which are `preparedRefusal`'s and the executor's own business),
106
+ records a `status: "failed"` task with `cost_usd: null, metered: false` and
107
+ `error: "turn outlived its parent; no result line was read"` before demoting the stage — so
108
+ `spendBasis`/`tallyOf` (the one derivation, unchanged) count it as a LOWER BOUND, and `run
109
+ status`/`budget show`/the dashboard say so instead of printing a silent `$0.00`.
110
+
111
+ - **`run auto` now names `--wait-gates` as the fix, at the moment an all-`auto` gate's only
112
+ blocking questions are answered but nothing will sign it (#342).** Measured live on a
113
+ `--gates none --questions none` run launched with no `--wait-gates`/`--wait-answers`: the
114
+ loop answers a blocking question itself (`questions_policy: recommended`) or once
115
+ `--wait-answers` polls one in, but the ONLY thing that ever self-closes a parked `auto`
116
+ gate is `selfCloseAutoGate`, reached only from `waitForGate`'s poll — so the next `next`
117
+ call reports the stage as `awaiting_gate` with a generic `gate pending: tldrx approve`,
118
+ never saying the gate would have closed itself with one more flag. `settleDeferredGate`
119
+ (`runAuto.ts`) now says so, by name, at the exact moment the gap is decided. The deeper fix
120
+ — `next` re-evaluating a parked `auto` gate on its own, so this holds even for a `next`
121
+ launched by hand — is recommendation (1) on the issue and stays open there; it touches
122
+ `runNext.ts`, out of scope for this change.
123
+
124
+ - **The base-tree DoD refusal now names the workspace's own declared `install:` as the fix
125
+ (#343).** Measured on a fresh clone where a multi-step installer had only partly run: the
126
+ refusal reported `eslint`/`tsc`/`vitest` all `command not found`, identical in shape to a
127
+ genuinely broken repo, even though the same `WorkspaceContext` the refusal already reads
128
+ carries the workspace's own `install:` line. `baseRefusalLines` (`preflight.ts`) now names
129
+ it, once per repo — never runs it: running an installer against the operator's own
130
+ checkout, unasked, ahead of a refusal that has not yet told them anything, is exactly the
131
+ side effect the base-measurement design already avoids for gate commands. The zero-touch
132
+ first-run guide (EN/ES) now says explicitly to run the declared `install:` in the checkout
133
+ itself before the first `run auto`, not only in a story's worktree.
134
+
135
+ - **`run status` now warns NO-RETRY on a phase that cannot afford a second attempt of its own
136
+ stage, not only `budget show` (#232).** The narrowed #232 asked for this at both screens;
137
+ `budget show` shipped it in `bd19763`/`efb4eee` and this closes the other half. `run status`
138
+ computed no retry verdict at all, so a pre-#170 run (sized for exactly one attempt) read every
139
+ phase as an ordinary progress bar right up to the refusal — the exact "operator discovers it at
140
+ the retry" failure the issue is about, just on the screen that gets opened more often. Shares
141
+ the one `retrySizing` derivation and the one `NO-RETRY` sentence (`noRetryBlock`, now exported
142
+ from `budgetView.ts`) that `budget show` already used, so the two screens cannot disagree about
143
+ the same measured shortfall. `buildStatus` takes an optional `root` (both CLI and `run auto`'s
144
+ heartbeat now pass it) to resolve the next stage's own `attempts:` the same tolerant way
145
+ `budget show` does; omitted, it falls back to the shipped default, so a caller that does not pass
146
+ it behaves exactly as before.
147
+
148
+ - **A fix list dispositioned away from `fix-now` by hand, after the story already parked `review`
149
+ behind it, now settles `done` with no agent spawned instead of buying a fresh developer round and
150
+ a full DoD re-run for nothing (#218).** Measured live: a reviewer signed `fixlist` with an open
151
+ finding, parking the story at `review`; the finding was later re-routed away from `fix-now`
152
+ outside the audited auto-close (which only ever closes what a spawned reviewer's approve was
153
+ shown); nothing else re-read the file, so the next `--prepare`/headless pass read `fixlistFor`'s
154
+ unnamed door returning `null` (0 open — correctly, it is not to be re-rendered) as "no fix list
155
+ ever happened" and dispatched a plain developer bundle for a story nothing faulted — one
156
+ developer task and one full DoD re-run over an unchanged tree. `closedFixlistCandidates` /
157
+ `settleClosedFixlists` (gh #329's pre-pass) now also recognizes a `review` story parked on a spent
158
+ `fixlist` verdict, alongside the `blocked`/`approve` shape it already settled, and asks
159
+ `openFixNow` before either. `--fixlist <path>` naming a file with 0 open findings is now a refusal
160
+ that names the file and the count, not a silent downgrade to a bundle.
161
+
162
+ - **A fix list the parser could not fully read — a typo'd `Disposition:` word, or a round file
163
+ emptied by a crash or a disk-full write — can no longer settle a story `done` (#218, pre-merge
164
+ review).** Reviewer-measured: `**fix-now**` mistyped `**fix-noww**` is silently dropped by
165
+ `parseFixlistFile` (by design — a heading with no readable disposition is not a finding, and
166
+ inventing `fix-now` for it would block a story over a typo), so `openFindings(...).length` read 0
167
+ exactly as it would for a genuinely closed round, and a plain `--prepare` settled the story `done`
168
+ with no spawn and no re-review over a live, undetected defect. `parseFixlistFile`'s parse now also
169
+ reports what it could not read (`unreadableFindings`/`fixlistFullyParsed`, `fixlist.ts`), and
170
+ `openFixNow`/`closedFixlistCandidates` (both the `blocked`/`approve` shape and the `review`/
171
+ `fixlist` shape above share this one check) hold rather than settle when a round parsed
172
+ uncleanly, naming the exact heading and line that could not be read.
173
+
174
+ - **`--fixlist <path>` naming a file this framework cannot fully parse — corrupted, truncated, or
175
+ another story's misnamed file — refuses with exit 1 (usage), not exit 5 (agent failed) (#218,
176
+ pre-merge review).** The refusal used to throw out of `fixlistFor` and land in the generic
177
+ executor `catch`, which reads any thrown `Error` as a STAGE failure: `run.yml`'s stage was stamped
178
+ `status: failed` with `cost_usd: 0.00`, and the next `tldrx next` printed "retrying … (cost already
179
+ spent is not refunded)" — a lie, since the refusal fires before anything is opened or spent. It
180
+ now returns the same `refusedOnSequence` precondition-refusal `offerAtFrontier`'s dependency wait
181
+ already uses: exit 1, `run.yml` untouched, no false retry line. The unnamed door's own corruption
182
+ check is scoped to `--prepare` only (the one caller with a `try`/`catch` around it) — the headless
183
+ in-session fix-round spawn keeps the pre-#218 fallback (a corrupted round is treated as absent
184
+ rather than crashing the run mid-wave), since the correctness half above already closes the
185
+ dangerous direction independently of this door.
186
+
187
+ - **A relaunch of `tldrx run auto <runId>` resuming its OWN already-claimed epic branch now
188
+ verifies it instead of trusting it silently (#347).** `run auto` does not expose `--reuse-epic`
189
+ (there is nobody present to type it), so `foreignEpicRefusal`'s `claimed.has(branch)` case — this
190
+ run's own `build.epic_branch` already naming the branch a killed attempt cut and claimed before
191
+ it died — was a bare `continue`: no check that the branch head still resolved, no gate, and no
192
+ record that anything had been resumed at all. It now confirms the branch is still reachable and,
193
+ when the repo declares a `typecheck` command, runs it once in a throwaway detached worktree
194
+ (removed immediately after) before trusting the claim; a repo with no `typecheck` role is
195
+ adopted with that said explicitly (`typecheck: absent`) rather than assumed green. Recorded
196
+ honestly either way — an `epic.resumed` line and event carrying the sha and the typecheck
197
+ verdict — and a typecheck that FAILS refuses the stage by name instead of building on a possibly
198
+ broken branch (`tldrx next --reuse-epic` still adopts it deliberately, unchanged). A claim
199
+ another run wrote is refused exactly as before — nothing here relaxes that guard.
200
+
3
201
  ## 0.30.0 — 2026-09-15
4
202
 
5
203
  ### Fixed
package/README.md CHANGED
@@ -335,6 +335,7 @@ back on the registry is 0.3.0.
335
335
 
336
336
  | Version | Date | Status | Contains |
337
337
  |---|---|---|---|
338
+ | 0.31.0 | 2026-09-16 | `beta` | Ten efficiency adjustments, ranked from a measured audit of 48 runs, so an unattended run spends less and stalls less. `how` is skipped when the seed already declares its own solution, and runs on `sonnet` when it does run (#346); `risks.md` is no longer generated (#350); `watch` follows `build`'s gate policy, so an all-auto run signs itself (#349); a Watch card's `[src:]` punctuation slip is repaired locally instead of costing a turn (#345); a fix list with zero open findings settles the round, an unreadable one holds it by name (#218); `run status` warns NO-RETRY before a retry the budget refuses (#232); a failed headless turn carries a named `failure_kind`, so the loop stops buying the same retry for every death (#348); an orphaned turn is banked as unmetered instead of a confident zero (#337); a relaunch resumes its own epic branch instead of restarting it (#347); the base-tree DoD refusal names the workspace `install:` (#343) and `run auto` names `--wait-gates` (#342). |
338
339
  | 0.30.0 | 2026-09-15 | `beta` | Seventeen changes that keep an unattended run moving instead of stranding. A cached Build refusal says so and a relaunch re-measures it (#339); a rate-limit warning is read instead of dropped, so a Build parks before the wall (#298); the plan-over-stage advisory speaks only past 2× (#302); a stage death stops recommending a retry the budget refuses (#232); a DoD command is measured twice, a fix-list finding is checked against the epic tip, and an over-long sha is refused by name (#163); a formatting slip gets one bounded repair turn (#288); five Checks stop asking the reviewer for what it can't do (#195); a Codex review no longer dies before it runs (#148); `tldrx init` probes four commands at once (#180); `tldrx ship` refuses a merged diff nobody judged (#311). Docs: the zero-touch recipe pairs `run auto` with `--wait-gates`/`--wait-answers` (#342). |
339
340
  | 0.29.0 | 2026-09-15 | `beta` | Sixteen changes that make an unattended run trust its own gates and its own numbers. Verdict integrity: a reviewer's `changes` must cite evidence (#326), a re-reviewed story's `changes` is consumed instead of parking it (#327), and a story blocked on a stale fix list settles `done` once resolved (#329). Fix-list flow: a headless fix list now buys its own fix round, and an approving reviewer's signature closes what it was shown (#327). Money and phase sizing: `run auto` rebalances a blocked phase's shortfall out of finished phases by default (#330), the `budget-gate` hook stops denying the launch that would fix the shortfall (#321), a Build gate no longer stops on paths outside the declared surface but carries them into the PR instead (#331), a parallel wave no longer hands two developers the same unspent money (#325), `budget show`/`run estimate`/the budget-gate hook price remaining work off the stage's own `attempts:` (#214), `run auto --until-done` states a token refusal in tokens rather than invented dollar words (#270), a Watch turn is recorded as unmetered rather than a false `$0.00` (#224), and a Watch spawn now records the ceiling it was given (#190). Dependency and safety: a Build gate names the story a dependent waits on (#303), the conflict-turn marker guard names unreadable paths, bounds its scan, and covers modified files too (#324), a developer's array-valued `notes` is joined instead of silently dropped (#217), and the Plan prompt states its per-item character cap where each field is described so an over-cap finding survives into the retry (#328). |
340
341
  | 0.28.1 | 2026-09-15 | `beta` | Two fixes so a reopened story's reason reaches the agents that need it. The developer and reviewer prompts now carry `## Why this story was reopened` — who signed it, whether it's a fix round, the note verbatim — off the same ledger value #308 reads, instead of the note reaching only a report line and #308's check (closes #322). `tldrx note --help` now says an operator note reaches no agent and names `dispatch-notes.md` as the channel that does (closes #151). |
@@ -1,23 +1,24 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  conflictOf
4
- } from "./chunk-fbmj1bk6.js";
4
+ } from "./chunk-04kpbhvk.js";
5
5
  import {
6
6
  FactsStore,
7
7
  formatJaccard
8
- } from "./chunk-gr83pq7x.js";
8
+ } from "./chunk-f9d2efhb.js";
9
9
  import {
10
10
  parseHookInput,
11
11
  readStdin
12
- } from "./chunk-z9hv47ps.js";
12
+ } from "./chunk-zyz0fbz5.js";
13
+ import"./chunk-ae6bkfs5.js";
13
14
  import {
14
15
  EventLog,
15
16
  PHASE_ID_RE
16
- } from "./chunk-77x6v0gg.js";
17
+ } from "./chunk-akemhezh.js";
17
18
  import {
18
19
  PHASE_IDS
19
- } from "./chunk-g4d2v4jc.js";
20
- import"./chunk-857jhjmb.js";
20
+ } from "./chunk-yyhwf3bp.js";
21
+ import"./chunk-5kv2vtha.js";
21
22
  import {
22
23
  ADVISORY_KEY,
23
24
  MAX_FACT_CHARS,
@@ -27,7 +28,7 @@ import {
27
28
  renderQuestionBlock,
28
29
  replaceBlock,
29
30
  serializeQuestions
30
- } from "./chunk-yanmz0vz.js";
31
+ } from "./chunk-x1f4dxwk.js";
31
32
  import {
32
33
  ITERATION_ONLY_SLOT,
33
34
  PROJECT_FRAMEWORK_DIR,
@@ -37,7 +38,7 @@ import {
37
38
  isScopedTemplate,
38
39
  parseYaml,
39
40
  scopedSlotOf
40
- } from "./chunk-qmp5p77c.js";
41
+ } from "./chunk-9bd6b3xf.js";
41
42
 
42
43
  // src/hooks/answer-capture.ts
43
44
  import { existsSync as existsSync5 } from "fs";
@@ -1,15 +1,15 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  budgetGateDeny
4
- } from "./chunk-mg3aj0sa.js";
4
+ } from "./chunk-q1ge3nex.js";
5
5
  import {
6
6
  allow,
7
7
  deny,
8
8
  readPayload,
9
9
  runHook,
10
10
  toolInput
11
- } from "./chunk-pne7afnf.js";
12
- import"./chunk-z9hv47ps.js";
11
+ } from "./chunk-27v2rwa9.js";
12
+ import"./chunk-zyz0fbz5.js";
13
13
  import {
14
14
  REBALANCE_SOURCE,
15
15
  applyRebalance,
@@ -27,10 +27,10 @@ import {
27
27
  validateRunBudget,
28
28
  wouldExceed,
29
29
  wouldExceedHostTokens
30
- } from "./chunk-zpch8e2j.js";
30
+ } from "./chunk-me0kzc6k.js";
31
31
  import {
32
32
  EventLog
33
- } from "./chunk-77x6v0gg.js";
33
+ } from "./chunk-akemhezh.js";
34
34
  import {
35
35
  cursorStage,
36
36
  hostTokensIn,
@@ -39,20 +39,20 @@ import {
39
39
  newestActiveRun,
40
40
  renderRunEconomies,
41
41
  runSpend
42
- } from "./chunk-0d4csx4s.js";
42
+ } from "./chunk-8ds2ret8.js";
43
43
  import {
44
44
  buildStageDefaults
45
- } from "./chunk-g4d2v4jc.js";
45
+ } from "./chunk-yyhwf3bp.js";
46
46
  import {
47
47
  noteDeprecations
48
- } from "./chunk-yanmz0vz.js";
48
+ } from "./chunk-x1f4dxwk.js";
49
49
  import {
50
50
  PROJECT_WORK_DIR,
51
51
  findWorkspaceRoot,
52
52
  locateWork,
53
53
  parseYaml,
54
54
  stageYamlPath
55
- } from "./chunk-qmp5p77c.js";
55
+ } from "./chunk-9bd6b3xf.js";
56
56
 
57
57
  // src/hooks/budget-gate.ts
58
58
  import { existsSync as existsSync2, readFileSync as readFileSync2, statSync } from "node:fs";
@@ -1,15 +1,15 @@
1
1
  import {
2
2
  findDuplicate
3
- } from "./chunk-gr83pq7x.js";
3
+ } from "./chunk-f9d2efhb.js";
4
4
  import {
5
5
  evidencePath,
6
6
  gateEvidencePath,
7
7
  parseEvidence
8
- } from "./chunk-77x6v0gg.js";
8
+ } from "./chunk-akemhezh.js";
9
9
  import {
10
10
  openBlocks,
11
11
  parseQuestions
12
- } from "./chunk-yanmz0vz.js";
12
+ } from "./chunk-x1f4dxwk.js";
13
13
 
14
14
  // src/core/distill/distill.ts
15
15
  var CONFLICT_THRESHOLD = 0.6;
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  parseHookInput,
3
3
  readStdin
4
- } from "./chunk-z9hv47ps.js";
4
+ } from "./chunk-zyz0fbz5.js";
5
5
 
6
6
  // src/hooks/lib/decide.ts
7
7
  function deny(reason) {
@@ -9,7 +9,7 @@ import {
9
9
  spentBasis,
10
10
  tallyOf,
11
11
  validateRunBudget
12
- } from "./chunk-zpch8e2j.js";
12
+ } from "./chunk-me0kzc6k.js";
13
13
  import {
14
14
  EventLog,
15
15
  OUTCOME_NOT_RECORDED,
@@ -22,12 +22,12 @@ import {
22
22
  isTerminal,
23
23
  stageAt,
24
24
  validateRunFile
25
- } from "./chunk-77x6v0gg.js";
25
+ } from "./chunk-akemhezh.js";
26
26
  import {
27
27
  cursorStage,
28
28
  isAttendedByHostView,
29
29
  openRunViews
30
- } from "./chunk-0d4csx4s.js";
30
+ } from "./chunk-8ds2ret8.js";
31
31
  import {
32
32
  backupPathFor,
33
33
  isAlive,
@@ -36,13 +36,13 @@ import {
36
36
  workspaceRootOfRunDir,
37
37
  writeAtomic,
38
38
  yamlScalar
39
- } from "./chunk-857jhjmb.js";
39
+ } from "./chunk-5kv2vtha.js";
40
40
  import {
41
41
  isAdvisory,
42
42
  noteDeprecations,
43
43
  openBlocks,
44
44
  parseQuestions
45
- } from "./chunk-yanmz0vz.js";
45
+ } from "./chunk-x1f4dxwk.js";
46
46
  import {
47
47
  FRAMEWORK_ROOT,
48
48
  MAX_ITEM_CHARS,
@@ -50,7 +50,7 @@ import {
50
50
  listRunDirs,
51
51
  parseYaml,
52
52
  parseYamlRepairing
53
- } from "./chunk-qmp5p77c.js";
53
+ } from "./chunk-9bd6b3xf.js";
54
54
 
55
55
  // src/core/statusline/runSnapshot.ts
56
56
  import { existsSync as existsSync3, readFileSync as readFileSync4 } from "node:fs";
@@ -134,6 +134,7 @@ function task(t, indent) {
134
134
  `${inner}error: ${yamlScalar(t.error)}, session_id: ${yamlScalar(t.session_id)},`,
135
135
  `${inner}started_at: ${yamlScalar(t.started_at)}, ended_at: ${yamlScalar(t.ended_at)},`,
136
136
  ...t.stopped_by === undefined || t.stopped_by === null ? [] : [`${inner}stopped_by: ${yamlScalar(t.stopped_by)},`],
137
+ ...t.failure_kind === undefined || t.failure_kind === null ? [] : [`${inner}failure_kind: ${yamlScalar(t.failure_kind)},`],
137
138
  ...t.banked_before_refusal === undefined ? [] : [`${inner}banked_before_refusal: true,`],
138
139
  ...t.dedupe === undefined ? [] : [`${inner}dedupe: ${yamlScalar(t.dedupe)},`],
139
140
  ...t.duration_ms === undefined ? [] : [`${inner}duration_ms: ${String(Math.round(t.duration_ms))}, ` + `duration_basis: ${yamlScalar(t.duration_basis ?? null)},`],
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  PROJECT_FRAMEWORK_DIR,
3
3
  PROJECT_WORK_DIR
4
- } from "./chunk-qmp5p77c.js";
4
+ } from "./chunk-9bd6b3xf.js";
5
5
 
6
6
  // src/core/lock/workspaceLock.ts
7
7
  import { existsSync as existsSync2, mkdirSync as mkdirSync2, openSync, readFileSync as readFileSync2, rmSync as rmSync2, writeSync, closeSync } from "node:fs";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  toolInput
3
- } from "./chunk-pne7afnf.js";
3
+ } from "./chunk-27v2rwa9.js";
4
4
 
5
5
  // src/hooks/lib/wouldBe.ts
6
6
  import { existsSync, readFileSync } from "node:fs";
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  listRunDirs,
3
3
  parseYaml
4
- } from "./chunk-qmp5p77c.js";
4
+ } from "./chunk-9bd6b3xf.js";
5
5
 
6
6
  // src/hooks/lib/runFile.ts
7
7
  import { existsSync, readFileSync } from "node:fs";
@@ -7969,6 +7969,7 @@ function diagnoseSrcToken(line, repos) {
7969
7969
  }
7970
7970
  return { rule: srcRule(id), line: quotable(line), piece: null };
7971
7971
  }
7972
+ var MECHANICALLY_REPAIRABLE = new Set(["marker-spelling", "trailing-position", "cmd-arrow"]);
7972
7973
  function parseSrcToken(line, repos) {
7973
7974
  const trimmed = trimTrailingClosers(line);
7974
7975
  const match = TRAILING_TOKEN_RE.exec(trimmed);
File without changes
@@ -2,7 +2,7 @@ import {
2
2
  GATE_POLICIES,
3
3
  validateGatesPolicy,
4
4
  validateStagePolicy
5
- } from "./chunk-g4d2v4jc.js";
5
+ } from "./chunk-yyhwf3bp.js";
6
6
  import {
7
7
  asDocument,
8
8
  isRecord,
@@ -15,7 +15,7 @@ import {
15
15
  requireString,
16
16
  requireVersion,
17
17
  result
18
- } from "./chunk-qmp5p77c.js";
18
+ } from "./chunk-9bd6b3xf.js";
19
19
 
20
20
  // src/core/events/EventLog.ts
21
21
  import { appendFileSync, existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
@@ -57,6 +57,7 @@ var EVENT_TYPES = [
57
57
  "story.touches_widened",
58
58
  "plan.fix_round",
59
59
  "epic.released",
60
+ "epic.resumed",
60
61
  "worktree.foreign_work_aside",
61
62
  "worktree.foreign_work_restored",
62
63
  "result.unreadable",
@@ -3,7 +3,7 @@ import {
3
3
  withWorkspaceLock,
4
4
  workspaceRootOfFactsPath,
5
5
  writeAtomic
6
- } from "./chunk-857jhjmb.js";
6
+ } from "./chunk-5kv2vtha.js";
7
7
  import {
8
8
  FACT_CONFIDENCES,
9
9
  FACT_DECIDERS,
@@ -15,7 +15,7 @@ import {
15
15
  isLive,
16
16
  isRetired,
17
17
  noteDeprecations
18
- } from "./chunk-yanmz0vz.js";
18
+ } from "./chunk-x1f4dxwk.js";
19
19
  import {
20
20
  SRC_PATTERNS,
21
21
  asDocument,
@@ -28,7 +28,7 @@ import {
28
28
  requireString,
29
29
  requireVersion,
30
30
  result
31
- } from "./chunk-qmp5p77c.js";
31
+ } from "./chunk-9bd6b3xf.js";
32
32
 
33
33
  // src/core/facts/FactsStore.ts
34
34
  import { existsSync, readFileSync } from "node:fs";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  STAGE_TUNING_DEFAULTS
3
- } from "./chunk-g4d2v4jc.js";
3
+ } from "./chunk-yyhwf3bp.js";
4
4
  import {
5
5
  MAX_PLAN_STORIES,
6
6
  MAX_STORIES_PER_WAVE,
@@ -25,7 +25,7 @@ import {
25
25
  requireVersion,
26
26
  requireVersion1,
27
27
  result
28
- } from "./chunk-qmp5p77c.js";
28
+ } from "./chunk-9bd6b3xf.js";
29
29
 
30
30
  // src/hooks/lib/actor.ts
31
31
  function currentActor() {
@@ -3,7 +3,7 @@ import {
3
3
  EMPTY_SECTION_RULE,
4
4
  noneBullet,
5
5
  srcRule
6
- } from "./chunk-qmp5p77c.js";
6
+ } from "./chunk-9bd6b3xf.js";
7
7
 
8
8
  // src/hooks/lib/messages.ts
9
9
  var GRAMMAR = "[src: <repo:path:line> | https://… | Q<n> | F<n> | $ <cmd> → exit <n> | graph:<node> | absent:<path>]";
@@ -3,7 +3,7 @@ import {
3
3
  parseSrcToken,
4
4
  srcToken,
5
5
  withoutSrcToken
6
- } from "./chunk-qmp5p77c.js";
6
+ } from "./chunk-9bd6b3xf.js";
7
7
 
8
8
  // src/core/text/questions.ts
9
9
  var REQUIRED_METADATA_KEYS = ["id", "status", "area", "asked_by", "asked_at"];
@@ -6,7 +6,7 @@ import {
6
6
  isRecord,
7
7
  loadWorkspace,
8
8
  parseYaml
9
- } from "./chunk-qmp5p77c.js";
9
+ } from "./chunk-9bd6b3xf.js";
10
10
 
11
11
  // src/core/run/workflowPreset.ts
12
12
  import { existsSync, readFileSync } from "node:fs";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  runtime
3
- } from "./chunk-qmp5p77c.js";
3
+ } from "./chunk-9bd6b3xf.js";
4
4
 
5
5
  // src/core/hooks/passthrough.ts
6
6
  async function readStdin() {
@@ -1,13 +1,13 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  wouldBeContent
4
- } from "./chunk-7xascvee.js";
4
+ } from "./chunk-72et1xzx.js";
5
5
  import {
6
6
  claimSourcesDeny,
7
7
  claimSourcesEmptySectionDeny,
8
8
  claimSourcesMalformedDeny,
9
9
  claimSourcesUnresolvedDeny
10
- } from "./chunk-mg3aj0sa.js";
10
+ } from "./chunk-q1ge3nex.js";
11
11
  import {
12
12
  allow,
13
13
  deny,
@@ -16,8 +16,8 @@ import {
16
16
  postContext,
17
17
  readPayload,
18
18
  runHook
19
- } from "./chunk-pne7afnf.js";
20
- import"./chunk-z9hv47ps.js";
19
+ } from "./chunk-27v2rwa9.js";
20
+ import"./chunk-zyz0fbz5.js";
21
21
  import {
22
22
  isHandoff,
23
23
  loadWorkspace,
@@ -25,7 +25,7 @@ import {
25
25
  toSrcContext,
26
26
  validateCitations,
27
27
  validateHandoff
28
- } from "./chunk-qmp5p77c.js";
28
+ } from "./chunk-9bd6b3xf.js";
29
29
 
30
30
  // src/hooks/claim-sources.ts
31
31
  await runHook("claim-sources", async () => {
@@ -1,13 +1,13 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  wouldBeContent
4
- } from "./chunk-7xascvee.js";
4
+ } from "./chunk-72et1xzx.js";
5
5
  import {
6
6
  dodGateDeny,
7
7
  dodGateInternalErrorDeny,
8
8
  dodGateMissingBlockDeny,
9
9
  dodGateRefusedCommandDeny
10
- } from "./chunk-mg3aj0sa.js";
10
+ } from "./chunk-q1ge3nex.js";
11
11
  import {
12
12
  allow,
13
13
  deny,
@@ -15,15 +15,15 @@ import {
15
15
  filePathOf,
16
16
  isWriteOrEdit,
17
17
  readPayload
18
- } from "./chunk-pne7afnf.js";
19
- import"./chunk-z9hv47ps.js";
18
+ } from "./chunk-27v2rwa9.js";
19
+ import"./chunk-zyz0fbz5.js";
20
20
  import {
21
21
  loadRunView
22
- } from "./chunk-0d4csx4s.js";
22
+ } from "./chunk-8ds2ret8.js";
23
23
  import {
24
24
  DEFAULT_TIMEOUT_S,
25
25
  buildStageDefaults
26
- } from "./chunk-g4d2v4jc.js";
26
+ } from "./chunk-yyhwf3bp.js";
27
27
  import {
28
28
  DodCommandRefused,
29
29
  loadWorkspace,
@@ -31,7 +31,7 @@ import {
31
31
  readStory,
32
32
  repoPath,
33
33
  runDodCommand
34
- } from "./chunk-qmp5p77c.js";
34
+ } from "./chunk-9bd6b3xf.js";
35
35
 
36
36
  // src/hooks/dod-gate.ts
37
37
  import { basename, join } from "node:path";