tldr-experts 0.26.0 → 0.26.1
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 +88 -0
- package/README.md +1 -0
- package/dist/tldrx.js +69 -2
- package/package.json +1 -1
- package/plugin/.claude-plugin/plugin.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,93 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.26.1 — 2026-09-14
|
|
4
|
+
|
|
5
|
+
### Changed
|
|
6
|
+
|
|
7
|
+
- **A patch is fixes again, and this release is one: 0.26.1, not 0.27.0 (owner decision).**
|
|
8
|
+
MEASURED with `git tag -l`: v0.19.0 through v0.26.0 are eight releases, every one a minor and
|
|
9
|
+
none a patch, while 0.21.0, 0.22.0, 0.23.0, 0.25.0 and 0.26.0 carry nothing but `### Fixed`.
|
|
10
|
+
Mechanism: `docs/RELEASING.md` said "bump minor when a command, file schema or hook changes
|
|
11
|
+
behaviour; patch for fixes only", and every fix changes some command's behaviour — so the
|
|
12
|
+
rule had no fix left to call a patch, and the version number stopped telling an upgrader
|
|
13
|
+
whether a release was safe to take blind. The rule now reads by capability: a *patch* is
|
|
14
|
+
fixes, including a fix that makes a command do what it already promised (a refusal where it
|
|
15
|
+
should have refused, a message that keeps what it should say); a *minor* is a new
|
|
16
|
+
capability — a new command, flag, file field or schema, hook or event, or a changed meaning
|
|
17
|
+
of an existing one. The maintain skill used to gloss the old rule beside its citation, which
|
|
18
|
+
made two copies to move; it now cites `docs/RELEASING.md` "What to consider" only, and
|
|
19
|
+
`test/maintain-skill.test.ts` refuses a skill line that restates what a patch or a minor is
|
|
20
|
+
(RED on the old gloss, which it quoted).
|
|
21
|
+
|
|
22
|
+
### Fixed
|
|
23
|
+
|
|
24
|
+
- **A Build fix round whose developer lands no diff is refused, not settled `done` (#308).**
|
|
25
|
+
MEASURED live on a client workspace (0.24.0, two `--for-fix` reopens, one Build re-run): the
|
|
26
|
+
developer read the file, said it "already satisfies every acceptance criterion", spent $1.69
|
|
27
|
+
across two spawns and changed nothing — and the stage closed with the story `done`, the epic
|
|
28
|
+
head unchanged and every gap the reopen note named still in the tree. Mechanism, confirmed at
|
|
29
|
+
`f1c9aa8`: `commitIfDirty` hands back the OLD head sha on a clean tree, so `settleHalf`'s "no
|
|
30
|
+
commit to review" gate never fired, `mergeNoFf` was a no-op on an already-merged tip, and the
|
|
31
|
+
ledger closes a fix round on `done` alone. `workSince` — the "did the tree move since the one
|
|
32
|
+
the developer was handed" comparison the two refusal branches already use — was never asked on
|
|
33
|
+
the success path, where a fix round is precisely the case "nothing changed and the DoD is still
|
|
34
|
+
green" looks normal, because that tree was accepted once already. Now, for a story a PERSON put
|
|
35
|
+
back with a note — a fix round or a plain reopen, off the ledger — `buildHalf` asks it BEFORE
|
|
36
|
+
the DoD: no work, and the attempt blocks with the existing `task.done`/`blocked` vocabulary and
|
|
37
|
+
a reason that carries the note's first line (`NO_DIFF_MARK`, exported and pinned) — no DoD paid
|
|
38
|
+
for, no reviewer asked to judge an empty diff, the fix round still open because nothing landed.
|
|
39
|
+
A first attempt that lands nothing is deliberately NOT gated: "added nothing — identical to the
|
|
40
|
+
epic" is a pinned rendering decision, not this defect. Three RED tests in
|
|
41
|
+
`test/story-reopen.test.ts` (no-write developer on a fix round, the same bytes rewritten, a
|
|
42
|
+
no-write developer on a plain reopen — each settled `done` before); the neighbouring fix-round
|
|
43
|
+
tests now land a REAL diff, because the fake developer's default rewrote the bytes the first
|
|
44
|
+
attempt committed, which is #308's shape exactly. Golden byte-identical.
|
|
45
|
+
- **`tldrx ship` refuses an epic carrying a story the reviewer rejected (#282).** MEASURED on a
|
|
46
|
+
live unattended run (0.18.2, 8 stories), story S5: DoD green, `48f8bdd merge(S5)` on the epic,
|
|
47
|
+
THEN `check.failed check:review verdict:changes`; the requeue did the same (`7fd2468 merge(S5)`,
|
|
48
|
+
`changes` again) and the story settled `blocked` — the epic carried, twice, code the reviewer
|
|
49
|
+
rejected twice. Merging BEFORE the review is design (#166: the reviewer reads the merged diff from
|
|
50
|
+
the epic base) and every verdict settles with `merged: true`; the defect was downstream. `ship`'s
|
|
51
|
+
only story refusal was #210's "zero stories done", so with one `done` story beside S5 it pushed
|
|
52
|
+
the epic and opened the PR with the rejected code in its diff, while the body listed S5 under
|
|
53
|
+
"Not done" with no word that the diff was there — a record lying in the dangerous direction (§7).
|
|
54
|
+
Reproduced at `bdc43e7` with the fake agent (`GOLDEN_ROUNDS`, S2 scripted `changes` twice): S1
|
|
55
|
+
`done`, S2 `blocked`, every S2 `task.done` commit an ancestor of `epic/e1`, and `ship --dry-run`
|
|
56
|
+
exited 0 printing the `gh pr create` line. Now `ship` asks the ledger's `lastMerge` (#295 — the
|
|
57
|
+
last `task.done` carrying `commit` and `epic_base`, which survives a reopen precisely so "what
|
|
58
|
+
does the epic hold" can be asked) for every story not `done`, and a `changes` there is a
|
|
59
|
+
rejection that stands: exit 2 — a gate DID say no, unlike #210's 1 — naming the story, the merge
|
|
60
|
+
and the handoff's own reason, with #210's remedy (`tldrx story reopen <id> --note`, re-run Build,
|
|
61
|
+
or open the PR by hand). Not widened to a merged diff nobody judged (`n-a`/`error`) — that is #311,
|
|
62
|
+
filed from this change. Guards, all passing before: a `changes` a later attempt merged over and got approved
|
|
63
|
+
ships; a story whose diff never merged ships; a story parked at `review` under a SIGNED fix list
|
|
64
|
+
ships with its findings listed. Two mutations of the predicate each redden a different test.
|
|
65
|
+
Golden byte-identical: nothing in Build changed. The unattended path was already closed one
|
|
66
|
+
door earlier — the auto gate refuses to self-sign over any story not `done`
|
|
67
|
+
(`autoGate.ts` `UNFINISHED_STORIES`, pinned in `test/build-executor.test.ts`) — so this reaches
|
|
68
|
+
a PR only after a person approved the Build gate, which is exactly when the record has to say
|
|
69
|
+
what the diff contains.
|
|
70
|
+
|
|
71
|
+
- **A stage-failure line keeps the branch it exists to name when the worktree path is wider than
|
|
72
|
+
the line (#310, #293).** MEASURED 2026-09-14 under a ~115-char session scratchpad, the very place
|
|
73
|
+
`AGENTS.md §2` sends every agent to work: the epic-worktree refusal put the absolute path FIRST
|
|
74
|
+
and the two branch names after it, `oneLine` kept the first 220 chars, and the operator line —
|
|
75
|
+
and `run.yml`'s task row, and the `stage.failed` event — read `epic worktree /private/tmp/…/
|
|
76
|
+
_epic-260829-build-E1 is checked out on \`epic/someb…`: the branch cut two characters in, while
|
|
77
|
+
the same test passed on the same sha under a short `TMPDIR`. A refusal that loses its subject
|
|
78
|
+
exactly when the path is long is a refusal that names the wrong thing when it matters most.
|
|
79
|
+
Two halves, one implementation each: `assertWorktreeOn` now says the branches first and the
|
|
80
|
+
path last (`… is checked out on \`X\`, not \`Y\` — refusing … — at <path>`), and `oneLine`'s
|
|
81
|
+
cap keeps a line's head AND its tail, losing the middle — so what a cut takes is the middle of
|
|
82
|
+
a directory whose root the operator already knows. The cap itself stays at 220: each of the
|
|
83
|
+
three records is still one bounded line. `test/build-executor.test.ts` builds the refusal under
|
|
84
|
+
a path wider than the cap on purpose (the fixture roots in `os.tmpdir()`, which reads
|
|
85
|
+
`TMPDIR` at call time) and pins both branches, the worktree's own directory and the bound in
|
|
86
|
+
all three records — RED on the old order (only the path and `…` survived), and each half
|
|
87
|
+
reverted alone reddens it on its own assertion; the unit test on `assertWorktreeOn` pins the
|
|
88
|
+
order. The existing WRONG-branch test asserts the worktree's relative directory rather than
|
|
89
|
+
the whole absolute path, so it no longer depends on how long the box's `TMPDIR` is.
|
|
90
|
+
|
|
3
91
|
## 0.26.0 — 2026-09-14
|
|
4
92
|
|
|
5
93
|
### 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.26.1 | 2026-09-14 | `beta` | Three refusals that stop a bad state from settling quietly. A Build fix round whose developer lands no diff is refused before the DoD instead of closing `done` on an unmoved tree, because `commitIfDirty` hands back the old head sha on a clean commit and the existing "no commit to review" gate never fires for it — `workSince` is now asked on the success path too, for any story a person put back with a note. `tldrx ship` refuses an epic carrying a story the reviewer marked `changes`: it asks the ledger's `lastMerge` for every story not `done` and exits 2 naming the story, the merge and the handoff's reason, instead of opening a PR whose "Not done" list hid rejected code already merged into the diff. And a stage-failure line keeps the branch it names when the worktree path is wider than the line: the refusal puts the branches before the path, and `oneLine` keeps a line's head and tail instead of dropping the identifiers that sat at the end. |
|
|
338
339
|
| 0.26.0 | 2026-09-14 | `beta` | Two field defects from the same class — a reader of an event payload trusting a field its writer never set — plus the ceiling that killed the turn that earned it. A wide story's measured `touches_widened` no longer throws after the turn has settled: the cap knows the three lists by name and drops them whole by count, every reader derives before/after through one function, and an executor that throws after paid turns leaves their rows and money in `run.yml` and saves before it emits. The cost surfaces then agree with each other: a turn whose result was lost to that fault reads as UNMETERED, LOWER BOUND on `tldrx cost` and in the Build handoff through the door other unmeasured turns already use, a turn still in flight reads as nothing, and a late result retires the lost mark instead of counting twice. Replay reads the reason a failed stage recorded instead of printing "no reason recorded" for every one. |
|
|
339
340
|
| 0.25.0 | 2026-09-14 | `beta` | The other half of the two-session protocol and four holes measured on live runs. `scripts/release.sh` now waits on a running wave's lock before touching the tree and never hands back the marker it wrote — release has precedence, the wave yields, exit 14 when it gives up; the heading gate also refuses an unreleased version at or below `package.json`; the marker carries its phase and `--status` reports a release queued behind a wave (#304). In-session `tldrx next --prepare` and `--commit` ask the same dependency frontier the headless loop asks: a terminal hold records the dependent `blocked`, a pending hold refuses without writing, a held `--commit` keeps the work it sits on and says where it is (#300). A Watch card refused for an unsourced `## Where` item comes back to its writer marked, the refusal names the cure, and the brief shows one non-file example, so a retry edits the card instead of regenerating it blind (#301). And `run.yml` no longer loses an external write to a live loop: a save carries only the fields the store changed over a fresh read, ownership is declared, `cancelled` is terminal and wins in the file and in the process, a file that cannot be merged is kept beside as `.bak` and said so, and a file recording another run is refused (#305). Minor release: a new exit code on a public script, a stricter merge gate, and `--prepare`/`--commit` refuse where they used to proceed. |
|
|
340
341
|
| 0.24.0 | 2026-09-14 | `beta` | Five fixes from one unattended night, all measured on live runs. The two-session protocol is mechanical where it was chat: the merge wave refuses a merged tree with two unreleased CHANGELOG headings or one at or below the last release (exit 12), `release.sh` holds a `.RELEASE-IN-PROGRESS` marker the wave waits on (exit 13 when it gives up), `merge-wave.sh --status` reads holder, branch and phase in one line, and the `maintain` skill gains a worker-mode section (#299). A per-story cap says what it was derived from — plan price × stage scale — and names the lever that moves it, and a plan whose prices exceed the stage budget is told so instead of being scaled in silence (#281). A story whose dependency is still at `review` waits in `todo` instead of being written `blocked`, and a dependency block left by an earlier version is released once the dependency is `done` (#280). `--until-done` compares the refusal across attempts, not the advice line printed under it, so a failure that made progress keeps its relaunches (#297). And the Build handoff no longer fails its own `claim-sources` check over a refused command that spans lines: one physical line per element, the break shown as `⏎` and explained once in the document (#283). Minor release: the wave and `--until-done` behave differently in situations that exist today. |
|
package/dist/tldrx.js
CHANGED
|
@@ -16573,6 +16573,7 @@ var ENTRIES = [
|
|
|
16573
16573
|
"It pushes ONLY when the run says so. Publishing a branch is a decision, so by default a branch the remote has not seen is a refusal that names the `git push` command rather than running it. The decision is taken once, at `tldrx run new --ship <push|pr|merge>` (#253): then this verb pushes the epic first, through the one push wrapper in the codebase (the Build phase itself still has none, spec §5), and under `merge` arms `gh pr merge --auto --merge` after the PR opens — unless the PR reports NO check at all, or the BASE branch was not seen to require one (#274: `--auto` waits on the base's requirements, not on the PR's reported checks, so over a base that requires nothing it merges at once), in which case the PR is left open and run.yml says which absence it was. `run auto` calls this verb when it sees the run close and skips a run whose record already carries `shipped_at`; typing `tldrx ship` again is the recovery after a partial failure — a repo whose merge is recorded `failed` is armed again, one recorded `queued` is left alone, and the record is the union, so a recorded failure is never erased. A run without the block is never written.",
|
|
16574
16574
|
"The body is WRITTEN for a PR (#167): an **Outcome** line saying how much of the plan landed (`partial: 1 of 3 stories delivered; S2 — …`); what shipped and what did not, from the handoff's own done/not-done split; the reviewer findings still open, read from the run's fix lists; and the LAST phase handoff the run has on disk — `04-build/handoff.md` on a run that built something — verbatim and complete, inside a `<details>` block. It goes to `gh` as a file, never as an argument, so a long body cannot overflow an argv limit.",
|
|
16575
16575
|
"It REFUSES a run that delivered no story (exit 1 — nothing behind it, #210). `ship` already knew: the body it renders keeps only the handoff bullets that say `done`, and with none it wrote `(nothing settled `done` in this run)` into a PR it opened anyway. The refusal names the counts and the first blocked story's own reason, and `--dry-run` is refused in the same words. A run that delivered at least one story ships exactly as it did before.",
|
|
16576
|
+
"It REFUSES an epic carrying a story the reviewer REJECTED (exit 2 — a gate said no, #282). A story merges into its epic BEFORE its review, by design, so a `changes` verdict leaves the diff on the branch: with one story `done` beside it the PR opened with the rejected code in its diff and a body that listed the story under \"Not done\" without saying so. The refusal reads the ledger's last merge for every story not `done` — the last `task.done` carrying `commit` and `epic_base` — and names the story, that merge and the handoff's own reason. A `changes` a later attempt merged over and got approved is not a rejection, a story whose diff never merged has nothing to refuse, and a story parked at `review` under a SIGNED fix list ships with its findings listed. `--dry-run` is refused in the same words.",
|
|
16576
16577
|
"It moves no gate and no cursor and appends no event. On a run with no `ship:` block it never writes to the run at all; on a run with one it writes exactly that block's record (`pr_urls`, `merge`, `merges`, `shipped_at`) and nothing else. To mirror the plan's epics and stories to a ticket tool, `tldrx tickets sync` is the verb that does that, and it stays separate.",
|
|
16577
16578
|
"It refuses cleanly, in a sentence, when there is no epic branch, no handoff, no remote, no `gh` on PATH, or when several epic branches leave the choice open.",
|
|
16578
16579
|
"When the branch exists in SEVERAL repos — the normal shape of a chained multi-repo run, whose epics share one integration branch — it opens one PR per repo: the same body, the repo name in the title, and every URL listed at the end. `--repo` narrows it to one.",
|
|
@@ -25908,7 +25909,7 @@ async function assertWorktreeOn(path, branch, what = "worktree") {
|
|
|
25908
25909
|
const on = await currentBranch(path);
|
|
25909
25910
|
if (on === branch)
|
|
25910
25911
|
return;
|
|
25911
|
-
throw new WorktreeBranchMismatchError(`${what}
|
|
25912
|
+
throw new WorktreeBranchMismatchError(`${what} is checked out on \`${on === "" ? "(no branch)" : on}\`, not \`${branch}\` — ` + `refusing to touch it, because a merge here would land on another run's branch — at ${path}`);
|
|
25912
25913
|
}
|
|
25913
25914
|
async function shaReachability(cwd, sha, ref) {
|
|
25914
25915
|
const resolved = await git(["rev-parse", "--verify", "--quiet", `${sha}^{commit}`], cwd);
|
|
@@ -26572,6 +26573,11 @@ var AS_IS_MARK = "the branch was taken AS IT STANDS — no developer was spawned
|
|
|
26572
26573
|
var AS_IS_NOT_AHEAD_MARK = "nothing to take as it stands";
|
|
26573
26574
|
var AS_IS_JUDGED_MARK = "the last review of that work stands";
|
|
26574
26575
|
var AS_IS_REVIEW_ONLY_MARK = "the review was run AS IT STANDS — nothing was merged and no developer was spawned";
|
|
26576
|
+
var NO_DIFF_MARK = "the developer produced no diff";
|
|
26577
|
+
function noDiffAfterReopenReason(parts) {
|
|
26578
|
+
const round4 = parts.fix ? "the fix round" : "the reopen";
|
|
26579
|
+
return `${NO_DIFF_MARK}: the tree is the one it was handed (\`${parts.handed.slice(0, 7)}\`), ` + `and ${round4} ${parts.actor} signed says \`${firstLine7(parts.note)}\`. ` + "Nothing changed, so nothing could have landed it — no DoD ran and no reviewer was spawned. " + "Reopen it with a note that names what the developer has to change";
|
|
26580
|
+
}
|
|
26575
26581
|
function asIsNotAheadReason(branch, epicBranch, ahead, judged) {
|
|
26576
26582
|
if (ahead === null) {
|
|
26577
26583
|
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";
|
|
@@ -40086,6 +40092,26 @@ class BuildSession {
|
|
|
40086
40092
|
before
|
|
40087
40093
|
};
|
|
40088
40094
|
}
|
|
40095
|
+
const reopen = this.reopenFor(planned);
|
|
40096
|
+
if (reopen !== null) {
|
|
40097
|
+
const proven = await workSince({
|
|
40098
|
+
workspaceRoot: this.workspace.root,
|
|
40099
|
+
repoDir: story.repoDir,
|
|
40100
|
+
worktree: story.worktree,
|
|
40101
|
+
since: handed
|
|
40102
|
+
});
|
|
40103
|
+
if (!proven) {
|
|
40104
|
+
return {
|
|
40105
|
+
story,
|
|
40106
|
+
cost: spent,
|
|
40107
|
+
dod: [],
|
|
40108
|
+
commit: null,
|
|
40109
|
+
failure: noDiffAfterReopenReason({ handed, ...reopen }),
|
|
40110
|
+
developerError: null,
|
|
40111
|
+
before
|
|
40112
|
+
};
|
|
40113
|
+
}
|
|
40114
|
+
}
|
|
40089
40115
|
const dod = await this.runDod(story);
|
|
40090
40116
|
if (!this.dodProves(dod)) {
|
|
40091
40117
|
const failing = dodFailure(dod);
|
|
@@ -40455,6 +40481,14 @@ class BuildSession {
|
|
|
40455
40481
|
return;
|
|
40456
40482
|
this.lines.push(` · ${planned.story.id} was reopened by ${ledger4.reopened.actor} (${ledger4.reopened.note}) — ` + `the verdicts before that do not count against it, so it runs as attempt 1 of ${String(this.attempts)}`);
|
|
40457
40483
|
}
|
|
40484
|
+
reopenFor(planned) {
|
|
40485
|
+
const ledger4 = readReviewLedger(this.ctx.runDir, planned.story.id);
|
|
40486
|
+
if (ledger4.fixRound !== null)
|
|
40487
|
+
return { note: ledger4.fixRound.note, actor: ledger4.fixRound.actor, fix: true };
|
|
40488
|
+
if (ledger4.reopened !== null)
|
|
40489
|
+
return { note: ledger4.reopened.note, actor: ledger4.reopened.actor, fix: false };
|
|
40490
|
+
return null;
|
|
40491
|
+
}
|
|
40458
40492
|
waveFailed(wave) {
|
|
40459
40493
|
return wave.stories.some((planned) => this.statusOf(planned) === "blocked");
|
|
40460
40494
|
}
|
|
@@ -43531,7 +43565,10 @@ function nowish(options) {
|
|
|
43531
43565
|
function oneLine7(text3, max = 220) {
|
|
43532
43566
|
const line = text3.split(`
|
|
43533
43567
|
`)[0]?.trim() ?? "";
|
|
43534
|
-
|
|
43568
|
+
if (line.length <= max)
|
|
43569
|
+
return line;
|
|
43570
|
+
const tail2 = Math.floor((max - 1) / 2);
|
|
43571
|
+
return `${line.slice(0, max - 1 - tail2)}…${line.slice(line.length - tail2)}`;
|
|
43535
43572
|
}
|
|
43536
43573
|
function phaseMoney(store, phaseId) {
|
|
43537
43574
|
const phase = store.budget.phases.find((entry) => entry.id === phaseId);
|
|
@@ -43746,6 +43783,19 @@ async function shipRun(options) {
|
|
|
43746
43783
|
};
|
|
43747
43784
|
}
|
|
43748
43785
|
const stories = runStories(store);
|
|
43786
|
+
const rejected = rejectedOnEpic(store.runDir, stories, view);
|
|
43787
|
+
if (rejected.length > 0) {
|
|
43788
|
+
const ids = rejected.map((row2) => row2.id);
|
|
43789
|
+
return refuse2([
|
|
43790
|
+
`${store.runId} carries a story the reviewer rejected, and its diff is on \`${branch}\``,
|
|
43791
|
+
...rejected.map((row2) => ` ${row2.id} — its last merge into the epic (commit ${row2.commit.slice(0, 7)} over epic base ` + `${row2.epicBase.slice(0, 7)}) was judged \`changes\`${row2.reason === null ? "" : `: ${row2.reason}`}`),
|
|
43792
|
+
" A story merges into the epic BEFORE its review, so a `changes` verdict leaves the diff on the branch;",
|
|
43793
|
+
` a PR over \`${branch}\` would carry that code, and its body would list ${ids.join(", ")} under "Not done"`,
|
|
43794
|
+
" without saying the diff is there.",
|
|
43795
|
+
` Unblock ${ids.length === 1 ? "it" : "them"} (` + (ids.length === 1 ? `\`tldrx story reopen ${ids[0] ?? ""} --note "<why>"\`` : `\`tldrx story reopen <id> --note "<why>"\` for each`) + ") and re-run Build so a verdict that stands lands over it,",
|
|
43796
|
+
" or open the PR by hand if you mean to ship the branch as it stands."
|
|
43797
|
+
]);
|
|
43798
|
+
}
|
|
43749
43799
|
const body = writeShipBody(store, branch, handoff, stories, new Set(loadWorkspace(options.root).repos.keys()));
|
|
43750
43800
|
try {
|
|
43751
43801
|
return await shipTo(options, store, branch, body, stories);
|
|
@@ -44195,6 +44245,23 @@ function remedyPaths(refused2, excused) {
|
|
|
44195
44245
|
return `${PROJECT_WORK_DIR} ${PROJECT_FRAMEWORK_DIR}`;
|
|
44196
44246
|
return refused2.map(quote2).join(" ");
|
|
44197
44247
|
}
|
|
44248
|
+
function rejectedOnEpic(runDir2, stories, view) {
|
|
44249
|
+
const rows = [];
|
|
44250
|
+
for (const story of stories) {
|
|
44251
|
+
if (story.status === "done")
|
|
44252
|
+
continue;
|
|
44253
|
+
const merge = readReviewLedger(runDir2, story.id).lastMerge;
|
|
44254
|
+
if (merge === null || merge.verdict !== "changes")
|
|
44255
|
+
continue;
|
|
44256
|
+
rows.push({
|
|
44257
|
+
id: story.id,
|
|
44258
|
+
commit: merge.commit,
|
|
44259
|
+
epicBase: merge.epicBase,
|
|
44260
|
+
reason: view?.unfinished.find((row2) => row2.id === story.id)?.reason ?? null
|
|
44261
|
+
});
|
|
44262
|
+
}
|
|
44263
|
+
return rows;
|
|
44264
|
+
}
|
|
44198
44265
|
function settledTouches(stories) {
|
|
44199
44266
|
const rows = [];
|
|
44200
44267
|
for (const story of stories) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tldr-experts",
|
|
3
|
-
"version": "0.26.
|
|
3
|
+
"version": "0.26.1",
|
|
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.26.
|
|
5
|
+
"version": "0.26.1",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Alan Martinez"
|
|
8
8
|
}
|