tldr-experts 0.14.0 → 0.14.2
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 +267 -111
- package/README.md +2 -0
- package/dist/hooks/answer-capture.js +12 -8
- package/dist/hooks/budget-gate.js +7 -7
- package/dist/hooks/{chunk-w9r9p155.js → chunk-97zs34wb.js} +5 -5
- package/dist/hooks/{chunk-1knfmj6j.js → chunk-bqmstq21.js} +26 -2
- package/dist/hooks/{chunk-yvgkw0gm.js → chunk-db7wf3hy.js} +1 -1
- package/dist/hooks/{chunk-f1w9sy07.js → chunk-dnat9z30.js} +1 -1
- package/dist/hooks/{chunk-qefqmgjn.js → chunk-dxk7eaqn.js} +1 -1
- package/dist/hooks/chunk-dzkg8c68.js +4 -0
- package/dist/hooks/{chunk-fsgsn8vb.js → chunk-fefnr71h.js} +1 -1
- package/dist/hooks/{chunk-v2xzr4qd.js → chunk-gvz0rzb9.js} +1 -1
- package/dist/hooks/{chunk-nwzevx0j.js → chunk-mzwqb08p.js} +3 -3
- package/dist/hooks/{chunk-cg8v4mz4.js → chunk-r95h9ynf.js} +1 -1
- package/dist/hooks/{chunk-mvz7jwt8.js → chunk-tpz0zb0t.js} +1 -1
- package/dist/hooks/{chunk-3ns54mnq.js → chunk-w8qm1jbn.js} +4 -4
- package/dist/hooks/{chunk-6fywd8xd.js → chunk-wwwmkey4.js} +307 -291
- package/dist/hooks/claim-sources.js +5 -5
- package/dist/hooks/dod-gate.js +5 -5
- package/dist/hooks/no-reask.js +8 -8
- package/dist/hooks/session-start.js +11 -10
- package/dist/hooks/statusline.js +7 -6
- package/dist/tldrx.js +208 -42
- package/package.json +1 -1
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/stages/how/stage.md +3 -0
- package/stages/plan/stage.md +3 -0
- package/stages/what/stage.md +3 -0
- package/templates/questions.md +11 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,10 +1,203 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
3
|
|
|
4
|
+
## 0.14.2 — 2026-09-09
|
|
5
|
+
|
|
6
|
+
### Fixed
|
|
7
|
+
|
|
8
|
+
- **A stage's `claim-sources` check reads the disk it is judging, not a snapshot taken stages
|
|
9
|
+
ago (#206).** `src/core/text/srcToken.ts` memoises the question and fact indexes at MODULE
|
|
10
|
+
scope, keyed by run dir and workspace root, and nothing outside `test/` ever dropped them —
|
|
11
|
+
`clearSrcCaches()` had 20 call sites, all in tests. `tldrx run auto` is one Node process for a
|
|
12
|
+
whole run (`runAuto.ts`, no re-exec per stage), so the FIRST citation resolved anywhere in the
|
|
13
|
+
run froze both indexes at whatever was on disk at that instant, and every later stage's check
|
|
14
|
+
judged its own outputs against that frozen view. Measured live on three real workspaces at
|
|
15
|
+
0.14.1: a `how` stage refused with `no such question Q2 … declared: Q1` over Q2–Q4 it had
|
|
16
|
+
itself written into `02-how/questions.md` minutes earlier, and another refused with
|
|
17
|
+
`it has 145 live fact(s)` over a `facts.yml` that held 148 — the three extra facts written by
|
|
18
|
+
the owner's own answers two seconds BEFORE the stage started. Roughly $11 of paid turns thrown
|
|
19
|
+
away for ids that were real the whole time. The indexes are now refreshed inside
|
|
20
|
+
`toSrcContext` — the one place a citation context is built, and a place every caller reaches
|
|
21
|
+
exactly once per check, gate or hook invocation — so the memoisation that pays for itself (one
|
|
22
|
+
document's forty citations read `facts.yml` once) is kept and the memory that outlives the
|
|
23
|
+
check is not. The git blob memo is deliberately left per-process: its cap bounds forks, not
|
|
24
|
+
staleness. Resolution rules are unchanged; what changed is when the checker last looked.
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
## 0.14.1 — 2026-09-09
|
|
28
|
+
|
|
29
|
+
### Fixed
|
|
30
|
+
|
|
31
|
+
- **A training test no longer runs on the fixed 5000 ms default: the spawner heuristic now
|
|
32
|
+
names the training shape (#194).** `test/machine-load.test.ts` decides which files must take
|
|
33
|
+
the load-aware budget by READING each test file for one of a few literal markers, and a
|
|
34
|
+
training turn matched none of them — it spawns the agent through `spawnAgent.ts`, two imports
|
|
35
|
+
away from any `Bun.spawn` the test file itself writes. Measured on `main` at cd8721a with a
|
|
36
|
+
probe that wraps `Bun.spawn` and `node:child_process` (validated first against a file the
|
|
37
|
+
heuristic already claims, which recorded 87 spawns, so the instrument can see the thing):
|
|
38
|
+
`test/knowledge-value.test.ts` spawned **3** real `claude` children while
|
|
39
|
+
`grep -c setDefaultTimeout` on it returned **0** — bun's fixed 5000 ms, which is the false RED
|
|
40
|
+
#43 was filed about, on the box's clock rather than the assertion's. The list now also names
|
|
41
|
+
`runTraining` and `makeTrainingWorkspace`, which claims two more files (81 → 83) and adds two
|
|
42
|
+
guard rows; `knowledge-value.test.ts` takes `spawnTestTimeout()`. The non-vacuity test is
|
|
43
|
+
anchored on that file as well as on `cli.test.ts`, because deleting a marker from a proxy list
|
|
44
|
+
otherwise shrinks the row set into a SHORTER green instead of a red — a marker list that can
|
|
45
|
+
quietly stop covering something is how this hid in the first place. The list is still a proxy:
|
|
46
|
+
the same probe found 14 more spawning files it does not claim, filed with the per-file counts
|
|
47
|
+
as #201.
|
|
48
|
+
- **The five phase ids are written out in one file now, not three, and a shape test refuses
|
|
49
|
+
the fourth (#187).** Measured on `main` at cd8721a: `PHASE_IDS`
|
|
50
|
+
(`src/core/run/workflowPreset.ts:28`) and `QUESTION_PHASES`
|
|
51
|
+
(`src/core/run/questionCards.ts:52`) were two `as const` array literals with the same five
|
|
52
|
+
ids in the same order, and `grep -rln "QUESTION_PHASES\|PHASE_IDS" test/` exited **1** —
|
|
53
|
+
nothing anywhere pinned them equal. Reading for the shape rather than the two names found a
|
|
54
|
+
THIRD: `questionFiles` in `src/cli/commands/questions.ts:175`, the list `questions lint`
|
|
55
|
+
walks, inline and unnamed — while the doc comment over `QUESTION_PHASES` claimed the list was
|
|
56
|
+
"kept in one place so the two verbs of `tldrx questions` cannot disagree", which was false of
|
|
57
|
+
the lint verb the sentence names. `workflowPreset.ts` owns the list; the other two import it,
|
|
58
|
+
so every existing consumer's import line is untouched and the `as const` tuple type survives
|
|
59
|
+
the indirection (proved with a type probe: the negative case, `99-nope` in slot five, fails
|
|
60
|
+
`tsc` with TS2322). `test/phase-ids-one-derivation.test.ts` pins it from both ends, because
|
|
61
|
+
neither end alone is enough: an identical second copy reddens the shape guard, and a copy
|
|
62
|
+
that has already drifted slips past that pattern and reddens the equality guard instead.
|
|
63
|
+
- **The changelog no longer credits a release with work it did not ship, and a gate now says so
|
|
64
|
+
(#200).** Measured on `main` at 0.14.0: `awk '/^## /{h=$0} /wait-gates/{print h}' CHANGELOG.md`
|
|
65
|
+
put #197's `--wait-gates` and `gate.timeout` bullets under `## 0.13.0 — 2026-09-08`, while
|
|
66
|
+
`git show v0.13.0:CHANGELOG.md | grep -c wait-gates` returned **0** — the tag that section
|
|
67
|
+
names never carried the flag. They were merged in 069a73e, after v0.13.1 was cut, and shipped
|
|
68
|
+
in v0.14.0; the implementer appended them to the FIRST `### Added` in the file instead of the
|
|
69
|
+
unreleased heading, which is the whole mechanism. Two older sections had the same slip:
|
|
70
|
+
0.3.1 carried a528985's four bullets (73 lines, committed 07:06 on 2026-08-31, twenty-six
|
|
71
|
+
minutes after v0.3.1 was tagged, first shipped in v0.4.0) and 0.6.1 had lost a blank line. All three are
|
|
72
|
+
now byte-identical to their tags, and the bullets sit under the release that actually shipped
|
|
73
|
+
them — a released section is **restored**, never edited, and the two corrections that had to
|
|
74
|
+
land inside a tagged section (0.14.0 and 0.4.0) are recorded in `CHANGELOG.amendments` rather
|
|
75
|
+
than smuggled in. The v0.14.0 GitHub release notes were re-published from the corrected
|
|
76
|
+
section and now carry both moved bullets; v0.13.0's were generated at tag time from a changelog
|
|
77
|
+
that did not carry them and are already right, so they are untouched.
|
|
78
|
+
- **`scripts/release-check.sh` refuses a rewritten released section.** For every dated
|
|
79
|
+
`## X.Y.Z — YYYY-MM-DD` heading whose tag is present, the section's text must equal that
|
|
80
|
+
section at `git show vX.Y.Z:CHANGELOG.md` — the one copy nobody can edit afterwards — and a
|
|
81
|
+
mismatch fails by version, quoting the first differing line and the remedy. Nothing checked
|
|
82
|
+
this before, which is why three sections drifted without anyone noticing. It runs in both
|
|
83
|
+
`--pre-push` and `--ci`, and never fails for a reason it cannot judge: a checkout that fetched
|
|
84
|
+
no tags (`publish.yml`'s does not) and a tag whose own section still said `unreleased` (v0.0.2,
|
|
85
|
+
which predates the dating convention) are skipped and COUNTED in one line, because a silent
|
|
86
|
+
skip is how a gate becomes a decoration. A deliberate correction is recorded in
|
|
87
|
+
`CHANGELOG.amendments` (`<version> <source-sha> <why>`) — a second file on purpose, since the
|
|
88
|
+
failure being caught is an append nobody meant and an append edits one file. That listing is
|
|
89
|
+
**not a licence**, which a reviewer proved by injecting an invented bullet into an amended
|
|
90
|
+
section and watching the first version of this check pass it: an amended section must still
|
|
91
|
+
contain the tag's section as an ORDERED SUBSEQUENCE (nothing deleted, nothing reworded), and
|
|
92
|
+
every line it adds must exist verbatim in `<source-sha>:CHANGELOG.md`. Both halves say the same
|
|
93
|
+
thing — an amendment moves text the changelog already carried, and cannot write a new claim
|
|
94
|
+
into a shipped release. A source sha that is not a commit here is refused, not trusted.
|
|
95
|
+
- **An auto gate now says WHY it did not close, and the reason reaches the phone (#203).**
|
|
96
|
+
Measured on a real workspace, 0.14.0, the first `run auto --wait-answers 4h --wait-gates 4h`
|
|
97
|
+
with the notify hook: the `what` stage finished for $1.98, every declared check passed, the
|
|
98
|
+
policy was `auto`, and the owner got *"waiting at a auto gate that did not close by itself —
|
|
99
|
+
a person signs it"*. No reason. The cause was four open questions, and `evaluateAutoGate`
|
|
100
|
+
had computed exactly that sentence — into a stdout line nobody was watching, because
|
|
101
|
+
`gate.requested` was appended a hundred lines BEFORE the verdict existed. The verdict is now
|
|
102
|
+
taken one statement earlier, the event carries `why` and `held_by`, and the notification
|
|
103
|
+
renders them. Both keys are present only for an `auto` policy: on a `human` or `agent` gate
|
|
104
|
+
nothing measured the seven conditions, and `held_by: []` there would read as "checked, and
|
|
105
|
+
nothing held it".
|
|
106
|
+
- **The questions are notified before the gate that is downstream of them (#203).** When the
|
|
107
|
+
ONLY failing condition is `questions`, `question.raised` goes out first and `gate.requested`'s
|
|
108
|
+
notification is held back — sent only if the gate is still pending once the questions settle,
|
|
109
|
+
or when `--wait-gates` lapses, and never at all when the loop closes the gate itself. The
|
|
110
|
+
owner used to be told to sign something before being told what the thing was, which sent him
|
|
111
|
+
to the wrong tap. The EVENT is appended either way: this defers a notification, never an
|
|
112
|
+
audit record.
|
|
113
|
+
- **An `auto` gate closes itself when the thing holding it clears (#203).** `--wait-gates` used
|
|
114
|
+
to poll `gate.status` and nothing else, so an auto gate whose only blocker was an open
|
|
115
|
+
question permanently degraded into a `human` gate for that stage: the owner answered all four
|
|
116
|
+
and nothing signed. Each poll now re-runs the seven conditions off disk for an `auto` policy
|
|
117
|
+
and, when every one holds, signs through the **same `approve` door** `next` uses — checks
|
|
118
|
+
re-run off disk, actor `auto`, the seven-condition note, the ordinary `gate.approved`. Never
|
|
119
|
+
for `human` and never for `agent`; a person's `approve` or `reject` still lands first and
|
|
120
|
+
overrides at any moment, and a refusal from `approve` leaves the gate waiting rather than
|
|
121
|
+
reporting a close.
|
|
122
|
+
- **"a auto gate" is now "an auto gate".** One article, on the one sentence that reaches a lock
|
|
123
|
+
screen, chosen by a helper rather than a literal because it depends on the policy word and
|
|
124
|
+
`auto` is the only vowel among the four.
|
|
125
|
+
|
|
126
|
+
### Added
|
|
127
|
+
|
|
128
|
+
- **A question can carry its own recommendation: `Recommended: <letter> — <why> [src: …]`
|
|
129
|
+
(#203).** Until now the only recommendation slot in the framework was the `agent` gate's
|
|
130
|
+
evidence note, which an `auto` gate never writes — so on the run above all four questions
|
|
131
|
+
rendered `recommendation: null`, while the stage that raised them was the one thing in the
|
|
132
|
+
run that knew the trade-off. The §2.7 block grammar gains one optional line, after the
|
|
133
|
+
options and before the `[Answer]:` slot, and the what/how/plan prompts and
|
|
134
|
+
`templates/questions.md` ask for one on every question with a real `[src:]`. The parse is
|
|
135
|
+
strict about the two machine-readable parts (one letter `A`–`E`, a dash before any prose) so
|
|
136
|
+
that it can be tolerant about everything else: **a line that does not match is ignored, never
|
|
137
|
+
refused** — it is guidance, so a typo costs the guidance and not the gate — and
|
|
138
|
+
`questions lint` says nothing about it. Precedence, stated once: an evidence note's
|
|
139
|
+
`recommend:` entry wins over the block's line for the same question id, and a question with
|
|
140
|
+
neither still renders no recommendation at all. Nothing manufactures one.
|
|
141
|
+
|
|
142
|
+
- **`tldrx facts add --repo <name>` is now checked against `workspace.yml`, through the one
|
|
143
|
+
implementation `tldrx answer --repo` already refused on (#186).** Measured on a scratch
|
|
144
|
+
workspace at `cd8721a`: `facts add "ghost repo test" --area test --decided-by driver --repo
|
|
145
|
+
ghost` exited **0** and wrote `repos: [ghost]`, while `answer Q1 "…" --repo ghost` on the same
|
|
146
|
+
workspace exited **1** with `--repo ghost is not a repo in this workspace — it has api`. The
|
|
147
|
+
mechanism was the whole story — `grep -n loadWorkspace src/cli/commands/facts.ts` exited 1:
|
|
148
|
+
that path never looked at what the workspace declares, it copied the flag onto the record. A
|
|
149
|
+
mis-scoped fact is the silent kind of wrong: nothing refuses it later, it is simply invisible
|
|
150
|
+
to every `{{facts}}` filter keyed on the real repo name, forever, and nothing anywhere says
|
|
151
|
+
why. The check is now a leaf (`src/cli/repoScope.ts`) both commands call rather than a
|
|
152
|
+
sentence copied into two files — two copies of a refusal drift, and the drift is silent
|
|
153
|
+
because each command's own test keeps passing — so `facts add` also inherits the
|
|
154
|
+
de-duplication the same flag already had on `answer` (`--repo api --repo api` scopes once).
|
|
155
|
+
Refused before the store is opened, exit **1**, `answer`'s family and `answer`'s wording,
|
|
156
|
+
including the "this workspace declares no repos" variant that keeps the sentence from
|
|
157
|
+
dangling. A test counts the sentence over `src/` and fails on the second copy.
|
|
158
|
+
|
|
4
159
|
## 0.14.0 — 2026-09-09
|
|
5
160
|
|
|
6
161
|
### Added
|
|
7
162
|
|
|
163
|
+
- **`tldrx run auto --wait-gates <duration>` — the loop waits for a signature the way it
|
|
164
|
+
already waits for an answer (#197).** Measured on a real workspace the day the notify hook
|
|
165
|
+
first drove a run: the question loop closed itself — the hook delivered Q1, the owner
|
|
166
|
+
answered from his chat, and `run auto` printed `waited 132s … resuming` — and then the very
|
|
167
|
+
next thing the stage did was reach its human gate, where the same owner approved from the
|
|
168
|
+
same chat and the loop had already exited 4. Three human gates a feature run is three manual
|
|
169
|
+
re-launches the hook was supposed to make unnecessary. The cause was scoped, not accidental:
|
|
170
|
+
`--wait-answers` gates its whole wait on an open-question card, which is `null` when the park
|
|
171
|
+
is a gate, so the wait was never reached. `--wait-gates` is a sibling flag rather than a wider
|
|
172
|
+
`--wait-answers`, because the two parks are closed by different verbs and calling a signature
|
|
173
|
+
an "answer" would be the flag name lying about what a person did. Approved → the loop carries
|
|
174
|
+
on; rejected → it stops and prints the note, which is now its LAST line so the `run.failed`
|
|
175
|
+
payload carries it to the phone of whoever has to act on it; lapsed → exit 4 with the same
|
|
176
|
+
lines it always had, after one `gate.timeout` (new kind, `question.timeout`'s twin, carrying
|
|
177
|
+
the approve and reject lines, the gate's policy, `waited_ms`, and `cost_usd` only when this
|
|
178
|
+
loop is the one that measured it). Nothing is spent while it polls — it reads files — and
|
|
179
|
+
whether a gate is pending is read through `waitingFor`, the one derivation `tldrx run status`
|
|
180
|
+
and the dashboard already share, never a second copy. **It waits FOR a signature and never
|
|
181
|
+
produces one**: there is no engine-side signing in this loop, so a stage on
|
|
182
|
+
`gates_policy: agent` stops it exactly as a `human` one does and is waited on identically —
|
|
183
|
+
an owner who switched three gates to `agent` expecting the loop to carry on was measuring
|
|
184
|
+
who MAY sign, not that anything had. Both wait flags may be given together, and without the
|
|
185
|
+
flag a gate exits 4 on the spot exactly as before.
|
|
186
|
+
- **The heartbeat stops telling a waiting owner that nothing is waiting on him at a GATE.**
|
|
187
|
+
`--notify-every`'s `status` payload learned in 0.11.1 not to say "Nothing is waiting on you"
|
|
188
|
+
over a run parked on a question — the fix that exists because a heartbeat is believed — but
|
|
189
|
+
its parked-ness came from the blocking-question predicate alone, so a run parked on a
|
|
190
|
+
signature got `waiting_on: []` and the exact sentence the fix was written to prevent. A
|
|
191
|
+
pending gate is now named in `waiting_on_gate` (`<phase>/<stage>`) beside `gate_policy`, the
|
|
192
|
+
summary says the run is waiting for a person to sign that stage, and `command` is the literal
|
|
193
|
+
`tldrx approve` line — the same spelling `gate.requested` and every decision card use, now
|
|
194
|
+
one exported helper instead of four literals. `waiting_on_gate` is a SIBLING key rather than
|
|
195
|
+
a member of `waiting_on`: an adapter maps every id in `waiting_on` to `tldrx answer <id>`,
|
|
196
|
+
and a stage id there would make it build a command nobody can type. Both keys are absent when
|
|
197
|
+
no gate is pending, so a heartbeat over a moving run is byte-identical to the one it sent
|
|
198
|
+
before. `gate.requested` now names the policy too, so an owner reading it on a phone knows
|
|
199
|
+
whether he is signing a `human` gate or overriding an `agent` one.
|
|
200
|
+
|
|
8
201
|
- **`run auto` now closes an `agent` gate itself, over a note it wrote and had validated (#198).**
|
|
9
202
|
`gates_policy: agent` said who MAY sign a gate; nothing in the engine produced the evidence note
|
|
10
203
|
it is signed over. The only writer was the `tldrx gate template` skeleton a host session fills
|
|
@@ -112,44 +305,6 @@
|
|
|
112
305
|
|
|
113
306
|
### Added
|
|
114
307
|
|
|
115
|
-
- **`tldrx run auto --wait-gates <duration>` — the loop waits for a signature the way it
|
|
116
|
-
already waits for an answer (#197).** Measured on a real workspace the day the notify hook
|
|
117
|
-
first drove a run: the question loop closed itself — the hook delivered Q1, the owner
|
|
118
|
-
answered from his chat, and `run auto` printed `waited 132s … resuming` — and then the very
|
|
119
|
-
next thing the stage did was reach its human gate, where the same owner approved from the
|
|
120
|
-
same chat and the loop had already exited 4. Three human gates a feature run is three manual
|
|
121
|
-
re-launches the hook was supposed to make unnecessary. The cause was scoped, not accidental:
|
|
122
|
-
`--wait-answers` gates its whole wait on an open-question card, which is `null` when the park
|
|
123
|
-
is a gate, so the wait was never reached. `--wait-gates` is a sibling flag rather than a wider
|
|
124
|
-
`--wait-answers`, because the two parks are closed by different verbs and calling a signature
|
|
125
|
-
an "answer" would be the flag name lying about what a person did. Approved → the loop carries
|
|
126
|
-
on; rejected → it stops and prints the note, which is now its LAST line so the `run.failed`
|
|
127
|
-
payload carries it to the phone of whoever has to act on it; lapsed → exit 4 with the same
|
|
128
|
-
lines it always had, after one `gate.timeout` (new kind, `question.timeout`'s twin, carrying
|
|
129
|
-
the approve and reject lines, the gate's policy, `waited_ms`, and `cost_usd` only when this
|
|
130
|
-
loop is the one that measured it). Nothing is spent while it polls — it reads files — and
|
|
131
|
-
whether a gate is pending is read through `waitingFor`, the one derivation `tldrx run status`
|
|
132
|
-
and the dashboard already share, never a second copy. **It waits FOR a signature and never
|
|
133
|
-
produces one**: there is no engine-side signing in this loop, so a stage on
|
|
134
|
-
`gates_policy: agent` stops it exactly as a `human` one does and is waited on identically —
|
|
135
|
-
an owner who switched three gates to `agent` expecting the loop to carry on was measuring
|
|
136
|
-
who MAY sign, not that anything had. Both wait flags may be given together, and without the
|
|
137
|
-
flag a gate exits 4 on the spot exactly as before.
|
|
138
|
-
- **The heartbeat stops telling a waiting owner that nothing is waiting on him at a GATE.**
|
|
139
|
-
`--notify-every`'s `status` payload learned in 0.11.1 not to say "Nothing is waiting on you"
|
|
140
|
-
over a run parked on a question — the fix that exists because a heartbeat is believed — but
|
|
141
|
-
its parked-ness came from the blocking-question predicate alone, so a run parked on a
|
|
142
|
-
signature got `waiting_on: []` and the exact sentence the fix was written to prevent. A
|
|
143
|
-
pending gate is now named in `waiting_on_gate` (`<phase>/<stage>`) beside `gate_policy`, the
|
|
144
|
-
summary says the run is waiting for a person to sign that stage, and `command` is the literal
|
|
145
|
-
`tldrx approve` line — the same spelling `gate.requested` and every decision card use, now
|
|
146
|
-
one exported helper instead of four literals. `waiting_on_gate` is a SIBLING key rather than
|
|
147
|
-
a member of `waiting_on`: an adapter maps every id in `waiting_on` to `tldrx answer <id>`,
|
|
148
|
-
and a stage id there would make it build a command nobody can type. Both keys are absent when
|
|
149
|
-
no gate is pending, so a heartbeat over a moving run is byte-identical to the one it sent
|
|
150
|
-
before. `gate.requested` now names the policy too, so an owner reading it on a phone knows
|
|
151
|
-
whether he is signing a `human` gate or overriding an `agent` one.
|
|
152
|
-
|
|
153
308
|
- **`tldrx expert rescore [<name>] [--area <a>]` — score the knowledge you already paid for.**
|
|
154
309
|
Its sibling `recompute` is arithmetic over the evidence rows already in `competencies.yml`;
|
|
155
310
|
`rescore` RE-READS `knowledge/*.md` and derives their evidence again under today's rules. It
|
|
@@ -1487,6 +1642,7 @@
|
|
|
1487
1642
|
correctly and is spliced into no prompt is the `templates/story.md` failure again (#48).
|
|
1488
1643
|
6 red before, green after. `docs/spec.md` §2.13 and the `delivery` expert each carry one
|
|
1489
1644
|
line pointing at the checklist.
|
|
1645
|
+
|
|
1490
1646
|
- **The landing page now sells the unattended flow it never mentioned (#128).** Measured at
|
|
1491
1647
|
`95a39db`: `grep -c 'tldrx drive' docs-site/index.md` returned `0`, and so did its Spanish
|
|
1492
1648
|
twin — a bare `grep drive` exited `1` on both. `tldrx drive` is the star command for handing
|
|
@@ -4073,6 +4229,79 @@ same amount after it; what changed is that the page now says how big the bound i
|
|
|
4073
4229
|
|
|
4074
4230
|
### Fixed
|
|
4075
4231
|
|
|
4232
|
+
- **A trainer that `cd`s no longer writes its knowledge file into a different git repo.**
|
|
4233
|
+
Measured 2026-08-31 on `~/scavtopia` (five repos, ten `expert train --mode light` runs): the
|
|
4234
|
+
`mcp` run was rejected with `mcp.md.partial was never written`, and the file had been written —
|
|
4235
|
+
46 lines, 9,567 bytes, complete and usable — to
|
|
4236
|
+
`whiteboard/.tldrx/experts/mcp/knowledge/mcp.md.partial`. The sub-agent ran
|
|
4237
|
+
`cd <workspace>/whiteboard` to execute that repo's declared gate command, then wrote the
|
|
4238
|
+
RELATIVE path the prompt had given it, and the path resolved against the repo it had `cd`'d
|
|
4239
|
+
into. Three costs from the one bug: **$1.23 charged for work that was finished and then
|
|
4240
|
+
orphaned**, a parasitic `.tldrx/` tree left inside an unrelated git repo (`git -C whiteboard
|
|
4241
|
+
status` → `?? .tldrx/`), and **no repair round possible** — the missing-file branch returns
|
|
4242
|
+
before the repair check, so this failure mode was unrecoverable by construction even with
|
|
4243
|
+
budget left. Fixed at both ends.
|
|
4244
|
+
- **Prevention: the prompt now states an ABSOLUTE output path**, workspace-root-resolved, and
|
|
4245
|
+
says why — "If you `cd` into a repo to run its gate command, a relative path then resolves
|
|
4246
|
+
against THAT repo … and throws the whole paid run away. That is measured, not hypothetical."
|
|
4247
|
+
Both training prompts carry it, and so does the repair round's target, for the same reason.
|
|
4248
|
+
- **Recovery: when the file is missing, the declared repo roots are probed** for the stray
|
|
4249
|
+
relative write before "never written" is said. A file found there is moved back and validated
|
|
4250
|
+
exactly as if it had landed correctly — recovery is not a pass, the same `parseKnowledgeFile`
|
|
4251
|
+
still judges it, and a recovered file that fails can still be repaired because the probe runs
|
|
4252
|
+
ABOVE the repair round.
|
|
4253
|
+
- **The note is honest and names the mess.** `recovered: the trainer wrote to
|
|
4254
|
+
whiteboard/.tldrx/… , inside the `whiteboard` repo — a relative `.tldrx/…` path resolves
|
|
4255
|
+
against whatever directory it had `cd`'d into.` The empty parasitic directories are removed
|
|
4256
|
+
on the way out; a directory holding anything else is **left in place and named**, with the
|
|
4257
|
+
`git -C <repo> status` to run, because a tool that deletes inside a repo it was never asked
|
|
4258
|
+
to touch is a worse bug than the one it is fixing. A repo carrying its own
|
|
4259
|
+
`.tldrx/workspace.yml` is skipped entirely — that file may belong to a nested workspace, and
|
|
4260
|
+
taking it would be theft rather than recovery.
|
|
4261
|
+
- When no stray is found the verdict is unchanged and now says where it looked.
|
|
4262
|
+
|
|
4263
|
+
- **A rejected training run records WHICH problems, not just how many.**
|
|
4264
|
+
Measured 2026-08-31: `components` failed with 12 problems for $1.02, and `training.jsonl` — the
|
|
4265
|
+
durable record — held only the string `"…does not validate — 12 problem(s)"`. The twelve went
|
|
4266
|
+
to stdout, where five of them were printed and the rest elided as `(+7 more)`. Anyone who had
|
|
4267
|
+
not captured stdout, which is anyone running this normally, could not tell why a $1.02 run
|
|
4268
|
+
failed. The list is now persisted twice.
|
|
4269
|
+
- **On the ledger**: `check.failed.payload` carries `problems` (the rendered per-problem
|
|
4270
|
+
lines), `problems_total`, `errors`, and `task`. The list is fitted to the record's 4 KB
|
|
4271
|
+
payload cap and reports `problems_omitted` when it does not fit — an append that THROWS on
|
|
4272
|
+
an oversize payload would take the cost line down with the reasons, which is the opposite of
|
|
4273
|
+
the point. The repair round's own `check.failed` carries what it sent back, so "what did the
|
|
4274
|
+
repair actually fix" is answerable later.
|
|
4275
|
+
- **In the file**: `<area>.rejected.md` now opens with a `# REJECTED` header — expert/area,
|
|
4276
|
+
mode, timestamp, dollars spent, error and warning counts, and every problem, uncapped —
|
|
4277
|
+
above the trainer's bytes exactly as written, separated by a rule. A quarantine with no
|
|
4278
|
+
verdict (a sub-agent that died, a rollback) gets no header: there were no reasons to state
|
|
4279
|
+
and inventing them would be inventing the reason.
|
|
4280
|
+
|
|
4281
|
+
- **`## Sources` is now taught as prose with the refused shape shown.** Same batch: four of the
|
|
4282
|
+
five problems the `components` report printed are one mistake four times — `L34 Sources: no
|
|
4283
|
+
[src: …] token`, `L35`, `L36`, `L37`. The trainer had written the recap as a bulleted list.
|
|
4284
|
+
The prompt already said "**Sources** — prose", and a writer who reads that as a style note
|
|
4285
|
+
writes bullets, because bullets are what the other four sections take. The rule it collides
|
|
4286
|
+
with is genuinely file-wide — `parseKnowledgeFile` requires a `[src: …]` token on EVERY list
|
|
4287
|
+
item in every declared section, recap included, and an unsourced one is an error that rejects
|
|
4288
|
+
the file whole. Both prompts now show the accepted prose next to the refused bullets, the same
|
|
4289
|
+
move the execution-claim rule makes. Whether an unsourced recap bullet should be a warning
|
|
4290
|
+
rather than an error is a real question and is deliberately NOT settled here.
|
|
4291
|
+
|
|
4292
|
+
|
|
4293
|
+
- **`tldrx expert train` already exits nonzero when a training fails.** The 2026-08-31 batch
|
|
4294
|
+
report measured shell `EXIT=0` on all ten invocations, including the three that failed their
|
|
4295
|
+
check — but nine of those ten ran on a build that predates this one (`dist/tldrx.js` was
|
|
4296
|
+
rewritten mid-batch at 05:40Z). On the current source the code path is intact:
|
|
4297
|
+
`runTraining` returns `EXIT_AGENT_FAILED` (5), `expert train` returns `outcome.code`,
|
|
4298
|
+
`dispatch` returns it, and `bin/tldrx.ts` does `process.exit(await dispatch(...))`. Now pinned
|
|
4299
|
+
by three tests that drive the REAL CLI as a subprocess with a fake `claude` on PATH and assert
|
|
4300
|
+
the PROCESS exit code — one for a file that does not validate, one for a file that was never
|
|
4301
|
+
written, one for the passing case — because "`runTraining` returns 5" and "the process exits
|
|
4302
|
+
5" are two different claims. Falsified before being trusted: making `expert train` return
|
|
4303
|
+
`EXIT_OK` breaks two of the three.
|
|
4304
|
+
|
|
4076
4305
|
- **`tldrx learn` — the cold-player QA round (#30).** A first-time player played all eight chapters
|
|
4077
4306
|
and returned SHIP-with-fixlist. Everything they found is fixed or recorded:
|
|
4078
4307
|
- **Chapter 8 no longer lies about the brake.** It said "the phase has already spent its Watch
|
|
@@ -5274,66 +5503,6 @@ none of these behaves byte-identically to the release before them.
|
|
|
5274
5503
|
|
|
5275
5504
|
### Fixed
|
|
5276
5505
|
|
|
5277
|
-
- **A trainer that `cd`s no longer writes its knowledge file into a different git repo.**
|
|
5278
|
-
Measured 2026-08-31 on `~/scavtopia` (five repos, ten `expert train --mode light` runs): the
|
|
5279
|
-
`mcp` run was rejected with `mcp.md.partial was never written`, and the file had been written —
|
|
5280
|
-
46 lines, 9,567 bytes, complete and usable — to
|
|
5281
|
-
`whiteboard/.tldrx/experts/mcp/knowledge/mcp.md.partial`. The sub-agent ran
|
|
5282
|
-
`cd <workspace>/whiteboard` to execute that repo's declared gate command, then wrote the
|
|
5283
|
-
RELATIVE path the prompt had given it, and the path resolved against the repo it had `cd`'d
|
|
5284
|
-
into. Three costs from the one bug: **$1.23 charged for work that was finished and then
|
|
5285
|
-
orphaned**, a parasitic `.tldrx/` tree left inside an unrelated git repo (`git -C whiteboard
|
|
5286
|
-
status` → `?? .tldrx/`), and **no repair round possible** — the missing-file branch returns
|
|
5287
|
-
before the repair check, so this failure mode was unrecoverable by construction even with
|
|
5288
|
-
budget left. Fixed at both ends.
|
|
5289
|
-
- **Prevention: the prompt now states an ABSOLUTE output path**, workspace-root-resolved, and
|
|
5290
|
-
says why — "If you `cd` into a repo to run its gate command, a relative path then resolves
|
|
5291
|
-
against THAT repo … and throws the whole paid run away. That is measured, not hypothetical."
|
|
5292
|
-
Both training prompts carry it, and so does the repair round's target, for the same reason.
|
|
5293
|
-
- **Recovery: when the file is missing, the declared repo roots are probed** for the stray
|
|
5294
|
-
relative write before "never written" is said. A file found there is moved back and validated
|
|
5295
|
-
exactly as if it had landed correctly — recovery is not a pass, the same `parseKnowledgeFile`
|
|
5296
|
-
still judges it, and a recovered file that fails can still be repaired because the probe runs
|
|
5297
|
-
ABOVE the repair round.
|
|
5298
|
-
- **The note is honest and names the mess.** `recovered: the trainer wrote to
|
|
5299
|
-
whiteboard/.tldrx/… , inside the `whiteboard` repo — a relative `.tldrx/…` path resolves
|
|
5300
|
-
against whatever directory it had `cd`'d into.` The empty parasitic directories are removed
|
|
5301
|
-
on the way out; a directory holding anything else is **left in place and named**, with the
|
|
5302
|
-
`git -C <repo> status` to run, because a tool that deletes inside a repo it was never asked
|
|
5303
|
-
to touch is a worse bug than the one it is fixing. A repo carrying its own
|
|
5304
|
-
`.tldrx/workspace.yml` is skipped entirely — that file may belong to a nested workspace, and
|
|
5305
|
-
taking it would be theft rather than recovery.
|
|
5306
|
-
- When no stray is found the verdict is unchanged and now says where it looked.
|
|
5307
|
-
|
|
5308
|
-
- **A rejected training run records WHICH problems, not just how many.**
|
|
5309
|
-
Measured 2026-08-31: `components` failed with 12 problems for $1.02, and `training.jsonl` — the
|
|
5310
|
-
durable record — held only the string `"…does not validate — 12 problem(s)"`. The twelve went
|
|
5311
|
-
to stdout, where five of them were printed and the rest elided as `(+7 more)`. Anyone who had
|
|
5312
|
-
not captured stdout, which is anyone running this normally, could not tell why a $1.02 run
|
|
5313
|
-
failed. The list is now persisted twice.
|
|
5314
|
-
- **On the ledger**: `check.failed.payload` carries `problems` (the rendered per-problem
|
|
5315
|
-
lines), `problems_total`, `errors`, and `task`. The list is fitted to the record's 4 KB
|
|
5316
|
-
payload cap and reports `problems_omitted` when it does not fit — an append that THROWS on
|
|
5317
|
-
an oversize payload would take the cost line down with the reasons, which is the opposite of
|
|
5318
|
-
the point. The repair round's own `check.failed` carries what it sent back, so "what did the
|
|
5319
|
-
repair actually fix" is answerable later.
|
|
5320
|
-
- **In the file**: `<area>.rejected.md` now opens with a `# REJECTED` header — expert/area,
|
|
5321
|
-
mode, timestamp, dollars spent, error and warning counts, and every problem, uncapped —
|
|
5322
|
-
above the trainer's bytes exactly as written, separated by a rule. A quarantine with no
|
|
5323
|
-
verdict (a sub-agent that died, a rollback) gets no header: there were no reasons to state
|
|
5324
|
-
and inventing them would be inventing the reason.
|
|
5325
|
-
|
|
5326
|
-
- **`## Sources` is now taught as prose with the refused shape shown.** Same batch: four of the
|
|
5327
|
-
five problems the `components` report printed are one mistake four times — `L34 Sources: no
|
|
5328
|
-
[src: …] token`, `L35`, `L36`, `L37`. The trainer had written the recap as a bulleted list.
|
|
5329
|
-
The prompt already said "**Sources** — prose", and a writer who reads that as a style note
|
|
5330
|
-
writes bullets, because bullets are what the other four sections take. The rule it collides
|
|
5331
|
-
with is genuinely file-wide — `parseKnowledgeFile` requires a `[src: …]` token on EVERY list
|
|
5332
|
-
item in every declared section, recap included, and an unsourced one is an error that rejects
|
|
5333
|
-
the file whole. Both prompts now show the accepted prose next to the refused bullets, the same
|
|
5334
|
-
move the execution-claim rule makes. Whether an unsourced recap bullet should be a warning
|
|
5335
|
-
rather than an error is a real question and is deliberately NOT settled here.
|
|
5336
|
-
|
|
5337
5506
|
- **A rejected knowledge file gets ONE repair round before the money is thrown away.**
|
|
5338
5507
|
Measured 2026-08-30 on `~/scavtopia`: `tldrx expert train dotnet-stack --area dotnet --mode
|
|
5339
5508
|
light` spent **$1.69**, the trainer wrote `knowledge/dotnet.md.partial`, and the validator
|
|
@@ -5666,19 +5835,6 @@ none of these behaves byte-identically to the release before them.
|
|
|
5666
5835
|
|
|
5667
5836
|
### Verified, not changed
|
|
5668
5837
|
|
|
5669
|
-
- **`tldrx expert train` already exits nonzero when a training fails.** The 2026-08-31 batch
|
|
5670
|
-
report measured shell `EXIT=0` on all ten invocations, including the three that failed their
|
|
5671
|
-
check — but nine of those ten ran on a build that predates this one (`dist/tldrx.js` was
|
|
5672
|
-
rewritten mid-batch at 05:40Z). On the current source the code path is intact:
|
|
5673
|
-
`runTraining` returns `EXIT_AGENT_FAILED` (5), `expert train` returns `outcome.code`,
|
|
5674
|
-
`dispatch` returns it, and `bin/tldrx.ts` does `process.exit(await dispatch(...))`. Now pinned
|
|
5675
|
-
by three tests that drive the REAL CLI as a subprocess with a fake `claude` on PATH and assert
|
|
5676
|
-
the PROCESS exit code — one for a file that does not validate, one for a file that was never
|
|
5677
|
-
written, one for the passing case — because "`runTraining` returns 5" and "the process exits
|
|
5678
|
-
5" are two different claims. Falsified before being trusted: making `expert train` return
|
|
5679
|
-
`EXIT_OK` breaks two of the three.
|
|
5680
|
-
|
|
5681
|
-
|
|
5682
5838
|
- **The walk already skips vendored and generated trees**, and always did: `SKIPPED_DIRS`
|
|
5683
5839
|
in `detect/walk.ts` covers `node_modules`, `dist`, `build`, `out`, `bin`, `obj`,
|
|
5684
5840
|
`target`, `.venv`, `Pods`, `.next`, `.expo`, `coverage` and more, plus every
|
package/README.md
CHANGED
|
@@ -316,6 +316,8 @@ back on the registry is 0.3.0.
|
|
|
316
316
|
|
|
317
317
|
| Version | Date | Status | Contains |
|
|
318
318
|
|---|---|---|---|
|
|
319
|
+
| 0.14.2 | 2026-09-09 | `beta` | every citation check starts from a fresh view of `questions.md` and `facts.yml`: `srcToken.ts` memoised both indexes at module scope and nothing outside `test/` ever dropped them, so in `tldrx run auto` — one Node process for a whole run — the FIRST citation resolved anywhere froze the view every later stage was then judged against; measured on three real unattended workspaces at 0.14.1, a `how` stage refused with `no such question Q2 … declared: Q1` over questions it had itself written minutes earlier, and another refused over `145 live fact(s)` when `facts.yml` held 148, the three extra written by the owner's answers two seconds before the stage started — roughly $11 of paid turns thrown away for ids that were real the whole time; the indexes are now refreshed inside `toSrcContext`, the one place a citation context is built and a place every caller reaches exactly once per check, gate or hook, so a document's forty citations still read `facts.yml` once while nothing survives the check that read it |
|
|
320
|
+
| 0.14.1 | 2026-09-09 | `beta` | an auto gate that says what holds it and closes itself, and three derivations cut to one: measured on a real workspace at 0.14.0, the first `run auto --wait-answers 4h --wait-gates 4h` with a notify hook finished its What stage for $1.98 with every declared check green and told the owner *"waiting at a auto gate that did not close by itself — a person signs it"* — no reason, because `evaluateAutoGate` had computed exactly that sentence into a stdout line nobody was watching while `gate.requested` was appended a hundred lines BEFORE the verdict existed; the verdict is now taken one statement earlier and `why`/`held_by` ride the event to the phone (present only for an `auto` policy, since `held_by: []` on a `human` gate would read as "checked, and nothing held it"), the questions that hold a gate are notified BEFORE the gate downstream of them so the owner stops being told to sign a thing before being told what it is, and an `auto` gate whose only blocker was an open question stops permanently degrading into a `human` one — each poll re-runs the seven conditions off disk and signs through the same `approve` door `next` uses, never for `human` and never for `agent`, a person's `approve` or `reject` still landing first; a question can carry its own `Recommended: <letter> — <why> [src: …]`, parsed tolerantly so an older note reads as not recorded rather than refusing; `tldrx facts add --repo <name>` is now checked against `workspace.yml` through the ONE leaf `answer --repo` already refused on, because the unvalidated flag wrote `repos: [ghost]` at exit 0 and every prompt or filter keyed on a real repo name was then silently blind to that fact; the five phase ids are written out in one file instead of three — `PHASE_IDS`, `QUESTION_PHASES`, and an inline literal walked by `questions lint` that `QUESTION_PHASES`' own doc comment claimed could not exist — pinned from both ends, because an identical second copy and a copy that has already drifted redden different guards and neither alone is enough; a training test stops running on bun's fixed 5000 ms after a spawn probe, validated first against a file the heuristic already claimed (87 spawns recorded, so the instrument can see the thing), found it spawning three real children unclaimed by any marker, with the 14 further unclaimed files filed rather than fixed; and `release-check.sh` refuses a rewritten released section — every dated heading whose tag is present must equal `git show vX.Y.Z:CHANGELOG.md` byte for byte, three sections having drifted with nothing checking, and an amendment recorded in `CHANGELOG.amendments` must still contain the tag's section as an ordered subsequence and may only add lines that exist verbatim in its source sha, after a reviewer proved the first version of that check happily passed an invented bullet |
|
|
319
321
|
| 0.14.0 | 2026-09-09 | `beta` | an `agent` gate the engine can actually close, and three computations of the same answer cut to one: `gates_policy: agent` named who MAY sign a gate but nothing in the engine produced the evidence note it is signed over — measured on 0.13.1, an owner ran `tldrx run gates set what:agent`, was told "an agent may now close it", and the loop stopped at the next gate anyway with exit 4 — so `run auto` now spawns one bounded **gate signer** when a stage's checks pass under an `agent` policy: the stage's own model and effort, a quarter of its per-agent ceiling, a tool allowance that reads anything and writes exactly one file, and a prompt carrying the stage's declared outputs, the seven `auto` conditions as measured and the §2.8 skeleton `gate template` itself renders — the note going through the UNCHANGED `approve --as-agent` path, so a refusal, a note that does not validate, a signer that wrote nothing and a signer that died are one outcome, pending for a person with the reason named on stdout and now in the `gate.requested` summary, and there is no flag to turn it on because an `agent` policy is already the owner's recorded decision; the turn is recorded like any other (`role: gate-signer`, a `run.yml` task row, a row in `tldrx cost`) and taken BEFORE the stage moves to `awaiting_gate`, because the other order had a person sign the gate the engine was mid-signing, three runs out of three; and the suite stops being run three times per change — the pre-merge reviewer now runs only the test files that cover its diff plus `typecheck`, never the full `bun test`, since the wave re-runs every gate on the MERGED tree anyway, `publish.yml` refuses unless `ci` has a `success` run for the same sha instead of recomputing typecheck/tests/build (~87 min/week of runner time, with a `cancelled` ci run failing by name and the remedy), `ci` cancels a run a newer push has already superseded (25 of 122 push runs began under 10 minutes apart), the docs deploy finally fires on `src/cli/helpText.ts` so a help-registry change stops deploying nothing while the published CLI reference goes stale, and `AGENTS.md` §2 now says out loud that a slash in a branch name is a directory, which is what the review-record gate builds |
|
|
320
322
|
| 0.13.1 | 2026-09-08 | `beta` | a stage prompt that opens by saying what to do: every stage prompt now leads with a generated brief — who the reader is, which stage of which run, that the template below is to be FILLED, the exact path of every declared output, and that a question goes in the questions file rather than back to an operator who is not there — because on a real workspace at 0.13.0 a What sub-agent read its 66,452-byte prompt, found no request in it, wrote none of its six declared outputs and asked what to do, $0.29 spent; the brief is generated from the same `outputs:` list `pending.json` records, so it cannot name a path the commit will not look for, and the failure was never a regression — the spliced citation grammar grew the stage section 5,007 B → 13,180 B and the missing instruction was finally outnumbered; and the `N runs are open` nudge, the one imperative-shaped sentence in that agent's window and the one it duly answered, stops reaching sub-agents at all — `spawnAgent` marks every child it spawns and `session-start` emits nothing when it sees the marker, an absent marker still being a human's session and behaving exactly as before |
|
|
321
323
|
| 0.13.0 | 2026-09-08 | `beta` | evidence a role expert can actually earn, and a review that leaves a record: `--mode full`'s runs pass mines `tldrx-work/**` while the domain gate judged every citation it produced against folders of code — measured at four role experts, **$9.47 and one evidence row**, and unfixable from the workspace because the single spelling the matcher would reach is the one `domainPaths()` drops — so the gate now treats the run record as in-domain for the file mined FROM it, scoped to the pass and never to the expert's `kind:` (a light file citing a handoff is still out of domain and still says so), two shipped role templates stop declaring `.tldrx/map/**` and `.tldrx/map/{repo}/gotchas.md` paths that matched nothing at all, and a pass that validated, spent money and earned zero rows now prints `the level did not move — $X.XX bought 0 evidence row(s)` with its reasons carried into `check.passed` instead of a silent ledger; `tldrx expert rescore` recovers what was already bought for $0 by re-reading `knowledge/*.md` under today's rules, dating rows by the knowledge file's own `trained_at` and never by the clock, with `rescored_at` additive beside `at` — its ABSENCE keeping the meaning every existing row had — and one `evidence.rescored` line per file it actually moved, so a free re-derivation can never be read as a paid turn; `scripts/merge-wave.sh` refuses a branch carrying no `.review/<branch>.md` with **exit 10**, its own code because `2` in that script is already "merge conflict", a stale record refusing rather than warning and staleness measured as "the code moved" rather than "the sha differs", since committing the record moves the head past exactly the sha it names; and the mutation check moves from the reviewer, whose allowance is `Read`/`Grep`/`Glob`/`Bash(git diff *)` and holds no pen, to the developer's contract that can run it, leaving the reviewer the read it can actually perform |
|
|
@@ -1,20 +1,23 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
conflictOf
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-w8qm1jbn.js";
|
|
5
5
|
import {
|
|
6
6
|
FactsStore,
|
|
7
7
|
formatJaccard
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-mzwqb08p.js";
|
|
9
9
|
import {
|
|
10
10
|
parseHookInput,
|
|
11
11
|
readStdin
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-tpz0zb0t.js";
|
|
13
|
+
import {
|
|
14
|
+
PHASE_IDS
|
|
15
|
+
} from "./chunk-dzkg8c68.js";
|
|
13
16
|
import {
|
|
14
17
|
EventLog,
|
|
15
18
|
PHASE_ID_RE
|
|
16
|
-
} from "./chunk-
|
|
17
|
-
import"./chunk-
|
|
19
|
+
} from "./chunk-dnat9z30.js";
|
|
20
|
+
import"./chunk-db7wf3hy.js";
|
|
18
21
|
import {
|
|
19
22
|
ADVISORY_KEY,
|
|
20
23
|
MAX_FACT_CHARS,
|
|
@@ -24,7 +27,7 @@ import {
|
|
|
24
27
|
renderQuestionBlock,
|
|
25
28
|
replaceBlock,
|
|
26
29
|
serializeQuestions
|
|
27
|
-
} from "./chunk-
|
|
30
|
+
} from "./chunk-bqmstq21.js";
|
|
28
31
|
import {
|
|
29
32
|
ITERATION_ONLY_SLOT,
|
|
30
33
|
PROJECT_FRAMEWORK_DIR,
|
|
@@ -32,7 +35,7 @@ import {
|
|
|
32
35
|
commandProbeIssues,
|
|
33
36
|
factsPath,
|
|
34
37
|
parseYaml
|
|
35
|
-
} from "./chunk-
|
|
38
|
+
} from "./chunk-wwwmkey4.js";
|
|
36
39
|
|
|
37
40
|
// src/hooks/answer-capture.ts
|
|
38
41
|
import { existsSync as existsSync5 } from "fs";
|
|
@@ -334,7 +337,7 @@ import { appendFileSync as appendFileSync2, existsSync as existsSync3, readFileS
|
|
|
334
337
|
import { join as join3 } from "node:path";
|
|
335
338
|
|
|
336
339
|
// src/core/run/questionCards.ts
|
|
337
|
-
var QUESTION_PHASES =
|
|
340
|
+
var QUESTION_PHASES = PHASE_IDS;
|
|
338
341
|
|
|
339
342
|
// src/core/answers/raiseConflict.ts
|
|
340
343
|
var RAISED_BY = "tldrx";
|
|
@@ -379,6 +382,7 @@ function raiseConflictQuestion(args) {
|
|
|
379
382
|
{ letter: "B", text: `${args.oldFactId} is right — supersede ${args.newFactId}` },
|
|
380
383
|
{ letter: "C", text: "Both are partly right — write the correction below" }
|
|
381
384
|
],
|
|
385
|
+
recommended: null,
|
|
382
386
|
answer: "",
|
|
383
387
|
answerIndex: -1,
|
|
384
388
|
footer: null,
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
budgetGateDeny
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-dxk7eaqn.js";
|
|
5
5
|
import {
|
|
6
6
|
allow,
|
|
7
7
|
deny,
|
|
8
8
|
readPayload,
|
|
9
9
|
runHook,
|
|
10
10
|
toolInput
|
|
11
|
-
} from "./chunk-
|
|
12
|
-
import"./chunk-
|
|
11
|
+
} from "./chunk-gvz0rzb9.js";
|
|
12
|
+
import"./chunk-tpz0zb0t.js";
|
|
13
13
|
import {
|
|
14
14
|
asRunBudget,
|
|
15
15
|
currentActor,
|
|
@@ -29,20 +29,20 @@ import {
|
|
|
29
29
|
validateRunBudget,
|
|
30
30
|
wouldExceed,
|
|
31
31
|
wouldExceedHostTokens
|
|
32
|
-
} from "./chunk-
|
|
32
|
+
} from "./chunk-fefnr71h.js";
|
|
33
33
|
import {
|
|
34
34
|
EventLog
|
|
35
|
-
} from "./chunk-
|
|
35
|
+
} from "./chunk-dnat9z30.js";
|
|
36
36
|
import {
|
|
37
37
|
noteDeprecations
|
|
38
|
-
} from "./chunk-
|
|
38
|
+
} from "./chunk-bqmstq21.js";
|
|
39
39
|
import {
|
|
40
40
|
PROJECT_WORK_DIR,
|
|
41
41
|
findWorkspaceRoot,
|
|
42
42
|
locateWork,
|
|
43
43
|
parseYaml,
|
|
44
44
|
stageYamlPath
|
|
45
|
-
} from "./chunk-
|
|
45
|
+
} from "./chunk-wwwmkey4.js";
|
|
46
46
|
|
|
47
47
|
// src/hooks/budget-gate.ts
|
|
48
48
|
import { existsSync as existsSync2, readFileSync as readFileSync2, statSync } from "node:fs";
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
spentBasis,
|
|
11
11
|
tallyOf,
|
|
12
12
|
validateRunBudget
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-fefnr71h.js";
|
|
14
14
|
import {
|
|
15
15
|
EventLog,
|
|
16
16
|
asRunFile,
|
|
@@ -22,7 +22,7 @@ import {
|
|
|
22
22
|
isTerminal,
|
|
23
23
|
stageAt,
|
|
24
24
|
validateRunFile
|
|
25
|
-
} from "./chunk-
|
|
25
|
+
} from "./chunk-dnat9z30.js";
|
|
26
26
|
import {
|
|
27
27
|
backupPathFor,
|
|
28
28
|
isAlive,
|
|
@@ -31,19 +31,19 @@ import {
|
|
|
31
31
|
workspaceRootOfRunDir,
|
|
32
32
|
writeAtomic,
|
|
33
33
|
yamlScalar
|
|
34
|
-
} from "./chunk-
|
|
34
|
+
} from "./chunk-db7wf3hy.js";
|
|
35
35
|
import {
|
|
36
36
|
isAdvisory,
|
|
37
37
|
noteDeprecations,
|
|
38
38
|
openBlocks,
|
|
39
39
|
parseQuestions
|
|
40
|
-
} from "./chunk-
|
|
40
|
+
} from "./chunk-bqmstq21.js";
|
|
41
41
|
import {
|
|
42
42
|
FRAMEWORK_ROOT,
|
|
43
43
|
listRunDirs,
|
|
44
44
|
parseYaml,
|
|
45
45
|
parseYamlRepairing
|
|
46
|
-
} from "./chunk-
|
|
46
|
+
} from "./chunk-wwwmkey4.js";
|
|
47
47
|
|
|
48
48
|
// src/core/statusline/runSnapshot.ts
|
|
49
49
|
import { existsSync as existsSync3, readFileSync as readFileSync4 } from "node:fs";
|