tldr-experts 0.22.0 → 0.23.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 +55 -0
- package/README.md +1 -0
- package/dist/tldrx.js +94 -11
- package/package.json +1 -1
- package/plugin/.claude-plugin/plugin.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,60 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.23.0 — 2026-09-14
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
|
|
7
|
+
- **A fix list with nothing to fix now settles the story `done` instead of buying a developer round
|
|
8
|
+
(#295, first half).** Measured on a live unattended run (0.20.0): the reviewer signed a two-finding
|
|
9
|
+
fix list, both `docs` and both `defer-with-log`, and the story still parked at `review` — the Build
|
|
10
|
+
then spawned a fix round for it, which died four times on command-form refusals (#294) with nothing
|
|
11
|
+
to fix. The rule that a `fixlist` verdict parks the story was written when a fix list meant "here is
|
|
12
|
+
what the author must do next"; #255 then made all-deferred lists the normal outcome of a docs-only
|
|
13
|
+
review and never adjusted who settles the story. Now the count the operator line already prints —
|
|
14
|
+
findings still `fix-now` in the PARSED list, #255's routing included — decides: zero means the
|
|
15
|
+
reviewer signed and nothing is owed a developer, so the story is `done` with `verdict: fixlist` on
|
|
16
|
+
its record. The artifact is still written and its deferred findings still reach `retro.md`: settling
|
|
17
|
+
the story does not settle what it deferred. One `fix-now` finding parks it exactly as before.
|
|
18
|
+
- **`story reopen --as-is` can settle a story whose work is already on the epic and whose review
|
|
19
|
+
never completed — review-only, merging nothing (#295, second half).** Under merge-before-review
|
|
20
|
+
(#282) a story's diff can sit entirely on the epic while nothing has judged it: measured twice on
|
|
21
|
+
2026-09-13, a reviewer that died and a later attempt that died on a bare `echo $?` with no work.
|
|
22
|
+
#279's guard — the branch must carry a commit the epic has not got — is right for a branch with no
|
|
23
|
+
work and blind to this mirror image, where there is nothing to MERGE and something to SETTLE; the
|
|
24
|
+
operator's only moves were inventing a commit for `--as-is` to take, which corrupts the
|
|
25
|
+
measurement, or leaving the story blocked with its dependents (#280). The guard is not relaxed. A
|
|
26
|
+
new, named case sits beside it: the review ledger now keeps the story's last recorded merge
|
|
27
|
+
(`commit` + `epic_base`) across reopen boundaries — a reopen resets what counts against a story,
|
|
28
|
+
not what the epic holds — and `--as-is` over a branch that carries nothing beyond its epic asks
|
|
29
|
+
whether anything judged that merge. `n-a` and `error` mean nothing did, and the turn runs
|
|
30
|
+
review-only: the branch is brought up to the epic `--ff-only` (nothing of its own to lose), the
|
|
31
|
+
DoD runs on the epic head, the reviewer is handed the recorded range, and nothing is merged —
|
|
32
|
+
`task.done` carries `as_is_reason: review-only`, the review log's developer line says so and names
|
|
33
|
+
the commit it reviewed. A review that STANDS (`changes`, `approve`, `fixlist`) still refuses, and
|
|
34
|
+
the refusal now names that verdict beside the #279 sentence: what is owed is a fix, not a third
|
|
35
|
+
opinion on the same bytes — which is also why the #279 pin test (two `changes` over a reset
|
|
36
|
+
branch) is untouched. The issue proposed "any verdict but `approve`"; that would have flipped that
|
|
37
|
+
pin, so the boundary shipped is the one in the issue's title: the review never completed.
|
|
38
|
+
- **A turn's failure record can no longer borrow the word `success` from the provider (#296).**
|
|
39
|
+
MEASURED on two live unattended runs while the account sat over its usage limit: both a
|
|
40
|
+
developer and a reviewer died with `claude exited 1 with is_error=true: success`. That line is
|
|
41
|
+
two layers' verdicts concatenated and nothing reconciling them — our `is_error` beside the
|
|
42
|
+
provider's own `subtype`, which was the literal `"success"` — and the ONLY human-readable word
|
|
43
|
+
on it was the one that was wrong. Tomorrow's reader learns the story failed; the truth is it
|
|
44
|
+
never ran, which is exactly the direction §7 forbids an audit record to lie in. `describe()`
|
|
45
|
+
is reached only for a FAILURE, so a subtype asserting the turn succeeded cannot be that
|
|
46
|
+
failure's reason: it is now refused as a reason and reported as the contradiction it is —
|
|
47
|
+
`claude exited 1 with is_error=true: no reason named (the provider's own subtype said
|
|
48
|
+
"success")` — keeping BOTH facts rather than dropping either, because a record that hides the
|
|
49
|
+
disagreement is only marginally better than one that resolves it wrongly. A failure with no
|
|
50
|
+
reason anywhere now says `no reason named` instead of trailing off after the exit code. A
|
|
51
|
+
reason the provider DID name (`Reached maximum budget ($0.26)`) and a genuine error subtype
|
|
52
|
+
(`error_during_execution`) are printed exactly as before. **This does not classify the limit
|
|
53
|
+
and does not change any retry**: what `subtype`, `errors[]` or HTTP status either provider
|
|
54
|
+
emits on a quota limit is still unmeasured, and a detector built on a guessed signal is a
|
|
55
|
+
detector that never fires — so the record is fixed today and the cause stays honestly unnamed
|
|
56
|
+
until the raw document of a rate-limited turn is captured.
|
|
57
|
+
|
|
3
58
|
## 0.22.0 — 2026-09-14
|
|
4
59
|
|
|
5
60
|
### Fixed
|
package/README.md
CHANGED
|
@@ -335,6 +335,7 @@ back on the registry is 0.3.0.
|
|
|
335
335
|
|
|
336
336
|
| Version | Date | Status | Contains |
|
|
337
337
|
|---|---|---|---|
|
|
338
|
+
| 0.23.0 | 2026-09-14 | `beta` | `tldrx story reopen --as-is` now settles a story whose work is already merged into the epic but whose review never completed (`n-a` or `error` on its last recorded merge): a new, named review-only case that merges nothing and only routes the story to its reviewer, read from an additive ledger field; the existing refusal ("no commit the epic has not got") is untouched and now names the standing verdict when one exists (`changes`, `approve`, `fixlist` still refuse). A fix list whose findings are all deferred settles the story `done` instead of spawning a developer with nothing to fix, measured on a live run that paid for four such developers (#295). And a turn that died on a provider limit no longer records `success` borrowed from the provider's own subtype: the failure record says what the host saw (#296, first half — DETECTING the provider limit as its own non-execution kind is #298 and is NOT in this release). Minor release: `story reopen --as-is` behaves differently in a situation that exists today. |
|
|
338
339
|
| 0.22.0 | 2026-09-14 | `beta` | A developer may now READ its own tree — `git status`, `log`, `diff` and `show` join the one constant the grant, the developer prompt and the refusal classifier all read; measured on a live unattended run where a developer was refused `git -C <worktree> log` twice and the story died, ~$5 for a command that changes nothing. `-C <path>` (and `--git-dir`, `--work-tree`) stays ungranted as a decision with its own refusal kind, `elsewhere`, because it points git at trees the story does not own. And every refusal cure now says WHY it is a cure: three consecutive developers on one story re-appended `; echo "EXIT:$?"` to a DoD command because the cure said what to drop and never that the facilitator re-runs the Definition of Done itself and records each exit code (#287, #294). Minor release: the developer's git allowance grew and a new refusal kind was added. |
|
|
339
340
|
| 0.21.0 | 2026-09-13 | `beta` | `tldrx budget raise <phase> <usd> --stage <id>` now moves the stage's own `budget_usd` — the figure that actually sets a developer's and a reviewer's spawn ceiling — instead of the phase figure, which caps no spawn at all: measured on a live unattended run where raising the plan's per-story price and the phase ceiling moved a developer's cap by nothing, and only raising the stage figure moved it; a raise naming no `--stage` now says outright that it moved no spawn ceiling. And a reviewer a nearly-exhausted stage cannot fund is refused before it is spawned — the run that surfaced this handed a reviewer **$0.43**, which died before reading a line of diff and recorded `verdict: error`, parking the story with its dependents blocked; the refusal now costs $0 and records `verdict: n-a`, not an error the reviewer never formed (#244, #289). Minor release: a new flag, `--stage`, on `budget raise`. |
|
|
340
341
|
| 0.20.0 | 2026-09-13 | `beta` | `tldrx seed check <file\|dir>` runs a hand-written seed through the same importer chain `run new --seed` runs, plus the authoring rules four unattended runs paid to learn — bullets under 200 characters, `[src:]` citations that resolve, `dod` lines byte-equal to a declared command with no shell separator, `depends_on` wherever two stories touch one file, a `Recommended:` on every open question — and prints the budget split and per-story caps a run would set, creating no run and spending nothing; `tldrx install --claude` now writes a second managed skill, `tldrx-plan`, that reads `workspace.yml` and the repo tree and turns "I want X" into seeds that pass the same check, naming only declared tools (#291). And a permission refusal over a command the workspace's `commands:` could have granted now names the operator's cure on the first dead developer instead of the fourth, measured on a live unattended run where four developers died and two review rounds ran before the missing command turned out to be one `workspace.yml` line away (#285). Minor release: a new CLI verb, `tldrx seed check`. |
|
package/dist/tldrx.js
CHANGED
|
@@ -16449,7 +16449,7 @@ var ENTRIES = [
|
|
|
16449
16449
|
{
|
|
16450
16450
|
name: "as-is",
|
|
16451
16451
|
arg: null,
|
|
16452
|
-
meaning: "Settle the story from its BRANCH AS IT STANDS: the next Build turn runs the dod, the review and the merge over the commits already on `story/<run>/<id>`, and spawns NO developer. For a story a person finished by hand — a rebase after a conflict, a fix typed directly on the branch — which the plain verb cannot land, because a developer handed finished work has nothing to do and the story blocks on the no-work rule. It is not a shortcut past a gate: a red dod blocks the story, and so does a branch carrying no commit its epic has not already got. Requires --note; refused together with --for-fix.",
|
|
16452
|
+
meaning: "Settle the story from its BRANCH AS IT STANDS: the next Build turn runs the dod, the review and the merge over the commits already on `story/<run>/<id>`, and spawns NO developer. For a story a person finished by hand — a rebase after a conflict, a fix typed directly on the branch — which the plain verb cannot land, because a developer handed finished work has nothing to do and the story blocks on the no-work rule. It is not a shortcut past a gate: a red dod blocks the story, and so does a branch carrying no commit its epic has not already got — with one named exception: a story whose work is ALREADY on the epic from an earlier turn and whose review never completed (the reviewer died, or the attempt after the merge died with no work) is settled review-only: nothing is merged, the dod runs on the epic head and the reviewer judges the range the story was merged as; a review that stands (`approve`, `changes`, `fixlist`) still refuses. Requires --note; refused together with --for-fix.",
|
|
16453
16453
|
sub: "reopen"
|
|
16454
16454
|
},
|
|
16455
16455
|
runFlag(),
|
|
@@ -20652,10 +20652,17 @@ function describe4(exitCode, doc, stderr, timedOut, stdout, provider) {
|
|
|
20652
20652
|
return `${name} exited ${exitCode} without a parseable result event: ${tail}`;
|
|
20653
20653
|
}
|
|
20654
20654
|
const errors2 = Array.isArray(doc.errors) ? doc.errors.filter((e) => typeof e === "string") : [];
|
|
20655
|
-
const
|
|
20656
|
-
const
|
|
20657
|
-
|
|
20658
|
-
}
|
|
20655
|
+
const verdict = `${name} exited ${exitCode} with is_error=${String(doc.is_error === true)}`;
|
|
20656
|
+
const named = typeof errors2[0] === "string" && errors2[0] !== "" ? errors2[0] : "";
|
|
20657
|
+
if (named !== "")
|
|
20658
|
+
return `${verdict}: ${named}`;
|
|
20659
|
+
const subtype = typeof doc.subtype === "string" ? doc.subtype : "";
|
|
20660
|
+
if (subtype !== "" && !SUCCESS_SUBTYPES.has(subtype))
|
|
20661
|
+
return `${verdict}: ${subtype}`;
|
|
20662
|
+
const contradiction = subtype === "" ? "" : ` (the provider's own subtype said "${subtype}")`;
|
|
20663
|
+
return `${verdict}: no reason named${contradiction}`;
|
|
20664
|
+
}
|
|
20665
|
+
var SUCCESS_SUBTYPES = new Set(["success"]);
|
|
20659
20666
|
function firstLine3(text2) {
|
|
20660
20667
|
const line = text2.split(`
|
|
20661
20668
|
`).map((l) => l.trim()).find((l) => l !== "");
|
|
@@ -25937,6 +25944,7 @@ function carriedFindings(findings2) {
|
|
|
25937
25944
|
return findings2.filter((f) => f.disposition === "defer-with-log" && !(f.resolved && f.resolvedSha !== null));
|
|
25938
25945
|
}
|
|
25939
25946
|
var CLAIMED_UNVERIFIED = "claimed-unverified";
|
|
25947
|
+
var FIXLIST_SETTLED_MARK = "every finding is routed away from `fix-now`, so the story is done without a fix round";
|
|
25940
25948
|
function parseFixFindings(value) {
|
|
25941
25949
|
const problems = [];
|
|
25942
25950
|
const format = [];
|
|
@@ -26452,8 +26460,16 @@ function reviewStillOwed(outcome) {
|
|
|
26452
26460
|
}
|
|
26453
26461
|
var AS_IS_MARK = "the branch was taken AS IT STANDS — no developer was spawned for it";
|
|
26454
26462
|
var AS_IS_NOT_AHEAD_MARK = "nothing to take as it stands";
|
|
26455
|
-
|
|
26456
|
-
|
|
26463
|
+
var AS_IS_JUDGED_MARK = "the last review of that work stands";
|
|
26464
|
+
var AS_IS_REVIEW_ONLY_MARK = "the review was run AS IT STANDS — nothing was merged and no developer was spawned";
|
|
26465
|
+
function asIsNotAheadReason(branch, epicBranch, ahead, judged) {
|
|
26466
|
+
if (ahead === null) {
|
|
26467
|
+
return `${AS_IS_NOT_AHEAD_MARK}: git could not count what \`${branch}\` carries beyond ` + `\`${epicBranch}\`, and a settlement that spawns no developer will not merge on a ` + "measurement it does not have. Nothing was merged";
|
|
26468
|
+
}
|
|
26469
|
+
return `${AS_IS_NOT_AHEAD_MARK}: \`${branch}\` carries no commit \`${epicBranch}\` has not already ` + "got, so there is no work to settle" + (judged === undefined ? "" : `, and ${AS_IS_JUDGED_MARK} — \`${judged.commit.slice(0, 7)}\` is on \`${epicBranch}\` and its ` + `review said \`${judged.verdict}\`, so the review is not what is owed`) + ". Commit the fix to the story branch first, then reopen it --as-is. Nothing was merged";
|
|
26470
|
+
}
|
|
26471
|
+
function reviewNeverCompleted(verdict) {
|
|
26472
|
+
return verdict === "n-a" || verdict === "error";
|
|
26457
26473
|
}
|
|
26458
26474
|
function scopedNote(result2) {
|
|
26459
26475
|
if (result2.scope !== "paths" || result2.rendered === undefined)
|
|
@@ -29008,7 +29024,7 @@ function renderReviewLog(outcome) {
|
|
|
29008
29024
|
`- Repo: \`${outcome.repo}\` · wave ${outcome.wave} · epic ${outcome.epic}`,
|
|
29009
29025
|
`- Branch: \`${outcome.branch}\` → \`${outcome.epicBranch}\` (${mergeWord(outcome)})`,
|
|
29010
29026
|
`- Commit: ${outcome.commit ?? "(none)"}`,
|
|
29011
|
-
...outcome.asIs == null ? [] : [`- Developer: **none** — ${AS_IS_MARK}. \`${outcome.branch}\` was settled as it stands, ` + `signed by ${outcome.asIs.actor}: ${outcome.asIs.note}. The Definition of Done below and ` + "the reviewer above judged that branch, unchanged."],
|
|
29027
|
+
...outcome.asIs == null ? [] : outcome.asIs.reason === "review-only" ? [`- Developer: **none** — ${AS_IS_REVIEW_ONLY_MARK}. \`${outcome.asIs.reviewed?.commit ?? outcome.commit ?? "(none)"}\` ` + `was already on \`${outcome.epicBranch}\` from an earlier turn and nothing had judged it; ` + `signed by ${outcome.asIs.actor}: ${outcome.asIs.note}. The Definition of Done below ran on the ` + "epic head, and the reviewer above judged the range that merge was recorded as."] : [`- Developer: **none** — ${AS_IS_MARK}. \`${outcome.branch}\` was settled as it stands, ` + `signed by ${outcome.asIs.actor}: ${outcome.asIs.note}. The Definition of Done below and ` + "the reviewer above judged that branch, unchanged."],
|
|
29012
29028
|
...outcome.developerError === null ? [] : [`- Developer: **FAILED** — ${outcome.developerError}`],
|
|
29013
29029
|
...outcome.permissionRefused == null ? [] : [withCure(`- Developer: \`${outcome.permissionRefused}\` was refused for approval by the agent's own ` + "permission layer; the tree held committed work, so the Definition of Done below decided", outcome.permissionRefused, outcome.declaredCommands)],
|
|
29014
29030
|
...outcome.budgetDeath == null ? [] : [`- Developer: died on its per-story cap — ${outcome.budgetDeath}; the tree held work, ` + "so the Definition of Done below decided"],
|
|
@@ -36610,6 +36626,7 @@ function readReviewLedger(runDir, storyId) {
|
|
|
36610
36626
|
blockedWithNothingRun: false,
|
|
36611
36627
|
reopened: null,
|
|
36612
36628
|
asIs: null,
|
|
36629
|
+
lastMerge: null,
|
|
36613
36630
|
fixRound: null,
|
|
36614
36631
|
formatRetries: 0,
|
|
36615
36632
|
formatRefusal: null,
|
|
@@ -36631,6 +36648,7 @@ function readReviewLedger(runDir, storyId) {
|
|
|
36631
36648
|
let lastDodOutputPath = null;
|
|
36632
36649
|
let reopened = null;
|
|
36633
36650
|
let asIs = null;
|
|
36651
|
+
let lastMerge = null;
|
|
36634
36652
|
let fixRound = null;
|
|
36635
36653
|
let formatRetries = 0;
|
|
36636
36654
|
let formatRefusal = null;
|
|
@@ -36698,6 +36716,13 @@ function readReviewLedger(runDir, storyId) {
|
|
|
36698
36716
|
commit2 = payload.commit;
|
|
36699
36717
|
if (typeof payload.epic_base === "string" && payload.epic_base !== "")
|
|
36700
36718
|
epicBase = payload.epic_base;
|
|
36719
|
+
if (typeof payload.commit === "string" && payload.commit !== "" && typeof payload.epic_base === "string" && payload.epic_base !== "") {
|
|
36720
|
+
lastMerge = {
|
|
36721
|
+
commit: payload.commit,
|
|
36722
|
+
epicBase: payload.epic_base,
|
|
36723
|
+
verdict: typeof payload.verdict === "string" && payload.verdict !== "" ? payload.verdict : "n-a"
|
|
36724
|
+
};
|
|
36725
|
+
}
|
|
36701
36726
|
if (payload.status === "done")
|
|
36702
36727
|
fixRound = null;
|
|
36703
36728
|
asIs = null;
|
|
@@ -36761,6 +36786,7 @@ function readReviewLedger(runDir, storyId) {
|
|
|
36761
36786
|
blockedWithNothingRun,
|
|
36762
36787
|
reopened,
|
|
36763
36788
|
asIs,
|
|
36789
|
+
lastMerge,
|
|
36764
36790
|
fixRound,
|
|
36765
36791
|
formatRetries,
|
|
36766
36792
|
formatRefusal,
|
|
@@ -39430,7 +39456,22 @@ class BuildSession {
|
|
|
39430
39456
|
};
|
|
39431
39457
|
}
|
|
39432
39458
|
const ahead = await commitsBetween(story.repoDir, story.epicBranch, story.branch);
|
|
39433
|
-
if (ahead ===
|
|
39459
|
+
if (ahead === 0) {
|
|
39460
|
+
const merge = readReviewLedger(this.ctx.runDir, id).lastMerge;
|
|
39461
|
+
if (merge !== null && reviewNeverCompleted(merge.verdict)) {
|
|
39462
|
+
return await this.reviewOnlyHalf(planned, asIs, story, before, merge);
|
|
39463
|
+
}
|
|
39464
|
+
return {
|
|
39465
|
+
story,
|
|
39466
|
+
cost: 0,
|
|
39467
|
+
dod: [],
|
|
39468
|
+
commit: null,
|
|
39469
|
+
failure: asIsNotAheadReason(story.branch, story.epicBranch, ahead, merge === null ? undefined : merge),
|
|
39470
|
+
developerError: null,
|
|
39471
|
+
before
|
|
39472
|
+
};
|
|
39473
|
+
}
|
|
39474
|
+
if (ahead === null) {
|
|
39434
39475
|
return {
|
|
39435
39476
|
story,
|
|
39436
39477
|
cost: 0,
|
|
@@ -39468,6 +39509,27 @@ class BuildSession {
|
|
|
39468
39509
|
}
|
|
39469
39510
|
return { story, cost: 0, dod, commit: commit2, failure: null, developerError: null, before };
|
|
39470
39511
|
}
|
|
39512
|
+
async reviewOnlyHalf(planned, asIs, story, before, merge) {
|
|
39513
|
+
const id = planned.story.id;
|
|
39514
|
+
const reviewed = { commit: merge.commit, epicBase: merge.epicBase };
|
|
39515
|
+
this.asIsSettlements.set(id, { ...asIs, reason: "review-only", reviewed });
|
|
39516
|
+
this.lines.push(` · ${id}: ${AS_IS_REVIEW_ONLY_MARK} — \`${merge.commit.slice(0, 7)}\` is already on ` + `\`${story.epicBranch}\` and nothing judged it; running the dod on the epic head and the review ` + `over \`${reviewDiffRange(merge.epicBase, story.epicBranch, story.branch)}\``);
|
|
39517
|
+
await this.writes.run(() => this.refreshStoryBase(planned, story.repoDir, story.worktree, story.branch, story.epicBranch));
|
|
39518
|
+
const dod = await this.runDod(story);
|
|
39519
|
+
if (!this.dodProves(dod)) {
|
|
39520
|
+
const failing = dodFailure(dod);
|
|
39521
|
+
return {
|
|
39522
|
+
story,
|
|
39523
|
+
cost: 0,
|
|
39524
|
+
dod,
|
|
39525
|
+
commit: null,
|
|
39526
|
+
failure: failing === undefined ? "the story declares no dod commands, so nothing could prove it" : dodFailureReason(failing, planned.story.repo),
|
|
39527
|
+
developerError: null,
|
|
39528
|
+
before
|
|
39529
|
+
};
|
|
39530
|
+
}
|
|
39531
|
+
return { story, cost: 0, dod, commit: merge.commit, failure: null, developerError: null, before, reviewOnly: reviewed };
|
|
39532
|
+
}
|
|
39471
39533
|
async buildHalf(planned) {
|
|
39472
39534
|
const asIs = this.asIsFor(planned);
|
|
39473
39535
|
if (asIs !== null)
|
|
@@ -39606,6 +39668,10 @@ class BuildSession {
|
|
|
39606
39668
|
await this.block(story, "the story produced no commit to review", half.cost, dod);
|
|
39607
39669
|
return "settled";
|
|
39608
39670
|
}
|
|
39671
|
+
if (half.reviewOnly !== undefined) {
|
|
39672
|
+
this.noteMerged(story, null);
|
|
39673
|
+
return await this.reviewAndSettle(story, dod, half.reviewOnly.commit, half.cost, null, half.reviewOnly.epicBase);
|
|
39674
|
+
}
|
|
39609
39675
|
const update = await updateStoryBase({
|
|
39610
39676
|
storyId: story.planned.story.id,
|
|
39611
39677
|
repoDir: story.repoDir,
|
|
@@ -39690,6 +39756,21 @@ class BuildSession {
|
|
|
39690
39756
|
if (review.verdict === "fixlist") {
|
|
39691
39757
|
const rel = this.writeFixlistFor(story, review, commit2, epicBase);
|
|
39692
39758
|
const open2 = openFindings(review.fixlist).length;
|
|
39759
|
+
if (open2 === 0) {
|
|
39760
|
+
this.lines.push(` · ${story.planned.story.id}: ${FIXLIST_SETTLED_MARK} (${rel})`);
|
|
39761
|
+
await this.settle(story, "done", {
|
|
39762
|
+
dod,
|
|
39763
|
+
commit: commit2,
|
|
39764
|
+
merged: true,
|
|
39765
|
+
carried,
|
|
39766
|
+
epicBase,
|
|
39767
|
+
verdict: "fixlist",
|
|
39768
|
+
review,
|
|
39769
|
+
cost,
|
|
39770
|
+
reason: null
|
|
39771
|
+
});
|
|
39772
|
+
return "settled";
|
|
39773
|
+
}
|
|
39693
39774
|
await this.settle(story, "review", {
|
|
39694
39775
|
dod,
|
|
39695
39776
|
commit: commit2,
|
|
@@ -40492,12 +40573,13 @@ class BuildSession {
|
|
|
40492
40573
|
...outcome.permissionRefused == null ? {} : { permission_refused: outcome.permissionRefused },
|
|
40493
40574
|
...outcome.budgetDeath == null ? {} : { budget_death: outcome.budgetDeath },
|
|
40494
40575
|
...outcome.reviewerUnfunded == null ? {} : { reviewer_unfunded: outcome.reviewerUnfunded },
|
|
40495
|
-
...outcome.asIs == null ? {} : { as_is: true, as_is_by: outcome.asIs.actor, as_is_note: outcome.asIs.note }
|
|
40576
|
+
...outcome.asIs == null ? {} : { as_is: true, as_is_by: outcome.asIs.actor, as_is_note: outcome.asIs.note },
|
|
40577
|
+
...outcome.asIs?.reason === undefined ? {} : { as_is_reason: outcome.asIs.reason }
|
|
40496
40578
|
});
|
|
40497
40579
|
if (pruning && (rescued === null || rescued.worktree === null)) {
|
|
40498
40580
|
await removeWorktree(story.repoDir, story.worktree);
|
|
40499
40581
|
}
|
|
40500
|
-
this.lines.push(` ${status === "done" ? "✓" : "·"} ${id} → \`${status}\`` + (outcome.reason === null ? "" : ` (${outcome.reason})`) + (outcome.asIs == null ? "" : ` — ${AS_IS_MARK};
|
|
40582
|
+
this.lines.push(` ${status === "done" ? "✓" : "·"} ${id} → \`${status}\`` + (outcome.reason === null ? "" : ` (${outcome.reason})`) + (outcome.asIs == null ? "" : ` — ${outcome.asIs.reason === "review-only" ? AS_IS_REVIEW_ONLY_MARK : AS_IS_MARK}; ` + `${outcome.asIs.actor} signed it`) + (outcome.permissionRefused == null ? "" : ` — ${withCure(`\`${outcome.permissionRefused}\` was refused for approval; the tree held committed work, so the DoD decided`, outcome.permissionRefused, outcome.declaredCommands)}`));
|
|
40501
40583
|
}
|
|
40502
40584
|
writeLog(outcome) {
|
|
40503
40585
|
const dir = join99(this.ctx.runDir, BUILD_PHASE, LOG_DIR);
|
|
@@ -49275,6 +49357,7 @@ function reopenStory(options) {
|
|
|
49275
49357
|
` signed by ${options.actor}: ${options.note}`,
|
|
49276
49358
|
" no developer will be spawned: the next Build turn runs the dod, the review and the merge " + "over the commits already on the story branch",
|
|
49277
49359
|
" it is not a shortcut past either gate — a red dod BLOCKS the story, and so does a branch " + "that carries no commit its epic has not already got; the refusal says which",
|
|
49360
|
+
" one named exception: work that is ALREADY on the epic with a review that never completed is " + "settled review-only — nothing merged, the dod on the epic head, the reviewer over the recorded range",
|
|
49278
49361
|
" the record will say the branch was taken as it stands and name you, so nothing reads as " + "though a developer delivered it",
|
|
49279
49362
|
...kept
|
|
49280
49363
|
]
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tldr-experts",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.23.0",
|
|
4
4
|
"description": "tldr-experts: an evidence-first, file-based AI development framework - five stages, a gate on every one, and every claim cited or refused. Installs the `tldrx` (and `tldr-experts`) command. Beta.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Alan Martinez",
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"$doc": "Shape verified from https://code.claude.com/docs/en/plugins.md (Quickstart > Create the plugin manifest). Fields used here: name, description, version, author.name. Only plugin.json goes inside .claude-plugin/; skills/, agents/ and hooks/ live at the plugin root.",
|
|
3
3
|
"name": "tldrx",
|
|
4
4
|
"description": "tldr-experts: an evidence-first, file-based AI development framework. Five stages, a gate on every one, every claim cited or refused. Beta.",
|
|
5
|
-
"version": "0.
|
|
5
|
+
"version": "0.23.0",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Alan Martinez"
|
|
8
8
|
}
|