tldr-experts 0.29.0 → 0.30.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.
- package/CHANGELOG.md +404 -0
- package/README.md +1 -0
- package/dist/hooks/answer-capture.js +2 -2
- package/dist/hooks/budget-gate.js +2 -2
- package/dist/hooks/{chunk-t6gbx3jg.js → chunk-77x6v0gg.js} +2 -0
- package/dist/hooks/{chunk-erasdab5.js → chunk-fbmj1bk6.js} +1 -1
- package/dist/hooks/{chunk-ggjxes7m.js → chunk-przta0a0.js} +35 -3
- package/dist/hooks/{chunk-w3d9kxcp.js → chunk-zpch8e2j.js} +3 -0
- package/dist/hooks/session-start.js +8 -4
- package/dist/hooks/statusline.js +3 -3
- package/dist/tldrx.js +1778 -764
- package/package.json +1 -1
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/templates/experts/stack/dotnet.md +4 -3
- package/templates/experts/stack/javascript.md +4 -3
- package/templates/experts/stack/overlays/postgres-testcontainers.md +7 -2
- package/templates/experts/stack/python.md +4 -3
- package/templates/experts/stack/typescript.md +4 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,409 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.30.0 — 2026-09-15
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
|
|
7
|
+
- **The plan-over-stage advisory speaks only when the plan asks for more than 2× what the stage
|
|
8
|
+
holds, instead of on every overage (#302).** #281 gave a scaled plan a voice before a spawn —
|
|
9
|
+
right, and the reason a $16.20 stage over a $114.00 plan no longer has to be learned from a dead
|
|
10
|
+
developer. Unconditioned, that voice was loud: MEASURED by the #281 pre-merge reviewer over 30
|
|
11
|
+
priced run dirs on one machine, 8 of them (27%; 54% in one of the two workspaces) had
|
|
12
|
+
`priceScale < 1`, at severities from 0.095 to 0.85 — and at the mild end the plan asks ~18% more
|
|
13
|
+
than the stage and usually finishes without one story reaching its scaled cap. A line on every
|
|
14
|
+
Build entry and every Plan gate for a case that harmless is the wallpaper that teaches an
|
|
15
|
+
operator to skip the channel before the run where it matters, the same shape #285/#290 already
|
|
16
|
+
cost once. So the PROACTIVE channel gets a bar — `PLAN_OVER_STAGE_ADVISORY_SCALE`, strictly below
|
|
17
|
+
0.5, in `caps.ts` beside the reasoning, one constant that both call sites inherit by calling the
|
|
18
|
+
same function — and the REACTIVE one keeps none: `capDeathReason` still names the formula, the
|
|
19
|
+
scale and the lever at every scale, because a story that actually died on its cap is never
|
|
20
|
+
wallpaper. The severe tail the channel exists for (0.095, >10× the stage) still speaks before the
|
|
21
|
+
spawn.
|
|
22
|
+
|
|
23
|
+
- **The provider's rate-limit warning is read instead of dropped, and a Build parks at a story
|
|
24
|
+
boundary before the wall instead of spending developers into it (#298, warning half).** The signal
|
|
25
|
+
was never missing, and the repo did not have to leave its own tree to find that out: `claude
|
|
26
|
+
--output-format stream-json` emits `{"type":"rate_limit_event","rate_limit_info":{…}}` WHILE a turn
|
|
27
|
+
is still working, `agentEvents.ts`'s own header has documented one since the transcript was
|
|
28
|
+
recorded, and `test/fixtures/agent/stream-json.jsonl:9` carries it in full — while
|
|
29
|
+
`test/agent-stream.test.ts` pinned it as noise, in the same list as `"{broken"`. It fell through
|
|
30
|
+
the dispatch `switch`'s `default:` and vanished, so the only trace a quota wall left was a
|
|
31
|
+
developer dying mid-story with the provider's own `success` on the record. Measured on a live
|
|
32
|
+
field turn (#298's thread): `status: "allowed_warning"` at `utilization: 0.92`, then `0.94`,
|
|
33
|
+
arriving BEFORE the limit bit, on a turn that then finished normally and cost $2.00 — and
|
|
34
|
+
`resetsAt` is an epoch integer, so a reader has a real deadline and never parses "resets 6pm". The
|
|
35
|
+
frame is now a typed `rate-limit` event, `AgentOutcome` carries the last one a turn streamed, and
|
|
36
|
+
a Build stage that sees a status the provider itself does not call `allowed` starts NO further
|
|
37
|
+
story: the stories already running finish and settle, the next one is not started, and the run
|
|
38
|
+
says so with the provider's own words on stdout and an `agent.rate_limited` event carrying the
|
|
39
|
+
status, the window, the utilization and the reset instant — written once per stage whether or not
|
|
40
|
+
a story was left to withhold, because a one-story wave that warns is a warning the operator still
|
|
41
|
+
acts on, and carried as the REASON on every story the handoff lists as not started, where the
|
|
42
|
+
audit record would otherwise say this stage had no reason for it. The park test is the provider's WORD,
|
|
43
|
+
never a utilization threshold this repo picked — the CLI already owns the judgement of when a
|
|
44
|
+
window has surpassed its threshold, and a second opinion here would be a second implementation of
|
|
45
|
+
it. Nothing waits and nothing retries: waiting to a reset the provider stated, and classifying the
|
|
46
|
+
DEATH itself, need a terminal capture nobody has obtained (measured negative, twice) and are filed
|
|
47
|
+
as the other half. Absent stays absent — a frame that states no utilization records
|
|
48
|
+
`utilization_absent: "not recorded — …"` rather than a `0` that would read as an empty window, and
|
|
49
|
+
a Codex turn carries no frame at all rather than a fabricated `allowed`, because nothing here has
|
|
50
|
+
measured what `codex exec --json` says about a quota.
|
|
51
|
+
|
|
52
|
+
- **`tldrx ship` refuses an epic carrying a merged diff NOBODY judged, instead of opening a PR over
|
|
53
|
+
it (#311).** #282 closed the case where a reviewer read a story's diff and said `changes` — a
|
|
54
|
+
rejection that stands, with the rejected code on the epic branch. It left the sibling open: three
|
|
55
|
+
settle paths park a story at `review` with `merged: true` beside a verdict that is not an opinion
|
|
56
|
+
about anything. The reviewer was refused for want of money and never spawned (#289) or the run was
|
|
57
|
+
cancelled before the review (#305), both recorded `n-a`; or the reviewer died mid-read, recorded
|
|
58
|
+
`error`. The run's own ledger already said what that class means — "`n-a` and `error` mean nothing
|
|
59
|
+
did [judge it]" — and `ship` asked it nothing: with one story `done` beside the parked one, #210's
|
|
60
|
+
refusal does not fire, #282's predicate does not match, and the PR opens with an unread diff in it
|
|
61
|
+
while the body lists that story under `## Not done` without one word that its code is in the diff.
|
|
62
|
+
Under `--ship merge` that body is read by nobody before auto-merge arms. It is now a refusal of its
|
|
63
|
+
own (exit 2, the family #282 uses: what is missing is a gate, and one of the three causes is
|
|
64
|
+
literally the money refusal), reading the SAME predicate the as-is path reads — `reviewNeverCompleted`,
|
|
65
|
+
not a second list of verdicts to fall out of step. It is a separate line rather than a widening of
|
|
66
|
+
#282's, because the REMEDY differs and a refusal that names the wrong verb sends the operator to the
|
|
67
|
+
wrong place: a rejection is a story to reopen and build again, while an unjudged merge is a review
|
|
68
|
+
still owed, and `tldrx next` settles a story parked at `review` by re-running the review with no
|
|
69
|
+
developer spawned. The line says which of the two non-verdicts it was, never just "unjudged": never
|
|
70
|
+
spawned and died mid-read are different problems with different next steps.
|
|
71
|
+
|
|
72
|
+
- **`tldrx init` probes a repo's four commands at once, and says which one it is waiting on
|
|
73
|
+
(#180).** MEASURED on `6fd2af2` with four 500 ms probes: they started at 0, 501, 1003 and
|
|
74
|
+
1504 ms and `probeCommands` returned after 2006 ms — the SUM of the four deadlines, not the
|
|
75
|
+
longest. The same shape at the real `PROBE_TIMEOUT_MS` is 4 × 120 s = eight minutes for ONE
|
|
76
|
+
repo whose toolchain hangs, multiplied by the repo count, and for all of it detection had
|
|
77
|
+
nothing to say: `repoStart` fires once per repo and the next line is the repo's summary.
|
|
78
|
+
Nothing about a probe ever wanted the one before it — each has its own argv, races its OWN
|
|
79
|
+
deadline and writes its OWN key — so the four now run together, which puts the same fixture at
|
|
80
|
+
502 ms (4.0×) and bounds one repo at one deadline. Bounding the wait was only half of it: two
|
|
81
|
+
minutes of a live view with nothing on it still reads as hung, so a probe that starts a process
|
|
82
|
+
announces itself and its outcome, `init` names the repo and the slots still in flight, and the
|
|
83
|
+
plain-mode heartbeat — the CI-log line, where there is no spinner to prove anything is alive —
|
|
84
|
+
carries that detail instead of only a clock. The rows are still assembled in slot order, not in
|
|
85
|
+
the order the probes happened to finish: `workspace.yml` is a file people diff, and a key order
|
|
86
|
+
that depended on which build was slower today would be a diff that means nothing. A slot decided
|
|
87
|
+
without starting anything — `run`, a command needing a shell, an absent one, a `--no-probe` skip
|
|
88
|
+
— announces nothing, because it costs no wait to report.
|
|
89
|
+
- **A Plan refused for a formatting slip gets ONE bounded repair turn instead of a full re-plan
|
|
90
|
+
(#288).** MEASURED on a live unattended run (0.18.3, `run auto --until-done`): the planner wrote
|
|
91
|
+
`acceptance: [ … ], test_plan: [ … ]` on one line — a flow-mapping comma inside a block mapping —
|
|
92
|
+
in three of three stories. The `plan` check refused it correctly, naming the file, the line and
|
|
93
|
+
the column; the stage then failed, because on a failed stage `tldrx next` IS a fresh stage. A
|
|
94
|
+
$2.19 Plan turn and one of the five `--until-done` relaunches were spent re-deriving a plan whose
|
|
95
|
+
only defect the checker had already localised, and the second planner passed only because it
|
|
96
|
+
happened to put one key per line. Build has always had the shape this needed — a verdict, then a
|
|
97
|
+
second turn on the same branch — and Plan had nothing between "refused" and "plan again". Now a
|
|
98
|
+
`plan` refusal whose every issue names a file that EXISTS re-spawns the planner ONCE over those
|
|
99
|
+
files, with the refusal VERBATIM and the same generated `## Output schemas` contract, and re-runs
|
|
100
|
+
the checks; a second refusal fails the stage exactly as before, in the same exit family, naming
|
|
101
|
+
both. Every bound is deliberate: the class is the CHECK's own answer computed from its issues and
|
|
102
|
+
not from the wording of its message (a refusal with nothing on disk to edit — "the Plan wrote no
|
|
103
|
+
stories" — earns no round, because a repair turn there is a re-plan wearing a fix round's
|
|
104
|
+
clothes); one round per planner turn, read off the `role: plan-fix` row rather than a counter
|
|
105
|
+
that could drift; headless only, since a `--prepare`/`--commit` cycle is a person for whom the
|
|
106
|
+
refusal is already the fix list, and it says so rather than doing nothing silently; and the money
|
|
107
|
+
goes through `wouldExceed`, the same predicate the budget gate decides on, at the same `agentCap`
|
|
108
|
+
the planner's own turn got — a phase that cannot fund the repair is told so and nothing is spent.
|
|
109
|
+
The turn is recorded like any other (a task row, an `agent.result`), with an additive
|
|
110
|
+
`plan.fix_round` event beside them saying why it happened and naming the row that holds the
|
|
111
|
+
dollars — one figure in one place. The Plan prompt also now states the rule the field run broke:
|
|
112
|
+
one key per line, never two joined by a comma.
|
|
113
|
+
|
|
114
|
+
- **Five more stack-pack reviewer Checks asked the read-only reviewer to do something it holds no
|
|
115
|
+
tool for, and one of them had no answer waiting for it anywhere (#195, #182's sibling).** #182
|
|
116
|
+
moved the can-it-fail Check to the developer because the mutation it asked for needs a write and
|
|
117
|
+
a test run, and the reviewer's whole allowance is `Read`, `Grep`, `Glob`, `Bash(git diff *)` — but
|
|
118
|
+
four more Checks, one per language pack, carried the identical contradiction: "run the typecheck
|
|
119
|
+
and lint commands declared in `.tldrx/workspace.yml`" sits sixty-odd lines above the SAME rendered
|
|
120
|
+
prompt's "Do not re-run them. They passed; that is why you are being asked," which follows the
|
|
121
|
+
`## Definition of Done — already re-run by the facilitator` section that already hands the
|
|
122
|
+
reviewer every declared command's exit code. Unlike #182's mutation, this one is not a producer's
|
|
123
|
+
obligation moved to whoever can perform it — the answer was already ON THE PAGE, so the four
|
|
124
|
+
Checks now point the reviewer at the Definition of Done section instead of asking it to run
|
|
125
|
+
anything a second time. The `postgres-testcontainers` overlay's ordering Check ("run the new tests
|
|
126
|
+
alone, then again … and compare") is a different case: nothing re-runs it at the Definition of
|
|
127
|
+
Done, so owner decision (Slack) gives it #182's shape instead — the overlay's own Defaults section
|
|
128
|
+
now asks the developer to run each new database test alone and with its neighbours and record the
|
|
129
|
+
result beside the test, and the Check asks the reviewer only whether that record is there.
|
|
130
|
+
|
|
131
|
+
- **A stage death no longer recommends the retry the budget is about to refuse (#232, remaining
|
|
132
|
+
half).** Every failed stage ended with the same literal — *"cost is recorded, not refunded —
|
|
133
|
+
retry with `tldrx next`"* — printed whether or not the phase could fund that retry. On a phase
|
|
134
|
+
sized to hold exactly one attempt of its stage (what `run new` wrote before #170, and what every
|
|
135
|
+
run created before 2026-09-09 keeps for life) `tldrx next` comes straight back as exit 2, so the
|
|
136
|
+
advice sent the operator to buy the refusal themselves: measured twice in one evening on the run
|
|
137
|
+
#232 was filed from, and once more on a second workspace where the starved phase was the last
|
|
138
|
+
one. The other half of #232 shipped in `efb4eee` — `budget show`'s `next` column says `NO-RETRY`
|
|
139
|
+
before a cent is spent — and this is the same fact said where the operator actually is when it
|
|
140
|
+
bites: the line now names the refusal, both figures, the shortfall and the `budget raise` that
|
|
141
|
+
clears it. It predicts the GATE and not the budget's health, using the gate's OWN inputs — what
|
|
142
|
+
the phase has left, the same remaining-work estimate the brake compares it against, the same
|
|
143
|
+
`planRebalance`, and this invocation's own `--rebalance-finished` state — deliberately: #232's
|
|
144
|
+
own comments measured how badly a derived estimate reads on a partly unmetered run, and a second
|
|
145
|
+
opinion computed here would disagree with the refusal the operator then hits. **That last term is
|
|
146
|
+
the one a first version of this fix got wrong, and a reviewer caught it before it merged**: the
|
|
147
|
+
rebalance is ON by default under `run auto` (#330), so a shortfall that finished phases can cover
|
|
148
|
+
is moved out of them before anything is refused, and the advice was telling the operator to raise
|
|
149
|
+
a ceiling on a run whose very next relaunch would have funded the retry by itself. `tldrx next`
|
|
150
|
+
alone never rebalances, so the same starved phase is refused through one door and funded through
|
|
151
|
+
the other; the advice now names which door it is standing in. Under a rebalancing launch whose
|
|
152
|
+
donors cover the shortfall it says the retry funds itself, names the donor phase and the move,
|
|
153
|
+
and says that a bare `tldrx next` is not that door — it stops short of a promise, because the
|
|
154
|
+
grant check on each move happens after this line is written. When no finished phase can cover it,
|
|
155
|
+
the refusal prediction stands through either door, with how short it still is *with all of it*.
|
|
156
|
+
Where this gate does not decide the retry at all — `on_exceed: warn` refuses nothing,
|
|
157
|
+
a `host-tokens` phase is a category error the dollar brake must never judge, an `attended_by:
|
|
158
|
+
host` run is allowed past it by policy — the plain line is printed unchanged: those are "not this
|
|
159
|
+
gate's call", which is not the same as "affordable", and claiming a refusal there would be the
|
|
160
|
+
invented value the house rules forbid. The failure's `signature` (#297) is untouched and is still
|
|
161
|
+
the failure's own sentence, never this line.
|
|
162
|
+
|
|
163
|
+
- **A Build refusal built from a CACHED base reading says so, and a relaunch re-measures the red
|
|
164
|
+
instead of being served it (#339).** MEASURED on a live `run auto --until-done`, 2026-09-15: two
|
|
165
|
+
Definition-of-Done commands exited 127 on the untouched base tree, the framework refused, the
|
|
166
|
+
operator installed the two missing binaries and confirmed all four commands green by hand — and
|
|
167
|
+
the next attempt printed the SAME refusal, byte for byte, over a base tree that was by then
|
|
168
|
+
repaired. Then the supervisor stopped the loop on it: "a refusal that repeats verbatim is not one
|
|
169
|
+
a relaunch moves", with four relaunches unspent and the run ended early on a workspace where the
|
|
170
|
+
thing complained about was fixed. Two halves compose into that. A cached refusal reproduces
|
|
171
|
+
itself exactly — which is correct for a cache and is the one condition the repeat guard reads —
|
|
172
|
+
and nothing anywhere said the second reading had not been taken. So: a refusal whose evidence was
|
|
173
|
+
re-used now names it as re-used, says when it was measured, against which base sha, and when it
|
|
174
|
+
stops being trusted, and it adds the sentence the generic advice cannot carry — that an edit to
|
|
175
|
+
`.tldrx/workspace.yml` or a base that moves clears a cached reading, but a repair the cache
|
|
176
|
+
cannot see (a binary installed, a service started) is not re-measured until that expiry. The
|
|
177
|
+
supervisor's repeat guard now reads a FRESHNESS field the producer sets rather than diffing text:
|
|
178
|
+
a repeat whose evidence was measured stops the loop exactly as it did, a repeat that was re-used
|
|
179
|
+
is relaunched and says why. And the relaunch is no longer served the same cache — an attempt that
|
|
180
|
+
follows a refusal re-probes a RED base unconditionally, the way `--prepare` already did, because
|
|
181
|
+
the attempt before it asked for exactly this repair. Greens are still re-used, so the cost of
|
|
182
|
+
that is bounded by the commands that are actually broken. The 30-minute red TTL is untouched;
|
|
183
|
+
it was never what saved this run, and the reason it was not is filed separately (#340).
|
|
184
|
+
|
|
185
|
+
- **A red Definition-of-Done command is measured TWICE before it pins a story `blocked`, and the
|
|
186
|
+
record carries both exit codes (#163, sub-fix 1).** MEASURED on a .NET workspace, 2026-09-05: a
|
|
187
|
+
story's DoD gate returned `dotnet test` → exit 2, so the story blocked; the operator then ran the
|
|
188
|
+
identical suite twice — in the story worktree and on the epic branch — and got exit 0 with 2860
|
|
189
|
+
tests and 0 failing. The red was contention over a container runtime, and it had consumed the
|
|
190
|
+
story's last attempt. `blocked` is terminal in-run, so a human had to reopen the story by hand.
|
|
191
|
+
The hole was not the terminality, which is a defensible rule: it was that a story's red was never
|
|
192
|
+
re-measured, so a flake and a defect were written down identically and nobody reading the block
|
|
193
|
+
could tell which one they had. The framework already did this one level up — the base-tree
|
|
194
|
+
pre-flight re-measures a command the cache cannot answer — and the story's own red had no
|
|
195
|
+
equivalent. It does now: when a command goes red on a story, the identical command runs once more
|
|
196
|
+
in the same tree, and the `check.failed` event, the story's `dod` row and the blocked reason all
|
|
197
|
+
carry what the second run said. **Nothing about terminality changes** — a red that reproduces
|
|
198
|
+
blocks exactly as before, and a red that does NOT reproduce also still blocks; it is now blocked
|
|
199
|
+
with a sentence that says the command passed the second time, which is the difference between
|
|
200
|
+
reopening the story and going looking for a bug that is not there. When no second run could be
|
|
201
|
+
taken the record says so with the reason and no number (§7): a command whose binary the tree
|
|
202
|
+
never had would only measure the same absence again, and a refused command never ran, so it has
|
|
203
|
+
no first reading to reproduce. **A red that TIMED OUT is re-run too, so a timing-out
|
|
204
|
+
Definition-of-Done command now costs up to two timeout periods instead of one.** That is stated
|
|
205
|
+
rather than buried: the owner approved one extra run of the suite, and doubling a timeout is a
|
|
206
|
+
fair reading of it but was not in the framing. Timeouts are deliberately not carved out — the
|
|
207
|
+
measured case was contention over a container runtime, and contention is exactly what makes a
|
|
208
|
+
suite hang rather than fail cleanly, so a carve-out would leave the likeliest flake the one thing
|
|
209
|
+
never measured. The blocked reason names both readings and marks either of them `(timed out)`,
|
|
210
|
+
so the record says which kind of red was paid for twice. Owner decision, answered on Slack:
|
|
211
|
+
re-run the red command once and record both exit codes. One reading per spawn, shared with the base-tree path, so the two cannot
|
|
212
|
+
disagree about what a timeout's exit code is or which line of a red suite is the summary.
|
|
213
|
+
|
|
214
|
+
- **A fix-list sha that is too LONG is refused by name, instead of reading as no sha at all
|
|
215
|
+
(#163, sub-fix 3).** The abbreviation edge of `Resolved: yes <sha>` was closed in 0.10.0 — a
|
|
216
|
+
7-39 hex claim that verifies is rewritten to the 40 `rev-parse` returned — and the same
|
|
217
|
+
grammar, `\b([0-9a-f]{7,40})\b`, was failing silently at the other edge, which is the
|
|
218
|
+
dangerous direction §7 names. No position inside a 41-character hex run is a word boundary, so
|
|
219
|
+
an over-long token produced NO match: the line read as a bare `Resolved: yes` that named
|
|
220
|
+
nothing, and the shape fault was never stated. Measured while fixing it, and worse than the
|
|
221
|
+
issue reported: the scan did not stop there but carried on to the next hex word on the line, so
|
|
222
|
+
`Resolved: yes <41 hex> (see 9f2c1ab)` closed the finding over `9f2c1ab` — a DIFFERENT commit
|
|
223
|
+
from the one the line claims, which is an audit record inventing its own evidence. A token of
|
|
224
|
+
41+ hex characters is now refused by name and by count, through the `claimed-unverified`
|
|
225
|
+
sentence #130 already writes into the file, so the finding keeps holding the story and the
|
|
226
|
+
record says why; a refusal anywhere on the line refuses the whole read, so the answer cannot
|
|
227
|
+
depend on which side of the bad token a good one happens to sit. 7-39 is still accepted and
|
|
228
|
+
still canonicalised — nothing a person legitimately types is refused. The grammar now lives in
|
|
229
|
+
one leaf, `readResolvedSha`, read by both the parser and the rewriter, which is what makes the
|
|
230
|
+
token that gets verified and the token that gets replaced provably the same span. The refusal is
|
|
231
|
+
written once and read everywhere: `verifyResolutions` is the single site that withdraws a claim,
|
|
232
|
+
so the file, the story's blocked reason and the stdout report all carry the SAME sentence. They
|
|
233
|
+
did not before — the file would have named the 41-character token while the report a human reads
|
|
234
|
+
first said `named no commit to point at`, which is two records of one event disagreeing.
|
|
235
|
+
|
|
236
|
+
- **`budget show` says a phase cannot afford its own retry BEFORE the money is spent, instead of
|
|
237
|
+
labelling it `ok` up to the refusal (closes #232).** A phase whose ceiling equals ONE attempt of
|
|
238
|
+
its stage refuses every retry after the first cent — by arithmetic, not by policy — and the
|
|
239
|
+
framework's own remedy for a failed stage ("cost is recorded, not refunded — retry with `tldrx
|
|
240
|
+
next`") is exactly what it then refuses. Measured twice in one run, and on a second workspace in
|
|
241
|
+
the LAST phase, where every dollar has already gone. `run new` has sized phases at `attempts ×`
|
|
242
|
+
their stage since #170 so it can no longer be created, and #330's rebalance reaches it only when
|
|
243
|
+
some FINISHED phase has slack to give — with none, the run stops at exit 2, the one exit nothing
|
|
244
|
+
unattended can leave. The `next` column now answers the SIZE question in its own word: `NO-RETRY`
|
|
245
|
+
with the exact raise, and `n/e` for a phase there is nothing to size in. The verdict reads the
|
|
246
|
+
stage's DECLARED `budget_usd` and its own `attempts:`, never the derived estimate beside it —
|
|
247
|
+
#232's own comments measured why: on a partly unmetered run the estimate is too small, so
|
|
248
|
+
`ceiling / est.` reports headroom that is not there, and the less a run is metered the safer that
|
|
249
|
+
ratio claims it is. A declaration does not move with metering. `attempts: 1` declares no retry, so
|
|
250
|
+
one attempt's worth is the right size for it and still reads `ok`. Nothing about the refusal moved:
|
|
251
|
+
`blocked`, `remaining` and `est.` are untouched and `tldrx next` still runs. One derivation
|
|
252
|
+
(`retrySizing`, `budget/wouldExceed.ts`), read back additively by `budget show --json`.
|
|
253
|
+
|
|
254
|
+
- **A killed headless turn reads as `interrupted` and is handed a command the next line of code
|
|
255
|
+
accepts, instead of being called a `--prepare` bundle that never existed (closes #246, half
|
|
256
|
+
one).** MEASURED on a 0.16.1 field run: the `tldrx` process driving `run auto` died
|
|
257
|
+
SIGKILL-class (no handler ran), leaving the stage `running`, a dead pid in the `.lock`, and
|
|
258
|
+
`.agent/<stage>/pending.json` on disk. `run status` said "a `--prepare` bundle is waiting — run
|
|
259
|
+
the prompt and `tldrx next --commit <id>`"; that command was then REFUSED ("what is `ready`, not
|
|
260
|
+
`running`"), so the only advice the screen gave was a dead end, and nobody was going to run a
|
|
261
|
+
prompt by hand on an unattended run anyway. The mechanism is that the two states are
|
|
262
|
+
indistinguishable on disk: `runNext` writes the bundle BEFORE it branches on the mode, so every
|
|
263
|
+
headless spawn leaves exactly what a `--prepare` leaves, and `waiting.ts` derived `prepared`
|
|
264
|
+
from stage `running` + dead lock + a bundle without ever reading which mode opened the turn. The
|
|
265
|
+
ledger already knew — `stage.started` has carried `mode` since it was first written — so the
|
|
266
|
+
answer is now read off the newest `stage.started` for that stage
|
|
267
|
+
(`src/core/run/lastStart.ts`, one implementation, imported by both readers that had made this
|
|
268
|
+
identification independently). A tenth waiting kind, `interrupted`, says what happened and
|
|
269
|
+
prescribes `tldrx run auto <id>` — the command that actually resumes the run; `prepared` is
|
|
270
|
+
reserved for a bundle a `--prepare` really wrote. `Ctrl-C` shared the bug and is fixed with it:
|
|
271
|
+
`stopInFlightRun` preserved any `killed === 0` stage with a bundle as `running` forever, and now
|
|
272
|
+
preserves only one whose last start was a `prepare`. A ledger that does not say keeps the old
|
|
273
|
+
reading exactly — an absent record is not evidence of a spawn. The second half of #246, the
|
|
274
|
+
orphaned sub-agent's dollars being recorded nowhere while `spent_usd` reads a confident `0.00`,
|
|
275
|
+
is untouched and stays open.
|
|
276
|
+
|
|
277
|
+
- **A Watch retry no longer re-buys a watcher card that already validated (closes #306).** The
|
|
278
|
+
Watch stage fails on the FIRST card that does not validate, and `tldrx next` / `run auto
|
|
279
|
+
--retry-failed` re-enter the executor from the top — so a run with N features where feature k
|
|
280
|
+
was refused spawned all N writers again, including the k-1 cards that had already validated and
|
|
281
|
+
been stamped. Each of those paid at least the `$0.25` cold-spawn floor for a card nothing had
|
|
282
|
+
refused, and on a two-feature field run (#301, run B) with a different card refused in each
|
|
283
|
+
attempt, every writer was bought at least twice. #301 made the second spend an edit rather than
|
|
284
|
+
a rewrite; this makes it zero. The headless path now takes one snapshot of disk as the stage is
|
|
285
|
+
entered, before anything spawns, and keeps a feature whose card passes the SAME
|
|
286
|
+
`parseWatcherCard` against the SAME source context the stage validates with — so the skip can
|
|
287
|
+
never hide a bad card, only decline to save money, and a card whose citations went stale in the
|
|
288
|
+
meantime is rewritten like any other. It is a snapshot rather than a per-feature read so that
|
|
289
|
+
one writer loose in the run directory can never decide whether the next feature is written at
|
|
290
|
+
all. A kept feature still gets its `tasks[]` row, with `cost_usd: 0.0`, `model: null` and
|
|
291
|
+
`session_id: null` — a measured zero, not the `metered: false` of a turn billed to a host
|
|
292
|
+
session — and the stage's report names every kept card, so a `$0.00` row is never read as a
|
|
293
|
+
writer that silently did nothing. `--prepare`/`--commit` is untouched.
|
|
294
|
+
|
|
295
|
+
- **The epic branch Build cuts is claimed in `run.yml` at the cut, not when the stage returns
|
|
296
|
+
(closes #262).** Reported from a field workspace: a `run auto` loop was killed mid-Build,
|
|
297
|
+
relaunched, and was refused its OWN epic — "`epic/<slug>` already exists … refusing to stack
|
|
298
|
+
this run's commits onto someone else's epic" — with `tldrx next --reuse-epic` the only way back
|
|
299
|
+
in, a flag `run auto` cannot pass. Same sentence as #248, different mechanism, and #248's fix
|
|
300
|
+
cannot reach this one: #248 closed the THROW path, where a process is still alive to run its
|
|
301
|
+
catch. Here the process is GONE. `ExecutorOutcome.epicBranches` merges into `build.epic_branch`
|
|
302
|
+
only after the executor RETURNS, and a Build stage does not return for as long as a developer
|
|
303
|
+
turn takes — so a SIGKILL, an OOM kill or a cut session anywhere in that window left
|
|
304
|
+
`epic/<slug>` in the repo with nothing in the record claiming it. SIGINT and SIGTERM are hooked
|
|
305
|
+
(`src/cli/signals.ts`); SIGKILL cannot be hooked by any process, so no handler was ever going to
|
|
306
|
+
cover this. The cut now records the claim itself, through the `ctx.claimEpicBranch` seam, once
|
|
307
|
+
per branch rather than once per story. It does NOT ride the write serializer and is not claimed
|
|
308
|
+
to: of `openStory`'s eight call sites, four wrap it in `this.writes.run` and four
|
|
309
|
+
(`prepare`, `prepareReview`, `commitReview`, `commit`) call it bare. What makes it safe is that
|
|
310
|
+
there is no `await` between the branch being cut and the claim, and the claim is synchronous —
|
|
311
|
+
so on every one of those paths the cut and its record are one uninterrupted step. The guard is
|
|
312
|
+
NOT widened: a branch this run did not cut is refused exactly as
|
|
313
|
+
before — the claim stays a RECORD, and the fix is to write the record when it is earned rather
|
|
314
|
+
than to teach the guard to accept inferred evidence. A `run.yml` save that cannot take the
|
|
315
|
+
workspace lock is NOT swallowed: it fails the stage naming the branch, the repo, why the write
|
|
316
|
+
failed and both ways back in, because a claim that could not be written is this defect happening
|
|
317
|
+
again in silence. Honest about how far this goes: the save is atomic against a KILLED PROCESS
|
|
318
|
+
(temp file, then `rename`), and it is not `fsync`ed — a power cut can still lose a write the
|
|
319
|
+
kernel had not flushed. It is pinned by a real SIGKILL of a real `tldrx next`, never a simulated
|
|
320
|
+
throw, which would only have re-tested #248.
|
|
321
|
+
|
|
322
|
+
- **A Codex Build review no longer dies before the reviewer runs, and the provider's own refusal
|
|
323
|
+
arrives on one line (#148).** MEASURED on a live Build smoke run, published 0.7.0 +
|
|
324
|
+
codex-cli 0.153.0: the developer stage finished and 4 tests passed, then the review failed at
|
|
325
|
+
the API with `invalid_json_schema: fixlist.items.required must include every property (missing
|
|
326
|
+
n)`. Codex's structured-output API requires every DECLARED property to be listed as required;
|
|
327
|
+
`REVIEW_SCHEMA` declares optional fields at two levels — `fixlist` itself, and `n`, `severity`,
|
|
328
|
+
`where`, `detail`, `do_not` inside each item — and the spawn wrote that object to Codex's
|
|
329
|
+
`--output-schema` file verbatim. Every Codex review was refused before it began, so the story
|
|
330
|
+
paid an attempt for a turn the provider never ran. The translation happens at the Codex spawn
|
|
331
|
+
boundary ALONE (`codexSchema`, one implementation, §7): an optional property becomes
|
|
332
|
+
`anyOf: [<its schema>, {"type": "null"}]` and joins `required`, recursively, and the review
|
|
333
|
+
parser already reads a `null` exactly as it read the absent field — so nothing about
|
|
334
|
+
fail-closed moves, and a `fixlist` verdict with nothing readable in it is still `changes`.
|
|
335
|
+
`REVIEW_SCHEMA` itself is untouched and Claude's `--json-schema` still carries it byte for
|
|
336
|
+
byte; the test asserts both directions, because a fix at a provider seam that quietly edits the
|
|
337
|
+
shared contract is the failure this one was supposed to avoid. Second half, same issue: Codex
|
|
338
|
+
names its refusal in a pretty-printed block, and that sentence is quoted into a handoff where
|
|
339
|
+
every line must carry its own `[src: …]` — a multi-line reason became uncited continuation
|
|
340
|
+
lines that failed the claim-sources check. The Codex reason is now collapsed onto one line
|
|
341
|
+
rather than cut at the first, because the first line of that block is `{` and the WHY is two
|
|
342
|
+
lines down. Claude's failure text is passed through unchanged. The acceptance is MEASURED,
|
|
343
|
+
not argued from the error message: the pre-merge reviewer ran one real
|
|
344
|
+
`codex exec -s read-only --output-schema <the translated schema>` in an isolated directory
|
|
345
|
+
against an authenticated codex-cli 0.153.0 — the issue's own version — and it exited 0 with
|
|
346
|
+
no `invalid_json_schema` and the structured output `{"verdict": "approve", "summary":
|
|
347
|
+
"Approved with no findings.", "findings": [], "fixlist": null}`. That `"fixlist": null` is
|
|
348
|
+
the translation's whole point arriving back off the wire: the field the API refused to leave
|
|
349
|
+
optional comes home as the null this parser already reads as absent. Two readings, taken by
|
|
350
|
+
different hands — the schema bytes the child was handed, asserted in the test on every run,
|
|
351
|
+
and that one live call, which is not re-run by any gate.
|
|
352
|
+
|
|
353
|
+
- **A still-open fix-list finding is re-checked against the EPIC tip before the Build gate, so a
|
|
354
|
+
defect a LATER story closed says so with the closing sha instead of reading `Resolved: no`
|
|
355
|
+
(#163, sub-fix 2).** MEASURED on a Next.js workspace, 2026-09-05: at a Build gate all 6
|
|
356
|
+
`fix-now` findings carried a resolving sha and the 3 `defer-with-log` entries read
|
|
357
|
+
`Resolved: no` — "correct PER STORY", in the host agent's own words — but two of those three
|
|
358
|
+
defects had in fact been closed later in the same run by a DIFFERENT story, which the host
|
|
359
|
+
verified by reading the code. Exactly one finding had genuinely shipped unfixed, the record
|
|
360
|
+
could not tell those apart, and so a human narrated the difference by hand at the gate. The hole
|
|
361
|
+
was structural rather than careless: verification asks whether a claim's sha is reachable from
|
|
362
|
+
the STORY's own branch, and it asks at the moment that story settles — a fix that lands later,
|
|
363
|
+
on somebody else's branch, and reaches the epic through a merge did not exist when the question
|
|
364
|
+
was asked and is not on the branch it was asked about. There was no third answer for it, so it
|
|
365
|
+
was recorded as the second. There is one now. After the last story settles and before the
|
|
366
|
+
handoff is written, every finding not already closed with evidence is re-checked against its
|
|
367
|
+
epic tip, and a sha that is reachable from the epic and NOT from the story's own branch is
|
|
368
|
+
recorded as what it is. **The two kinds of close are spelled apart, never flattened**:
|
|
369
|
+
`Resolved: yes <sha>` stays the story's own close, and a close a later story landed is
|
|
370
|
+
`Resolved: yes-on-epic <sha>` — a different word because it is a different fact about who fixed
|
|
371
|
+
it and where the fix lives. `yes-on-epic` is not `yes`, so nothing the sweep writes changes what
|
|
372
|
+
any gate decides; the finding is exactly as open afterwards as it was before, and a finding that
|
|
373
|
+
genuinely shipped unfixed still reads unfixed. **Nothing is ever re-marked on inference.** A
|
|
374
|
+
reachable commit the record already names is the only thing that closes anything here — never a
|
|
375
|
+
changed file, never a heading that resembles another story's work — so the sweep cannot invent
|
|
376
|
+
the one direction §7 refuses to be wrong in. **And it does not go quiet when it finds nothing.**
|
|
377
|
+
Every finding it examined gets a `Swept:` line saying what was measured and against which tip,
|
|
378
|
+
because a `Resolved: no` that has been re-checked against the whole run and one that only ever
|
|
379
|
+
meant "correct per story" were indistinguishable, and that indistinguishability is the defect.
|
|
380
|
+
A sweep that could NOT be taken — no epic branch recorded, a tip git will not resolve, a file
|
|
381
|
+
that cannot be re-read — names its reason on every finding and in the handoff's `## Unknowns`,
|
|
382
|
+
rather than reporting a clean sweep over a measurement nobody took. **And it asks the weaker
|
|
383
|
+
question too, in words that say it is weaker.** The three entries that Next.js gate got wrong
|
|
384
|
+
named no commit at all — nobody had claimed one — so re-checking shas the record already names
|
|
385
|
+
has nothing to say about exactly the findings a human had to read the code for. For those, the
|
|
386
|
+
sweep asks what that human asked first: did a commit on the EPIC, and not on this story's own
|
|
387
|
+
branch, CHANGE the file this finding cites? Up to three such commits come back named, on the
|
|
388
|
+
`Swept:` line and as their own `## Unknowns` bullet — and they resolve NOTHING. A changed file
|
|
389
|
+
is not a closed defect, and writing `Resolved: yes` off one would be the same lie arrived at
|
|
390
|
+
from the other side; what this replaces is a person grepping the run's commits by hand at the
|
|
391
|
+
gate, never a person's judgement about them. **A probe git REFUSED says that, too.** An
|
|
392
|
+
unresolvable story ref and a file nobody changed both come back as no commits, and writing
|
|
393
|
+
"no later commit changed this" over the first would be a measurement nobody took wearing the
|
|
394
|
+
words of one that was — so the refusal carries git's own reason onto the `Swept:` line and
|
|
395
|
+
into `## Unknowns`, and is never counted as a clean probe. `Swept:` and the
|
|
396
|
+
`yes-on-epic` word are additive: a fix list written before this change reads exactly as it did.
|
|
397
|
+
|
|
398
|
+
### Changed
|
|
399
|
+
|
|
400
|
+
- **The zero-touch launch recipe (EN and ES) now pairs `run auto` with `--wait-gates` and
|
|
401
|
+
`--wait-answers` everywhere it shows opening a run with `--gates none`, and says why: `--gates
|
|
402
|
+
none` sets the policy, but only `--wait-gates` lets `run auto` re-sign a parked `auto` gate once
|
|
403
|
+
the question that held it is auto-answered — without it the loop exits `awaiting human` on the
|
|
404
|
+
first gate that parks on a question, even though every one of its conditions already holds (see
|
|
405
|
+
#342).**
|
|
406
|
+
|
|
3
407
|
## 0.29.0 — 2026-09-15
|
|
4
408
|
|
|
5
409
|
### Changed
|
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.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). |
|
|
338
339
|
| 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). |
|
|
339
340
|
| 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). |
|
|
340
341
|
| 0.28.0 | 2026-09-15 | `beta` | Four changes that keep an unattended run moving instead of stranding on a person. A story whose merge-up to its epic conflicts in at most three of its own touched files gets one automated conflict turn instead of a person, guarded against committing conflict markers (#286). `story reopen` on a blocked story now releases every dependent it alone was holding, in the same command (#312). Seed's `Recommended:` line and the loop's question parser now share one grammar, so a citation-only recommendation no longer parks a run that `seed check` had already passed (#323). And an `absent:` needle over `facts.yml` no longer trips on a fact's own recording metadata, while an `auto` gate refused only by failed checks gets one automatic re-run before waiting for a person (#231). |
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
conflictOf
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-fbmj1bk6.js";
|
|
5
5
|
import {
|
|
6
6
|
FactsStore,
|
|
7
7
|
formatJaccard
|
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
import {
|
|
14
14
|
EventLog,
|
|
15
15
|
PHASE_ID_RE
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-77x6v0gg.js";
|
|
17
17
|
import {
|
|
18
18
|
PHASE_IDS
|
|
19
19
|
} from "./chunk-g4d2v4jc.js";
|
|
@@ -27,10 +27,10 @@ import {
|
|
|
27
27
|
validateRunBudget,
|
|
28
28
|
wouldExceed,
|
|
29
29
|
wouldExceedHostTokens
|
|
30
|
-
} from "./chunk-
|
|
30
|
+
} from "./chunk-zpch8e2j.js";
|
|
31
31
|
import {
|
|
32
32
|
EventLog
|
|
33
|
-
} from "./chunk-
|
|
33
|
+
} from "./chunk-77x6v0gg.js";
|
|
34
34
|
import {
|
|
35
35
|
cursorStage,
|
|
36
36
|
hostTokensIn,
|
|
@@ -39,6 +39,7 @@ var EVENT_TYPES = [
|
|
|
39
39
|
"task.done",
|
|
40
40
|
"agent.spawned",
|
|
41
41
|
"agent.result",
|
|
42
|
+
"agent.rate_limited",
|
|
42
43
|
"question.asked",
|
|
43
44
|
"question.answered",
|
|
44
45
|
"gate.requested",
|
|
@@ -54,6 +55,7 @@ var EVENT_TYPES = [
|
|
|
54
55
|
"story.review_retried",
|
|
55
56
|
"story.work_rescued",
|
|
56
57
|
"story.touches_widened",
|
|
58
|
+
"plan.fix_round",
|
|
57
59
|
"epic.released",
|
|
58
60
|
"worktree.foreign_work_aside",
|
|
59
61
|
"worktree.foreign_work_restored",
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
spentBasis,
|
|
10
10
|
tallyOf,
|
|
11
11
|
validateRunBudget
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-zpch8e2j.js";
|
|
13
13
|
import {
|
|
14
14
|
EventLog,
|
|
15
15
|
OUTCOME_NOT_RECORDED,
|
|
@@ -22,7 +22,7 @@ import {
|
|
|
22
22
|
isTerminal,
|
|
23
23
|
stageAt,
|
|
24
24
|
validateRunFile
|
|
25
|
-
} from "./chunk-
|
|
25
|
+
} from "./chunk-77x6v0gg.js";
|
|
26
26
|
import {
|
|
27
27
|
cursorStage,
|
|
28
28
|
isAttendedByHostView,
|
|
@@ -881,8 +881,33 @@ function hasPreparedBundle(runDir, stageId) {
|
|
|
881
881
|
return preparedBundles(runDir, stageId).length > 0;
|
|
882
882
|
}
|
|
883
883
|
|
|
884
|
+
// src/core/run/lastStart.ts
|
|
885
|
+
function lastStageStartMode(runDir, stageId) {
|
|
886
|
+
const events = EventLog.forRun(runDir).readAll().events;
|
|
887
|
+
for (let index = events.length - 1;index >= 0; index -= 1) {
|
|
888
|
+
const event = events[index];
|
|
889
|
+
if (event === undefined)
|
|
890
|
+
continue;
|
|
891
|
+
if (event.type !== "stage.started" || event.stage !== stageId)
|
|
892
|
+
continue;
|
|
893
|
+
const mode = event.payload.mode;
|
|
894
|
+
return typeof mode === "string" && mode !== "" ? mode : null;
|
|
895
|
+
}
|
|
896
|
+
return null;
|
|
897
|
+
}
|
|
898
|
+
function startedHeadless(runDir, stageId) {
|
|
899
|
+
return lastStageStartMode(runDir, stageId) === "headless";
|
|
900
|
+
}
|
|
901
|
+
|
|
884
902
|
// src/core/run/waiting.ts
|
|
885
|
-
var MOVABLE_KINDS = [
|
|
903
|
+
var MOVABLE_KINDS = [
|
|
904
|
+
"gate",
|
|
905
|
+
"answer",
|
|
906
|
+
"ready",
|
|
907
|
+
"failed",
|
|
908
|
+
"prepared",
|
|
909
|
+
"interrupted"
|
|
910
|
+
];
|
|
886
911
|
function isMovable(kind) {
|
|
887
912
|
return MOVABLE_KINDS.includes(kind);
|
|
888
913
|
}
|
|
@@ -952,6 +977,13 @@ function waitingFor(run, runDir) {
|
|
|
952
977
|
questions: open
|
|
953
978
|
};
|
|
954
979
|
}
|
|
980
|
+
if (startedHeadless(runDir, entry.stage.id)) {
|
|
981
|
+
return {
|
|
982
|
+
kind: "interrupted",
|
|
983
|
+
message: `${entry.phase.id}/${entry.stage.id} was interrupted — its headless turn's process is gone — ` + `\`tldrx run auto ${basename2(runDir)}\``,
|
|
984
|
+
questions: open
|
|
985
|
+
};
|
|
986
|
+
}
|
|
955
987
|
if (hasPreparedBundle(runDir, entry.stage.id)) {
|
|
956
988
|
return {
|
|
957
989
|
kind: "prepared",
|
|
@@ -743,6 +743,9 @@ var MARKER_SCAN_MAX_BYTES = 4 * 1024 * 1024;
|
|
|
743
743
|
// src/core/build/fixlist.ts
|
|
744
744
|
var MAX_FIXLIST_ROUNDS = STAGE_TUNING_DEFAULTS.fixlistRounds;
|
|
745
745
|
var FINDING_KINDS = ["correctness", "security", "docs", "style"];
|
|
746
|
+
var CLAIMED_UNVERIFIED = "claimed-unverified";
|
|
747
|
+
var CLOSED_ON_EPIC = "yes-on-epic";
|
|
748
|
+
var CLAIMING_VERDICTS = new Set(["yes", CLAIMED_UNVERIFIED, CLOSED_ON_EPIC]);
|
|
746
749
|
|
|
747
750
|
// src/core/build/refusalKind.ts
|
|
748
751
|
var OUTCOME_ALREADY_REPORTED = "the facilitator re-runs the Definition of Done after you and records each command's exit " + "code, so the number the `echo` would print is measured and written down whether you " + "capture it or not";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
questionsCard
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-fbmj1bk6.js";
|
|
5
5
|
import"./chunk-gr83pq7x.js";
|
|
6
6
|
import {
|
|
7
7
|
allow,
|
|
@@ -20,17 +20,17 @@ import {
|
|
|
20
20
|
runSnapshot,
|
|
21
21
|
statusWithOutcome,
|
|
22
22
|
whatIsWaiting
|
|
23
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-przta0a0.js";
|
|
24
24
|
import {
|
|
25
25
|
expertsDir,
|
|
26
26
|
loadExperts,
|
|
27
27
|
pathsIntersect,
|
|
28
28
|
readExpertDomain,
|
|
29
29
|
stackExpertNames
|
|
30
|
-
} from "./chunk-
|
|
30
|
+
} from "./chunk-zpch8e2j.js";
|
|
31
31
|
import {
|
|
32
32
|
isFinished
|
|
33
|
-
} from "./chunk-
|
|
33
|
+
} from "./chunk-77x6v0gg.js";
|
|
34
34
|
import {
|
|
35
35
|
openRunViews
|
|
36
36
|
} from "./chunk-0d4csx4s.js";
|
|
@@ -628,6 +628,8 @@ function summaryOf(run, waiting, blocked) {
|
|
|
628
628
|
return `run ${what} was closed by hand — ${waiting.message}`;
|
|
629
629
|
case "prepared":
|
|
630
630
|
return `run ${what} has a --prepare bundle waiting for the host session to run it`;
|
|
631
|
+
case "interrupted":
|
|
632
|
+
return `run ${what} was interrupted at ${run.cursor.phase}/${run.cursor.stage} and is waiting to be relaunched`;
|
|
631
633
|
case "running":
|
|
632
634
|
return `run ${what} is running ${run.cursor.phase}/${run.cursor.stage} right now`;
|
|
633
635
|
default:
|
|
@@ -643,6 +645,8 @@ function commandFor(run, waiting) {
|
|
|
643
645
|
case "ready":
|
|
644
646
|
case "failed":
|
|
645
647
|
return `tldrx next ${run.run}`;
|
|
648
|
+
case "interrupted":
|
|
649
|
+
return `tldrx run auto ${run.run}`;
|
|
646
650
|
default:
|
|
647
651
|
return "";
|
|
648
652
|
}
|
package/dist/hooks/statusline.js
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
import {
|
|
3
3
|
bar,
|
|
4
4
|
runSnapshot
|
|
5
|
-
} from "./chunk-
|
|
6
|
-
import"./chunk-
|
|
7
|
-
import"./chunk-
|
|
5
|
+
} from "./chunk-przta0a0.js";
|
|
6
|
+
import"./chunk-zpch8e2j.js";
|
|
7
|
+
import"./chunk-77x6v0gg.js";
|
|
8
8
|
import"./chunk-0d4csx4s.js";
|
|
9
9
|
import"./chunk-g4d2v4jc.js";
|
|
10
10
|
import"./chunk-857jhjmb.js";
|