tldr-experts 0.18.3 → 0.19.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 +36 -0
- package/README.md +1 -0
- package/dist/tldrx.js +144 -7
- package/package.json +1 -1
- package/plugin/.claude-plugin/plugin.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,41 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.19.0 — 2026-09-13
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- **`tldrx story reopen <id> --as-is --note "…"` settles a story a person finished BY HAND
|
|
8
|
+
from its branch as it stands, with no developer spawned (#279).** Measured on a real
|
|
9
|
+
unattended run at 0.18.2: S2's merge into its epic hit the #268 conflict, a person rebased
|
|
10
|
+
the branch by hand (DoD green, `git merge-tree` clean), and then there was no way to get that
|
|
11
|
+
branch merged. `story reopen` hands the story to a developer; the developer had nothing to
|
|
12
|
+
do; #271's rule — work is measured SINCE THE SPAWN — blocked the story after one attempt,
|
|
13
|
+
correctly, because work that already exists, older than the spawn, is indistinguishable from
|
|
14
|
+
a developer that did nothing. A second reopen buys the same outcome, so the only way out was
|
|
15
|
+
to invent a commit for the developer to make. This is the case that rule could not see, added
|
|
16
|
+
beside it and not carved out of it: `--as-is` spawns nothing at all, so there is no spawn to
|
|
17
|
+
measure work since. Everything after the missing developer is the path every other story
|
|
18
|
+
takes — the epic is merged in if it moved (#268), the DoD runs in the story's worktree, the
|
|
19
|
+
same reviewer judges the same diff, the same merge lands it. The worktree is found BY NAME
|
|
20
|
+
from the branch and reopened when the build had removed it, which is the ordinary case: a
|
|
21
|
+
blocked story's tree is pruned as it settles, and the hand fix is typically made in a scratch
|
|
22
|
+
worktree that is then removed. Two refusals are STRUCTURAL and no flag skips either, because
|
|
23
|
+
a verb that merges a branch nobody's agent wrote is exactly where a `--force` gets added at
|
|
24
|
+
3 a.m. to unstick a run: the branch must carry at least one commit its epic has not already
|
|
25
|
+
got (`commitsBetween`, whose `null` stays "could not count" and refuses rather than reading
|
|
26
|
+
as "did not move", #273), and the DoD must go green under the same `dodProves` every other
|
|
27
|
+
story faces — the story blocks with the reason naming which. And the record does not lie
|
|
28
|
+
about who did the work (§7): `story.reopened` carries `reason: as_is` with the actor and the
|
|
29
|
+
note, `task.done` carries `as_is`/`as_is_by`/`as_is_note`, the review log's developer line
|
|
30
|
+
opens **none** and names the branch it took and the person who signed for it, and no task row
|
|
31
|
+
is written for a turn nothing was spawned for. The signature is cleared the moment a real
|
|
32
|
+
developer attempt starts, so a story requeued after a `changes` verdict over a hand-finished
|
|
33
|
+
branch records a DEVELOPER and not an as-is — found by review before it shipped, and pinned.
|
|
34
|
+
`--for-fix` and `--as-is` together are a usage error rather than a resolution in favour of
|
|
35
|
+
one: they answer different questions, and quietly performing the other is the CLI reporting
|
|
36
|
+
success for work it did not do. `tldrx next --prepare` refuses an `--as-is` story rather than
|
|
37
|
+
dispatching a developer bundle for it.
|
|
38
|
+
|
|
3
39
|
## 0.18.3 — 2026-09-13
|
|
4
40
|
|
|
5
41
|
### Fixed
|
package/README.md
CHANGED
|
@@ -331,6 +331,7 @@ back on the registry is 0.3.0.
|
|
|
331
331
|
|
|
332
332
|
| Version | Date | Status | Contains |
|
|
333
333
|
|---|---|---|---|
|
|
334
|
+
| 0.19.0 | 2026-09-13 | `beta` | `tldrx story reopen <id> --as-is --note "…"` settles a story a person finished by hand from its branch as it stands, spawning no developer — measured on a live unattended run where a hand-rebased branch had a green DoD and a clean merge-tree but no way to land, blocked by #271's since-the-spawn rule from a developer with nothing to do (#279). Everything after the missing developer is the ordinary path: the epic merges in first if it moved, the same DoD and reviewer judge the diff, the same merge lands it. The record never lies about who did the work — `story.reopened` carries `reason: as_is` with the actor and note, `task.done` carries `as_is`/`as_is_by`/`as_is_note`, and the review log names the branch and the signer. Two refusals are structural with no flag to skip either: a branch with no commit ahead of its epic, and a red DoD. The signature clears the moment a real developer attempt starts, so a requeued story after a `changes` verdict records a developer, not an as-is. Minor release: a new flag, `--as-is`, on `story reopen`. |
|
|
334
335
|
| 0.18.3 | 2026-09-13 | `beta` | four fixes to the unattended build path, all measured on live runs: a headless permission refusal is now classified — a chained shell command, a git verb outside the developer's allowance, or neither — so a chained line gets one retry with the cure stated first, an ungranted verb names its granted equivalent, and the developer prompt now lists the git verbs it holds from the one constant the grant is built from, after six refusals in one day left a ledger that recorded the symptom and not the cause; a wave no longer merges a story into its epic from the base it was cut from once a sibling has moved that epic — the story is measured against the epic tip immediately before merge, the epic is merged into the story and its Definition of Done re-run only when the epic actually moved, and a conflict blocks the story naming the files rather than merging half of one; a seed bullet over the claim cap no longer has its citation cut in half, with the clip moving back to the citation's start instead; and a failed commit count is no longer read as zero, so `--discard-pending` can no longer delete a plan just because git could not answer. |
|
|
335
336
|
| 0.18.2 | 2026-09-13 | `beta` | three things measured on the first fully unattended run and on the live runs that followed it, one of them a regression 0.18.1 shipped hours earlier: `--ship merge` armed GitHub's auto-merge over a base that required nothing — the guard counted the checks the PR REPORTED while `--auto` waits on the base's REQUIREMENTS, so a proof run's pull request merged at 06:20:14 while all four of its checks finished between 06:21:38 and 06:24:56, and the record said `queued — GitHub merges when its checks pass`; ship now asks the base what it requires through rulesets and classic protection, names its three absences apart, and every unreadable answer arms nothing; the plan's per-story price had become a hard wall at 0.8x the planner's guess once 0.18.1 made those fields actually read, killing stories mid-flight at $1.28 and $3.25 against real costs an order of magnitude higher, so the ceiling is now `max(price x 3, $4.00)` derived at DISPATCH from the budget as it sits on disk — a run already in flight is covered without migrating anything — and, the half that carries the expensive end, a developer that dies on its cap with work in its tree commits it and lets the Definition of Done decide instead of parking the story, the same authority #271 gave a refused one; and the zero-touch recipe is written down where a person looks for it, seed file to backgrounded loop, on the README quick start and both documentation sites, with the honest boundary that this is the happy path without a person |
|
|
336
337
|
| 0.18.1 | 2026-09-13 | `beta` | five things a run could not survive on its own, every one measured on the unattended proof runs that followed 0.18.0 rather than reasoned about: a priced plan was never priced — the planner wrote `stories[].estimate_usd` and Build read only `per_phase_usd` + `spent_usd`, so every story got the same $5.40 cap while the plan said $28, and the contract now names the shape and the plan check refuses a budget.yml that does not carry it; a headless refusal under a host-tokens ceiling wrote nothing to the ledger, so `--until-done` relaunched over a refusal it could not see, and now writes its `budget.blocked` first; a developer that had committed its work and was then refused a compound verification command was blocked BEFORE the facilitator ran the Definition of Done, so the DoD now decides when there is work and the refusal is recorded either way, with the prompt saying to run each DoD command verbatim and alone because shell separators split a line into subcommands that each need their own grant; a workspace can declare `<slot>_scoped` DoD commands that run per story on the paths it touched with the full suite once per epic head, after the full suite was measured running three times per story attempt; and a cancelled run no longer leaves an epic branch that refuses the ordinary retry — `run cancel` releases it and Build renames aside an epic whose owner run is closed, while an open or unknown owner still refuses verbatim and an uncounted branch is never deleted |
|
package/dist/tldrx.js
CHANGED
|
@@ -16427,11 +16427,18 @@ var ENTRIES = [
|
|
|
16427
16427
|
meaning: "Open a FIX ROUND on a story that is `done`: one named defect in work a reviewer already approved. No attempt is consumed, the fix passes the same dod and the same reviewer as the original, and the story's acceptance criteria are not touched — it is not a way to relitigate scope. Refused when the story is not done, when --note is missing, and when that story already has a fix round open (the bound is one).",
|
|
16428
16428
|
sub: "reopen"
|
|
16429
16429
|
},
|
|
16430
|
+
{
|
|
16431
|
+
name: "as-is",
|
|
16432
|
+
arg: null,
|
|
16433
|
+
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.",
|
|
16434
|
+
sub: "reopen"
|
|
16435
|
+
},
|
|
16430
16436
|
runFlag(),
|
|
16431
16437
|
root()
|
|
16432
16438
|
],
|
|
16433
16439
|
examples: [
|
|
16434
16440
|
'tldrx story reopen S3 --note "it gates wave 3 (S4, S6) and the owner has decided it ships"',
|
|
16441
|
+
'tldrx story reopen S2 --as-is --note "rebased onto the epic by hand after the merge conflict; dod green"',
|
|
16435
16442
|
'tldrx story reopen S11 --for-fix --note "linkEmail succeeds then setDisplayName fails: account linked, score never claimable"',
|
|
16436
16443
|
'tldrx story widen S3 platform/Auth.cs --note "the tenancy check the story is for lives here too"'
|
|
16437
16444
|
],
|
|
@@ -16442,6 +16449,7 @@ var ENTRIES = [
|
|
|
16442
16449
|
"The story goes back to `todo` and its attempt counter restarts at 1 of 2. Nothing is erased to make that true: `story.reopened` is a reset boundary the review ledger reads, every earlier attempt stays in events.jsonl, and the event records how many verdicts the closed run consumed.",
|
|
16443
16450
|
"It runs no agent, spends nothing, deletes nothing and refunds nothing. The story's branch is kept — that is what carries the last developer's commits forward — and its worktree is left exactly as the build left it, to be reopened from the branch if the build had removed it.",
|
|
16444
16451
|
'It does NOT make the stage runnable. If the Build stage is at its gate, `tldrx reject --note "…"` sends it back to `ready` first; if the gate is already signed, `tldrx reject --stage` takes that back.',
|
|
16452
|
+
"`--as-is` is the other arc the plain verb cannot cover: a story a PERSON finished. `reopen` hands the story to a developer, and a developer handed a branch whose work is already done has nothing to commit — the story then blocks on the rule that measures work SINCE THE SPAWN, because work older than the spawn cannot be told apart from a developer that did nothing. `--as-is` spawns nothing at all, so there is nothing to measure: the dod runs in the story's worktree (reopened from the branch when the build had removed it), the reviewer judges the same diff, and the merge into the epic is the same merge. It records `story.reopened` with `reason: as_is`, and the story's row and review log say the branch was taken as it stands and name who signed that — no record claims a developer delivered it.",
|
|
16445
16453
|
"`widen` adds paths to a story's `touches:` — the sanctioned form of the advice the boundary decision card gives when a Build stage changed a path nobody scoped. Widenable states are `todo`, `in_progress`, `review` and `blocked`. A `done` story refuses: its evidence was written against the surface it DECLARED, and widening it afterwards would make the record say the plan declared a path it did not — reopen it with `--for-fix` first. A path the story already declares refuses too, because a widening that widened nothing is a record of something that did not happen.",
|
|
16446
16454
|
"`widen` runs no agent, spends nothing, consumes no attempt, changes no status and moves no cursor — it declares scope and nothing else. No gate code knows about it: the boundary condition re-reads `touches:` off disk at evaluation time, so the same run, the same branch and the same diff simply stop counting the widened path as outside the surface at the next evaluation. One `story.touches_widened` records the paths, the note and the list before and after."
|
|
16447
16455
|
]
|
|
@@ -26402,6 +26410,11 @@ function dodRefused(result2) {
|
|
|
26402
26410
|
}
|
|
26403
26411
|
var DOD_REFUSAL_FALLBACK = "the gate declined to run it";
|
|
26404
26412
|
var DEVELOPER_FAILED = "the developer sub-agent failed";
|
|
26413
|
+
var AS_IS_MARK = "the branch was taken AS IT STANDS — no developer was spawned for it";
|
|
26414
|
+
var AS_IS_NOT_AHEAD_MARK = "nothing to take as it stands";
|
|
26415
|
+
function asIsNotAheadReason(branch, epicBranch, ahead) {
|
|
26416
|
+
return ahead === null ? `${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" : `${AS_IS_NOT_AHEAD_MARK}: \`${branch}\` carries no commit \`${epicBranch}\` has not already ` + "got, so there is no work to settle. Commit the fix to the story branch first, then reopen it " + "--as-is. Nothing was merged";
|
|
26417
|
+
}
|
|
26405
26418
|
function scopedNote(result2) {
|
|
26406
26419
|
if (result2.scope !== "paths" || result2.rendered === undefined)
|
|
26407
26420
|
return "";
|
|
@@ -28877,6 +28890,7 @@ function renderReviewLog(outcome) {
|
|
|
28877
28890
|
`- Repo: \`${outcome.repo}\` · wave ${outcome.wave} · epic ${outcome.epic}`,
|
|
28878
28891
|
`- Branch: \`${outcome.branch}\` → \`${outcome.epicBranch}\` (${mergeWord(outcome)})`,
|
|
28879
28892
|
`- Commit: ${outcome.commit ?? "(none)"}`,
|
|
28893
|
+
...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."],
|
|
28880
28894
|
...outcome.developerError === null ? [] : [`- Developer: **FAILED** — ${outcome.developerError}`],
|
|
28881
28895
|
...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)],
|
|
28882
28896
|
...outcome.budgetDeath == null ? [] : [`- Developer: died on its per-story cap — ${outcome.budgetDeath}; the tree held work, ` + "so the Definition of Done below decided"],
|
|
@@ -36457,6 +36471,7 @@ function readReviewLedger(runDir, storyId) {
|
|
|
36457
36471
|
developerErroredWith: null,
|
|
36458
36472
|
blockedWithNothingRun: false,
|
|
36459
36473
|
reopened: null,
|
|
36474
|
+
asIs: null,
|
|
36460
36475
|
fixRound: null,
|
|
36461
36476
|
formatRetries: 0,
|
|
36462
36477
|
formatRefusal: null,
|
|
@@ -36477,6 +36492,7 @@ function readReviewLedger(runDir, storyId) {
|
|
|
36477
36492
|
let current = [];
|
|
36478
36493
|
let lastDodOutputPath = null;
|
|
36479
36494
|
let reopened = null;
|
|
36495
|
+
let asIs = null;
|
|
36480
36496
|
let fixRound = null;
|
|
36481
36497
|
let formatRetries = 0;
|
|
36482
36498
|
let formatRefusal = null;
|
|
@@ -36514,6 +36530,7 @@ function readReviewLedger(runDir, storyId) {
|
|
|
36514
36530
|
};
|
|
36515
36531
|
if (payload.reason === "fix")
|
|
36516
36532
|
fixRound = reopened;
|
|
36533
|
+
asIs = payload.reason === "as_is" ? reopened : null;
|
|
36517
36534
|
formatRetries = 0;
|
|
36518
36535
|
formatRefusal = null;
|
|
36519
36536
|
spawned = null;
|
|
@@ -36545,6 +36562,7 @@ function readReviewLedger(runDir, storyId) {
|
|
|
36545
36562
|
epicBase = payload.epic_base;
|
|
36546
36563
|
if (payload.status === "done")
|
|
36547
36564
|
fixRound = null;
|
|
36565
|
+
asIs = null;
|
|
36548
36566
|
blockedWithNothingRun = payload.status === "blocked" && payload.verdict === "n-a" && (payload.commit === null || payload.commit === undefined || payload.commit === "") && !ranACheck && !sawReviewer;
|
|
36549
36567
|
}
|
|
36550
36568
|
if (event4.type !== "check.passed" && event4.type !== "check.failed")
|
|
@@ -36604,6 +36622,7 @@ function readReviewLedger(runDir, storyId) {
|
|
|
36604
36622
|
developerErroredWith,
|
|
36605
36623
|
blockedWithNothingRun,
|
|
36606
36624
|
reopened,
|
|
36625
|
+
asIs,
|
|
36607
36626
|
fixRound,
|
|
36608
36627
|
formatRetries,
|
|
36609
36628
|
formatRefusal,
|
|
@@ -38835,6 +38854,7 @@ class BuildSession {
|
|
|
38835
38854
|
outcomes = new Map;
|
|
38836
38855
|
refusals = new Map;
|
|
38837
38856
|
capDeaths = new Map;
|
|
38857
|
+
asIsSettlements = new Map;
|
|
38838
38858
|
epics = new EpicState;
|
|
38839
38859
|
lines = [];
|
|
38840
38860
|
counters = new ReviewCounters;
|
|
@@ -38917,6 +38937,10 @@ class BuildSession {
|
|
|
38917
38937
|
const review = this.reviewWorkFor(planned);
|
|
38918
38938
|
if (review !== null)
|
|
38919
38939
|
return await this.prepareReview(planned, review);
|
|
38940
|
+
const asIs = this.asIsFor(planned);
|
|
38941
|
+
if (asIs !== null) {
|
|
38942
|
+
return refusedOnSequence(this.ctx, `${planned.story.id} was reopened \`--as-is\` by ${asIs.actor} (${asIs.note}) — the branch is to be ` + "settled as it stands, and `--prepare` dispatches a developer. `tldrx next` (headless) runs the " + "dod, the review and the merge over it; to build it with a developer instead, " + `\`tldrx story reopen ${planned.story.id} --note "…"\` replaces the signature.`);
|
|
38943
|
+
}
|
|
38920
38944
|
let fixlist;
|
|
38921
38945
|
try {
|
|
38922
38946
|
fixlist = this.fixlistFor(planned.story.id);
|
|
@@ -39225,10 +39249,88 @@ class BuildSession {
|
|
|
39225
39249
|
throw rejected.reason;
|
|
39226
39250
|
return halves;
|
|
39227
39251
|
}
|
|
39252
|
+
asIsFor(planned) {
|
|
39253
|
+
return readReviewLedger(this.ctx.runDir, planned.story.id).asIs;
|
|
39254
|
+
}
|
|
39255
|
+
async asIsHalf(planned, asIs) {
|
|
39256
|
+
const id = planned.story.id;
|
|
39257
|
+
const before = this.statusOf(planned);
|
|
39258
|
+
this.refusals.delete(id);
|
|
39259
|
+
this.capDeaths.delete(id);
|
|
39260
|
+
this.asIsSettlements.set(id, asIs);
|
|
39261
|
+
const story = await this.writes.run(() => this.openStory(planned));
|
|
39262
|
+
await this.writes.run(() => {
|
|
39263
|
+
this.ctx.emit("task.started", {
|
|
39264
|
+
phase: this.ctx.phaseId,
|
|
39265
|
+
story: id,
|
|
39266
|
+
wave: planned.wave,
|
|
39267
|
+
repo: planned.story.repo,
|
|
39268
|
+
branch: story.branch,
|
|
39269
|
+
attempt: story.attempt,
|
|
39270
|
+
as_is: true
|
|
39271
|
+
});
|
|
39272
|
+
this.setStoryStatus(planned, "in_progress");
|
|
39273
|
+
});
|
|
39274
|
+
this.lines.push(` · ${id}: settling \`${story.branch}\` as it stands — signed by ${asIs.actor}: ${asIs.note}`);
|
|
39275
|
+
const install = await this.installDeps(story);
|
|
39276
|
+
if (install !== null && installFailed(install)) {
|
|
39277
|
+
return {
|
|
39278
|
+
story,
|
|
39279
|
+
cost: 0,
|
|
39280
|
+
dod: [],
|
|
39281
|
+
commit: null,
|
|
39282
|
+
failure: installFailureReason(install, planned.story.repo),
|
|
39283
|
+
developerError: null,
|
|
39284
|
+
before
|
|
39285
|
+
};
|
|
39286
|
+
}
|
|
39287
|
+
const ahead = await commitsBetween(story.repoDir, story.epicBranch, story.branch);
|
|
39288
|
+
if (ahead === null || ahead === 0) {
|
|
39289
|
+
return {
|
|
39290
|
+
story,
|
|
39291
|
+
cost: 0,
|
|
39292
|
+
dod: [],
|
|
39293
|
+
commit: null,
|
|
39294
|
+
failure: asIsNotAheadReason(story.branch, story.epicBranch, ahead),
|
|
39295
|
+
developerError: null,
|
|
39296
|
+
before
|
|
39297
|
+
};
|
|
39298
|
+
}
|
|
39299
|
+
const dod = await this.runDod(story);
|
|
39300
|
+
if (!this.dodProves(dod)) {
|
|
39301
|
+
const failing = dodFailure(dod);
|
|
39302
|
+
return {
|
|
39303
|
+
story,
|
|
39304
|
+
cost: 0,
|
|
39305
|
+
dod,
|
|
39306
|
+
commit: null,
|
|
39307
|
+
failure: failing === undefined ? "the story declares no dod commands, so nothing could prove it" : dodFailureReason(failing, planned.story.repo),
|
|
39308
|
+
developerError: null,
|
|
39309
|
+
before
|
|
39310
|
+
};
|
|
39311
|
+
}
|
|
39312
|
+
const commit2 = await headSha(story.worktree);
|
|
39313
|
+
if (commit2 === "") {
|
|
39314
|
+
return {
|
|
39315
|
+
story,
|
|
39316
|
+
cost: 0,
|
|
39317
|
+
dod,
|
|
39318
|
+
commit: null,
|
|
39319
|
+
failure: `\`${story.branch}\` has no HEAD to take — git could not resolve it`,
|
|
39320
|
+
developerError: null,
|
|
39321
|
+
before
|
|
39322
|
+
};
|
|
39323
|
+
}
|
|
39324
|
+
return { story, cost: 0, dod, commit: commit2, failure: null, developerError: null, before };
|
|
39325
|
+
}
|
|
39228
39326
|
async buildHalf(planned) {
|
|
39327
|
+
const asIs = this.asIsFor(planned);
|
|
39328
|
+
if (asIs !== null)
|
|
39329
|
+
return await this.asIsHalf(planned, asIs);
|
|
39229
39330
|
const before = this.statusOf(planned);
|
|
39230
39331
|
this.refusals.delete(planned.story.id);
|
|
39231
39332
|
this.capDeaths.delete(planned.story.id);
|
|
39333
|
+
this.asIsSettlements.delete(planned.story.id);
|
|
39232
39334
|
const story = await this.writes.run(() => this.openStory(planned, true));
|
|
39233
39335
|
await this.writes.run(() => {
|
|
39234
39336
|
this.ctx.emit("task.started", {
|
|
@@ -40168,6 +40270,7 @@ class BuildSession {
|
|
|
40168
40270
|
reason: parts.reason,
|
|
40169
40271
|
permissionRefused: this.refusals.get(id) ?? null,
|
|
40170
40272
|
budgetDeath: this.capDeaths.get(id) ?? null,
|
|
40273
|
+
asIs: this.asIsSettlements.get(id) ?? null,
|
|
40171
40274
|
rescued,
|
|
40172
40275
|
cost_usd: parts.cost
|
|
40173
40276
|
};
|
|
@@ -40198,12 +40301,13 @@ class BuildSession {
|
|
|
40198
40301
|
attempt: story.attempt,
|
|
40199
40302
|
...parts.epicBase === null || parts.epicBase === undefined || parts.epicBase === "" ? {} : { epic_base: parts.epicBase },
|
|
40200
40303
|
...outcome.permissionRefused == null ? {} : { permission_refused: outcome.permissionRefused },
|
|
40201
|
-
...outcome.budgetDeath == null ? {} : { budget_death: outcome.budgetDeath }
|
|
40304
|
+
...outcome.budgetDeath == null ? {} : { budget_death: outcome.budgetDeath },
|
|
40305
|
+
...outcome.asIs == null ? {} : { as_is: true, as_is_by: outcome.asIs.actor, as_is_note: outcome.asIs.note }
|
|
40202
40306
|
});
|
|
40203
40307
|
if (pruning && (rescued === null || rescued.worktree === null)) {
|
|
40204
40308
|
await removeWorktree(story.repoDir, story.worktree);
|
|
40205
40309
|
}
|
|
40206
|
-
this.lines.push(` ${status === "done" ? "✓" : "·"} ${id} → \`${status}\`` + (outcome.reason === null ? "" : ` (${outcome.reason})`) + (outcome.permissionRefused == null ? "" : ` — ${withCure(`\`${outcome.permissionRefused}\` was refused for approval; the tree held committed work, so the DoD decided`, outcome.permissionRefused)}`));
|
|
40310
|
+
this.lines.push(` ${status === "done" ? "✓" : "·"} ${id} → \`${status}\`` + (outcome.reason === null ? "" : ` (${outcome.reason})`) + (outcome.asIs == null ? "" : ` — ${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)}`));
|
|
40207
40311
|
}
|
|
40208
40312
|
writeLog(outcome) {
|
|
40209
40313
|
const dir = join100(this.ctx.runDir, BUILD_PHASE, LOG_DIR);
|
|
@@ -48330,8 +48434,13 @@ function reopenStory(options) {
|
|
|
48330
48434
|
return refuse4(['story reopen needs a story id: `tldrx story reopen S3 --note "why"`']);
|
|
48331
48435
|
}
|
|
48332
48436
|
const forFix = options.forFix === true;
|
|
48437
|
+
const asIs = options.asIs === true;
|
|
48333
48438
|
if (options.note.trim() === "") {
|
|
48334
|
-
return
|
|
48439
|
+
return asIs ? refuse4([
|
|
48440
|
+
`story reopen --as-is needs --note: \`tldrx story reopen ${id} --as-is --note "<what you did by hand>"\``,
|
|
48441
|
+
" the note is the ONLY record of who finished this branch and how — no developer will run,",
|
|
48442
|
+
" so nothing else in the log will say where the commits came from"
|
|
48443
|
+
]) : forFix ? refuse4([
|
|
48335
48444
|
`story reopen --for-fix needs --note: \`tldrx story reopen ${id} --for-fix --note "<the defect>"\``,
|
|
48336
48445
|
" the note IS the defect — it is what scopes the fix round, and what the reviewer reads",
|
|
48337
48446
|
" to tell a fix from a second opinion about the story"
|
|
@@ -48425,7 +48534,7 @@ function reopenStory(options) {
|
|
|
48425
48534
|
}
|
|
48426
48535
|
throw error;
|
|
48427
48536
|
}
|
|
48428
|
-
const event7 = reopenEvent(options, store.runId, id, row2.wave, row2.status, ledger4.verdicts, forFix);
|
|
48537
|
+
const event7 = reopenEvent(options, store.runId, id, row2.wave, row2.status, ledger4.verdicts, forFix, asIs);
|
|
48429
48538
|
const validation = validateEvent(event7);
|
|
48430
48539
|
if (!validation.ok) {
|
|
48431
48540
|
const first2 = validation.issues[0];
|
|
@@ -48456,6 +48565,19 @@ function reopenStory(options) {
|
|
|
48456
48565
|
]
|
|
48457
48566
|
};
|
|
48458
48567
|
}
|
|
48568
|
+
if (asIs) {
|
|
48569
|
+
return {
|
|
48570
|
+
code: EXIT_OK12,
|
|
48571
|
+
lines: [
|
|
48572
|
+
`reopened ${id} in ${store.runId} — \`${row2.status}\` → \`${REOPENED_TO}\` (${row2.wave}), ` + "to be settled from its branch AS IT STANDS",
|
|
48573
|
+
` signed by ${options.actor}: ${options.note}`,
|
|
48574
|
+
" 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",
|
|
48575
|
+
" 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",
|
|
48576
|
+
" the record will say the branch was taken as it stands and name you, so nothing reads as " + "though a developer delivered it",
|
|
48577
|
+
...kept
|
|
48578
|
+
]
|
|
48579
|
+
};
|
|
48580
|
+
}
|
|
48459
48581
|
return {
|
|
48460
48582
|
code: EXIT_OK12,
|
|
48461
48583
|
lines: [
|
|
@@ -48481,7 +48603,7 @@ function nextStep(runId, stage2) {
|
|
|
48481
48603
|
function buildStage(store) {
|
|
48482
48604
|
return store.run.phases.find((phase) => phase.id === BUILD_PHASE2)?.stages[0];
|
|
48483
48605
|
}
|
|
48484
|
-
function reopenEvent(options, runId, storyId, wave, from, verdicts, forFix) {
|
|
48606
|
+
function reopenEvent(options, runId, storyId, wave, from, verdicts, forFix, asIs) {
|
|
48485
48607
|
return {
|
|
48486
48608
|
ts: options.at,
|
|
48487
48609
|
run: runId,
|
|
@@ -48496,7 +48618,7 @@ function reopenEvent(options, runId, storyId, wave, from, verdicts, forFix) {
|
|
|
48496
48618
|
from_status: from,
|
|
48497
48619
|
to_status: REOPENED_TO,
|
|
48498
48620
|
verdicts,
|
|
48499
|
-
reason: forFix ? "fix" : "attempts",
|
|
48621
|
+
reason: forFix ? "fix" : asIs ? "as_is" : "attempts",
|
|
48500
48622
|
note: options.note
|
|
48501
48623
|
}
|
|
48502
48624
|
};
|
|
@@ -48703,7 +48825,7 @@ var VALUE_FLAGS6 = ["run", "root", "note"];
|
|
|
48703
48825
|
var storyCommand = {
|
|
48704
48826
|
name: "story",
|
|
48705
48827
|
summary: "Give one Build story another run of attempts, or widen the paths it declares",
|
|
48706
|
-
usage: `tldrx story reopen <id> --note <text> [--for-fix] [--run <id>] [--root <path>]
|
|
48828
|
+
usage: `tldrx story reopen <id> --note <text> [--for-fix | --as-is] [--run <id>] [--root <path>]
|
|
48707
48829
|
` + " tldrx story widen <id> <path>… --note <text> [--run <id>] [--root <path>]",
|
|
48708
48830
|
implemented: true,
|
|
48709
48831
|
async run(argv) {
|
|
@@ -48721,11 +48843,20 @@ ${storyCommand.usage}
|
|
|
48721
48843
|
function storyReopen(argv) {
|
|
48722
48844
|
try {
|
|
48723
48845
|
const args = parseArgs(argv, VALUE_FLAGS6);
|
|
48846
|
+
if (boolFlag(args, "for-fix") && boolFlag(args, "as-is")) {
|
|
48847
|
+
process.stderr.write(`tldrx story reopen: --for-fix and --as-is answer different questions — nothing was written
|
|
48848
|
+
` + " --for-fix opens a fix round on a `done` story: one named defect, landed by a developer.\n" + ` --as-is settles an UNFINISHED story from its branch as it stands, with no developer at all.
|
|
48849
|
+
` + ` Type the one you mean.
|
|
48850
|
+
` + `${storyCommand.usage}
|
|
48851
|
+
`);
|
|
48852
|
+
return EXIT_USAGE;
|
|
48853
|
+
}
|
|
48724
48854
|
const outcome = reopenStory({
|
|
48725
48855
|
root: workspaceRootFrom(args),
|
|
48726
48856
|
storyId: args.positionals[0] ?? "",
|
|
48727
48857
|
note: stringFlag(args, "note") ?? "",
|
|
48728
48858
|
forFix: boolFlag(args, "for-fix"),
|
|
48859
|
+
asIs: boolFlag(args, "as-is"),
|
|
48729
48860
|
runId: stringFlag(args, "run"),
|
|
48730
48861
|
actor: currentActor(),
|
|
48731
48862
|
at: nowRfc3339()
|
|
@@ -48745,6 +48876,12 @@ function storyReopen(argv) {
|
|
|
48745
48876
|
function storyWiden(argv) {
|
|
48746
48877
|
try {
|
|
48747
48878
|
const args = parseArgs(argv, VALUE_FLAGS6);
|
|
48879
|
+
if (boolFlag(args, "as-is")) {
|
|
48880
|
+
process.stderr.write("tldrx story widen: --as-is is a flag of `reopen`, not of `widen` — nothing was written\n" + " widening declares a path; `--as-is` settles a story from its branch as it stands.\n" + ` \`tldrx story reopen <id> --as-is --note "<what you did by hand>"\` is the verb you want.
|
|
48881
|
+
` + `${storyCommand.usage}
|
|
48882
|
+
`);
|
|
48883
|
+
return EXIT_USAGE;
|
|
48884
|
+
}
|
|
48748
48885
|
if (boolFlag(args, "for-fix")) {
|
|
48749
48886
|
process.stderr.write("tldrx story widen: --for-fix is a flag of `reopen`, not of `widen` — nothing was written\n" + " widening declares a path; `--for-fix` reopens finished work for one named defect.\n" + ` \`tldrx story reopen <id> --for-fix --note "<the defect>"\` first, then widen the reopened story.
|
|
48750
48887
|
` + `${storyCommand.usage}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tldr-experts",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.19.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.19.0",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Alan Martinez"
|
|
8
8
|
}
|