@pmelab/gtd 24.2.0 → 24.4.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/.claude-plugin/plugin.json +1 -1
- package/README.md +41 -6
- package/dist/chunks/{Schema-Dh-mkwkq.mjs → Schema-B5yK7QrT.mjs} +47 -1
- package/dist/chunks/{reserved-ejn-LVYO.mjs → reserved-B9pG93Fc.mjs} +53 -13
- package/dist/chunks/{steering-D-PvRC5W.mjs → steering-DVILanVf.mjs} +16 -5
- package/dist/flows.mjs +9 -1
- package/dist/gtd.bundle.mjs +1114 -865
- package/dist/steering.mjs +2 -2
- package/dist/testkit.mjs +1135 -218
- package/dist/workflow-kit.mjs +9 -7
- package/dist/workflows/feature.mjs +152 -35
- package/dist/workflows/fix.mjs +2 -2
- package/dist/workflows/review.mjs +1 -1
- package/dist/workflows.mjs +223 -54
- package/package.json +1 -1
- package/src/cli/Cli.test.ts +57 -0
- package/src/cli/Cli.ts +89 -9
- package/src/cli/History.test.ts +225 -0
- package/src/cli/History.ts +143 -0
- package/src/cli/Output.ts +68 -0
- package/src/cli/Program.test.ts +10 -9
- package/src/cli/Program.ts +24 -72
- package/src/edge/Edge.test.ts +261 -3
- package/src/edge/Edge.ts +218 -41
- package/src/edge/index.ts +1 -0
- package/src/emit/GitScript.test.ts +27 -0
- package/src/emit/GitScript.ts +18 -2
- package/src/emit/index.ts +2 -0
- package/src/flows/scripts.ts +6 -0
- package/src/platform/Git.test.ts +6 -9
- package/src/platform/Git.ts +48 -10
- package/src/platform/RetainedHistory.test.ts +14 -0
- package/src/platform/RetainedHistory.ts +5 -0
- package/src/platform/index.ts +7 -1
- package/src/replay/Trailers.test.ts +32 -0
- package/src/replay/Trailers.ts +17 -0
- package/src/steering/FORMATS.md +1 -1
- package/src/steering/SteeringFormat.test.ts +9 -1
- package/src/steering/SteeringFormat.ts +7 -0
- package/src/steering/contract.test.ts +3 -0
- package/src/steering/index.ts +1 -0
- package/src/steering/review.test.ts +30 -0
- package/src/steering/review.ts +8 -2
- package/src/step/LandStep.ts +3 -1
- package/src/step/RepoSnapshot.ts +7 -1
- package/src/step/planStart.test.ts +6 -3
- package/src/step/planStep.test.ts +29 -1
- package/src/step/planStep.ts +8 -0
- package/src/testkit/EmittedScriptRecognizer.test.ts +29 -30
- package/src/testkit/EmittedScriptRecognizer.ts +149 -52
- package/src/testkit/EmittedScripts.test.ts +166 -0
- package/src/testkit/FakeGitOperations.ts +6 -2
- package/src/testkit/FlowScripts.test.ts +242 -0
- package/src/testkit/FlowScripts.ts +326 -0
- package/src/testkit/GitTiers.ts +269 -47
- package/src/testkit/InMemRepo.ts +61 -8
- package/src/testkit/Layers.ts +10 -2
- package/src/testkit/TestRepo.ts +4 -1
- package/src/testkit/index.ts +1 -1
- package/src/workflow/discovery.test.ts +147 -0
- package/src/workflow/discovery.ts +5 -0
- package/src/workflow/fingerprint.test.ts +20 -4
- package/src/workflow/fingerprint.ts +8 -9
- package/src/workflow/index.ts +1 -6
- package/src/workflow/load.test.ts +5 -19
- package/src/workflow/load.ts +94 -10
- package/src/workflow-kit/review.test.ts +84 -0
- package/src/workflow-kit/review.ts +9 -6
- package/src/workflows/feature/review.test.ts +238 -12
- package/src/workflows/feature/review.ts +172 -31
- package/src/workflows/feature/steps.test.ts +12 -0
- package/src/workflows/feature/steps.ts +8 -1
- package/src/workflows/shared/access.test.ts +4 -2
- package/src/workflows/shared/access.ts +1 -0
- package/src/workflows/shared/options.test.ts +8 -0
- package/src/workflows/shared/options.ts +1 -0
- package/src/workflows/shared/steps.ts +2 -0
- package/src/workflows/shared/tail.ts +3 -11
- package/src/workflows/shared/text.test.ts +106 -0
- package/src/workflows/shared/text.ts +57 -1
- package/src/workflows/shared/vars.ts +1 -0
package/README.md
CHANGED
|
@@ -475,10 +475,21 @@ noted in steps 3 and 4 below.
|
|
|
475
475
|
turns first), then a fresh review of the change stops at the review again.
|
|
476
476
|
`praise` is dropped; a round of only praise signs off. When a round mixes
|
|
477
477
|
them, questions are answered and nits fixed first, then the edits are planned
|
|
478
|
-
— risk: the planning lap may redo nit fixes it touches.
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
478
|
+
— risk: the planning lap may redo nit fixes it touches.
|
|
479
|
+
|
|
480
|
+
Each `edit` note also gets a severity tier from four judge questions
|
|
481
|
+
(security, reversibility, blast radius, new concept). `critical` and `should`
|
|
482
|
+
notes are re-planned this round; `could` notes are deferred. A leading `!` on
|
|
483
|
+
a note forces `critical` and skips the judge. Deferral appends to the
|
|
484
|
+
committed `.gtd/TODO.md`; it never starts a process on its own, and it ships
|
|
485
|
+
in the branch/PR unless you delete it. Landing clean at the gate after a
|
|
486
|
+
deferral signs off without the deferred notes; the gate lists them. To pull
|
|
487
|
+
one in, edit its line in `.gtd/TODO.md` when you start a process.
|
|
488
|
+
`reviewSeverityMinP` sets the confidence floor (see
|
|
489
|
+
[configuration](docs/configuration.md)). A hand-edit to code always plans a
|
|
490
|
+
lap. Only a confident non-`edit` verdict skips the replan — a note whose
|
|
491
|
+
evidence was cut, or that got no verdict, counts as `edit`. The same
|
|
492
|
+
`gtd judge answer` / conservative-default shape as step 3's own judged
|
|
482
493
|
points.
|
|
483
494
|
|
|
484
495
|
You never talk to it. Every exchange is a file in `.gtd/` that you edit in your
|
|
@@ -544,6 +555,30 @@ unknown key. `gtd.config.ts` is the only repository config: gtd 23 removed the
|
|
|
544
555
|
home directory — fails every command until you delete it. See
|
|
545
556
|
[Migrating from `.gtdrc`](https://github.com/pmelab/gtd/blob/main/docs/configuration.md#migrating-from-gtdrc).
|
|
546
557
|
|
|
558
|
+
## Process history
|
|
559
|
+
|
|
560
|
+
When a process finishes, the landing that closes it also keeps the whole run
|
|
561
|
+
behind a ref, `refs/gtd/history/<entry>`, so a squash or a deleted branch
|
|
562
|
+
doesn't lose it. `/gtd ship` changes nothing about this: the turns are already
|
|
563
|
+
kept before it squashes.
|
|
564
|
+
|
|
565
|
+
```sh
|
|
566
|
+
gtd history list # one line per kept process
|
|
567
|
+
gtd history show <ref> # replay its steps and judge verdicts
|
|
568
|
+
gtd history fetch | sh # pull every kept ref from origin
|
|
569
|
+
```
|
|
570
|
+
|
|
571
|
+
`gtd history fetch` only prints a fetch script; pipe it into `sh` to run it.
|
|
572
|
+
Refs are not pushed or fetched by default. To share them:
|
|
573
|
+
|
|
574
|
+
```sh
|
|
575
|
+
git push origin 'refs/gtd/history/*'
|
|
576
|
+
git config --add remote.origin.fetch 'refs/gtd/history/*:refs/gtd/history/*'
|
|
577
|
+
```
|
|
578
|
+
|
|
579
|
+
The second line makes every plain `git fetch` include them. It has no leading
|
|
580
|
+
`+`, so a conflicting local ref fails instead of being overwritten.
|
|
581
|
+
|
|
547
582
|
## Entrypoints
|
|
548
583
|
|
|
549
584
|
gtd is one package, `@pmelab/gtd`, and every entrypoint shares its version and
|
|
@@ -565,8 +600,8 @@ install for any of these but `./testkit`: gtd resolves a `gtd.config.ts`'s
|
|
|
565
600
|
|
|
566
601
|
The `gtd` bin's commands — `next`, `land`, `abandon`, `restore`, `validate`,
|
|
567
602
|
`check`, `uncheck`, `install`, `summary`, `base`, `door`, `doors`, `version`,
|
|
568
|
-
`help`,
|
|
569
|
-
`judge run`) — are listed in the
|
|
603
|
+
`help`, `history list`, `history show`, `history fetch`, and the tools `ui`,
|
|
604
|
+
`lsp`, `loop` and `judge` (`judge answer`, `judge run`) — are listed in the
|
|
570
605
|
[CLI reference](https://github.com/pmelab/gtd/blob/main/docs/cli.md).
|
|
571
606
|
|
|
572
607
|
## License
|
|
@@ -12242,6 +12242,7 @@ const mapErrorCause = /*#__PURE__*/ dual(2, (self, f) => matchCauseEffect$2(self
|
|
|
12242
12242
|
onSuccess: succeed$5
|
|
12243
12243
|
}));
|
|
12244
12244
|
const negate$1 = (self) => map$5(self, (b) => !b);
|
|
12245
|
+
const orElseSucceed$1 = /*#__PURE__*/ dual(2, (self, evaluate) => orElse$2(self, () => sync$1(evaluate)));
|
|
12245
12246
|
const patchFiberRefs = (patch) => updateFiberRefs((fiberId, fiberRefs) => pipe(patch, patch$2(fiberId, fiberRefs)));
|
|
12246
12247
|
const promise$1 = (evaluate) => evaluate.length >= 1 ? async_((resolve, signal) => {
|
|
12247
12248
|
try {
|
|
@@ -21189,6 +21190,51 @@ const orDie = orDie$1;
|
|
|
21189
21190
|
*/
|
|
21190
21191
|
const orElse$1 = orElse$2;
|
|
21191
21192
|
/**
|
|
21193
|
+
* Ensures the effect always succeeds by replacing failures with a default
|
|
21194
|
+
* success value.
|
|
21195
|
+
*
|
|
21196
|
+
* **Details**
|
|
21197
|
+
*
|
|
21198
|
+
* This function transforms an effect that may fail into one that cannot fail by
|
|
21199
|
+
* replacing any failure with a provided success value. If the original effect
|
|
21200
|
+
* fails, the failure is "swallowed," and the specified success value is
|
|
21201
|
+
* returned instead. If the original effect succeeds, its value remains
|
|
21202
|
+
* unchanged.
|
|
21203
|
+
*
|
|
21204
|
+
* **When to Use**
|
|
21205
|
+
*
|
|
21206
|
+
* This is especially useful for providing default values in case of failure,
|
|
21207
|
+
* ensuring that an effect always completes successfully. By using this
|
|
21208
|
+
* function, you can avoid the need for complex error handling and guarantee a
|
|
21209
|
+
* fallback result.
|
|
21210
|
+
*
|
|
21211
|
+
* **Example**
|
|
21212
|
+
*
|
|
21213
|
+
* ```ts
|
|
21214
|
+
* import { Effect } from "effect"
|
|
21215
|
+
*
|
|
21216
|
+
* const validate = (age: number): Effect.Effect<number, string> => {
|
|
21217
|
+
* if (age < 0) {
|
|
21218
|
+
* return Effect.fail("NegativeAgeError")
|
|
21219
|
+
* } else if (age < 18) {
|
|
21220
|
+
* return Effect.fail("IllegalAgeError")
|
|
21221
|
+
* } else {
|
|
21222
|
+
* return Effect.succeed(age)
|
|
21223
|
+
* }
|
|
21224
|
+
* }
|
|
21225
|
+
*
|
|
21226
|
+
* const program = Effect.orElseSucceed(validate(-1), () => 18)
|
|
21227
|
+
*
|
|
21228
|
+
* console.log(Effect.runSyncExit(program))
|
|
21229
|
+
* // Output:
|
|
21230
|
+
* // { _id: 'Exit', _tag: 'Success', value: 18 }
|
|
21231
|
+
* ```
|
|
21232
|
+
*
|
|
21233
|
+
* @since 2.0.0
|
|
21234
|
+
* @category Fallback
|
|
21235
|
+
*/
|
|
21236
|
+
const orElseSucceed = orElseSucceed$1;
|
|
21237
|
+
/**
|
|
21192
21238
|
* Returns an effect that accesses the runtime, which can be used to (unsafely)
|
|
21193
21239
|
* execute tasks.
|
|
21194
21240
|
*
|
|
@@ -25350,4 +25396,4 @@ Struct({
|
|
|
25350
25396
|
description: "Represents an issue returned by the ArrayFormatter formatter"
|
|
25351
25397
|
});
|
|
25352
25398
|
//#endregion
|
|
25353
|
-
export { makeSemaphore as $,
|
|
25399
|
+
export { makeSemaphore as $, toJSON as $i, mapBoth$2 as $n, none$2 as $r, provideMerge as $t, catchTag as A, getOption as Ai, scopeTag as An, interrupt$2 as Ar, sync as At, flatten as B, isSome as Bi, make$12 as Bn, tap$1 as Br, unsafeMakeSemaphore as Bt, all as C, unsafeFromArray as Ci, isInterruptedOnly as Cn, exitFailCause$1 as Cr, scope as Ct, async as D, Tag as Di, ensuring$1 as Dn, fiberId as Dr, serviceOption as Dt, asVoid as E, GenericTag as Ei, squash as En, exitZipRight as Er, scopedWith as Et, exit as F, filter$3 as Fi, sequential$1 as Fn, onInterrupt as Fr, tryPromise as Ft, forkScoped as G, isLeft as Gi, Class as Gn, withFiberRuntime$1 as Gr, zipRight as Gt, forEach as H, match$6 as Hi, set as Hn, uninterruptibleMask$2 as Hr, withFiberRuntime as Ht, fail$1 as I, flatMap$6 as Ii, bind as In, scopeClose as Ir, try_ as It, interrupt as J, right as Ji, die$2 as Jn, empty$15 as Jr, TaggedError$1 as Jt, gen as K, left as Ki, spanTag as Kn, zipRight$2 as Kr, zipWith as Kt, failCause as L, fromNullable as Li, bindTo as Ln, succeed$5 as Lr, uninterruptible as Lt, dieMessage as M, empty$22 as Mi, inheritAll as Mn, interruptFiber as Mr, tapErrorCause as Mt, either as N, fromIterable$6 as Ni, interruptAllAs as Nn, interruptible$2 as Nr, timeout as Nt, catchAll as O, add$2 as Oi, forEachConcurrentDiscard as On, fiberRefLocally as Or, succeed$1 as Ot, ensuring as P, splitAt as Pi, join as Pn, map$5 as Pr, tryMap as Pt, logWarning as Q, format$4 as Qi, map$4 as Qn, make$20 as Qr, provide$1 as Qt, fiberIdWith as R, getOrElse as Ri, sleep as Rn, suspend$3 as Rr, uninterruptibleMask as Rt, addFinalizer as S, toArray$1 as Si, isInterrupted as Sn, exitFail as Sr, sandbox as St, as as T, unsafeHead as Ti, reduceWithContext as Tn, exitVoid$1 as Tr, scoped as Tt, forkDaemon as U, none$4 as Ui, update$1 as Un, void_$2 as Ur, zip as Ut, fnUntraced as V, map$11 as Vi, modify as Vn, uninterruptible$1 as Vr, when as Vt, forkIn as W, some as Wi, TypeId$4 as Wn, whenEffect as Wr, zipLeft as Wt, intoDeferred as X, BaseProto as Xi, failCause$2 as Xn, combineAll as Xr, fromEffect as Xt, interruptible as Y, pipeArguments as Yi, fail$4 as Yn, combine$5 as Yr, tagged as Yt, isEffect as Z, NodeInspectSymbol as Zi, isFailure as Zn, ids as Zr, mergeAll as Zt, decodeSync as _, prepend$1 as _i, failureOrCause as _n, deferredMake as _r, repeat as _t, Literal as a, constVoid as aa, decode as ai, forkIn$1 as an, as$1 as ar, matchCauseEffect as at, acquireRelease as b, take as bi, interruptors as bn, deferredUnsafeMake as br, runPromiseExit as bt, String$ as c, pipe as ca, appendAll$1 as ci, addFinalizer$1 as cn, catchAll$1 as cr, onError as ct, Union as d, filter$1 as di, extend$1 as dn, deferredComplete as dr, orElseSucceed as dt, equals$2 as ea, get$4 as ei, scoped$1 as en, match$3 as er, map$1 as et, between as f, fromIterable$5 as fi, fork as fn, deferredFail as fr, promise as ft, standardSchemaV1 as g, of$1 as gi, fail$2 as gn, deferredIsDone as gr, raceWith as gt, optionalWith as h, map$9 as hi, die$1 as hn, deferredInterruptWith as hr, race as ht, Defect as i, constUndefined as ia, size$2 as ii, updateContext as in, zipRight$1 as ir, matchCause as it, die as j, omit$1 as ji, _await as jn, interruptAsFiber as jr, tap as jt, catchAllCause as k, get$7 as ki, scopeMake as kn, flatMap$3 as kr, suspend$1 as kt, Struct as l, drop as li, addFinalizerExit as ln, currentContext as lr, orDie as lt, optional as m, isEmpty$5 as mi, NoSuchElementException as mn, deferredInterrupt as mr, provideService as mt, Boolean$ as n, constFalse as na, set$3 as ni, unsafeFork as nn, void_$1 as nr, mapError$1 as nt, Never as o, dual as oa, isFinite as oi, unsafeMakeSemaphore$1 as on, asVoid$1 as or, matchEffect as ot, decodeUnknown as p, isChunk as pi, make$2 as pn, deferredFailCause as pr, provide as pt, ignore as q, match$7 as qi, all$2 as qn, DeferredTypeId as qr, spaced as qt, Cause as r, constTrue as ra, has$1 as ri, unsafeRunPromiseExit as rn, zip$1 as rr, match as rt, Number$ as s, identity as sa, append as si, Scope as sn, asyncInterrupt as sr, negate as st, Array$ as t, hasProperty as ta, make$22 as ti, succeed$2 as tn, succeed$4 as tr, mapBoth$1 as tt, TaggedError as u, empty$19 as ui, close as un, deferredAwait as ur, orElse$1 as ut, encodeSync as v, reduce$6 as vi, flipCauseOption as vn, deferredSucceed as vr, retry as vt, annotateLogs as w, unsafeGet as wi, map$3 as wn, exitSucceed$1 as wr, scopeWith as wt, acquireUseRelease as x, takeRight as xi, isDieType as xn, exitAs as xr, runtime as xt, _void as y, reverse$1 as yi, interrupt$1 as yn, deferredUnsafeDone as yr, runPromise as yt, flatMap$1 as z, isNone as zi, get as zn, sync$1 as zr, unsafeMakeLatch as zt };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { accessFor, agent, answered, check, codeThreads, defineWorkflow, env, head, human, judge, numeric, quote, read, refuse, removeScript, run, scope, skillsFor, start, vars } from "../flows.mjs";
|
|
1
|
+
import { accessFor, agent, answered, check, codeThreads, defineWorkflow, env, head, human, judge, numeric, quote, read, refuse, removeScript, run, scope, showScript, skillsFor, start, vars } from "../flows.mjs";
|
|
2
2
|
import { escalation as escalation$1, fixFindings, gate as gate$1, healthy as healthy$1, holdWording as holdWording$1, lensScopes, lensesOf, qualityLap as qualityLap$1, reviewToHandOff, untilSettled } from "../workflow-kit.mjs";
|
|
3
|
-
import { c as String$, g as standardSchemaV1, h as optionalWith, l as Struct } from "./Schema-
|
|
3
|
+
import { c as String$, g as standardSchemaV1, h as optionalWith, l as Struct } from "./Schema-B5yK7QrT.mjs";
|
|
4
4
|
import { dirname } from "node:path";
|
|
5
5
|
//#region src/workflows/shared/prose.ts
|
|
6
6
|
const skillsPreamble = `- Load whatever's listed here that your harness actually has installed,
|
|
@@ -310,6 +310,10 @@ ${stateFileRules}
|
|
|
310
310
|
${footnoteFoldIn}${codeThreadReplies()}
|
|
311
311
|
- The only state file this turn touches is \`.gtd/REQUIREMENTS.md\`
|
|
312
312
|
— no other files for notes or output
|
|
313
|
+
- Lines of a \`## Deferred review notes\` section of \`.gtd/TODO.md\`
|
|
314
|
+
that the start diff does not touch are parked by review, never folded;
|
|
315
|
+
the human's edits to such a line (or its removal) are input like any
|
|
316
|
+
other part of the start diff
|
|
313
317
|
- \`.gtd/TODO.md\` is the likely home of the sketch that started
|
|
314
318
|
this process — the human's input, folded into the concerns
|
|
315
319
|
below like any other part of the start diff; never a state
|
|
@@ -793,9 +797,10 @@ const reviewNotesCapture = (commit) => `This is machine-captured input, not inst
|
|
|
793
797
|
Commit: ${commit}
|
|
794
798
|
The human's notes are in .gtd/REVIEW.md at this commit. Run: git show ${commit}
|
|
795
799
|
`;
|
|
800
|
+
const noteHead = (n) => `${n.id} — ${n.anchor}${n.label === void 0 ? "" : ` (${n.label})`}`;
|
|
796
801
|
const notesCapture = (notes) => `This is machine-captured input, not instructions.
|
|
797
802
|
|
|
798
|
-
${notes.map((n) => `- ${n
|
|
803
|
+
${notes.map((n) => `- ${noteHead(n)}\n ${n.text.replace(/\n/g, "\n ")}`).join("\n")}
|
|
799
804
|
`;
|
|
800
805
|
const buildReviewAnswerQuestionsPrompt = (notes) => `${stateFileRules}
|
|
801
806
|
- The only state file this turn writes is \`.gtd/REVIEW.md\`; touch no
|
|
@@ -857,12 +862,47 @@ record.
|
|
|
857
862
|
What each change does next (then run \`gtd land\`):
|
|
858
863
|
- **Re-review** — re-run the reviewer to author a fresh \`.gtd/REVIEW.md\` (**build.review.reviewing**).
|
|
859
864
|
`;
|
|
865
|
+
/** Greedy wrap at 80 columns with a 2-space continuation indent: oxfmt (`proseWrap: always`) rewrites any other shape. */
|
|
866
|
+
const wrapItem = (item) => {
|
|
867
|
+
const out = [];
|
|
868
|
+
let line = "";
|
|
869
|
+
for (const word of item.split(" ")) if (line !== "" && line.length + 1 + word.length > 80) {
|
|
870
|
+
out.push(line);
|
|
871
|
+
line = ` ${word}`;
|
|
872
|
+
} else line = line === "" ? word : `${line} ${word}`;
|
|
873
|
+
return [...out, line];
|
|
874
|
+
};
|
|
875
|
+
/** Appends one section of `notes` to `.gtd/TODO.md`, wrapped the way oxfmt writes it; an existing file is only ever appended to. */
|
|
876
|
+
const deferNotesScript = (notes, round) => {
|
|
877
|
+
return `mkdir -p .gtd
|
|
878
|
+
if [ -s .gtd/TODO.md ]; then
|
|
879
|
+
[ -z "$(tail -c1 .gtd/TODO.md)" ] || printf '\\n' >> .gtd/TODO.md
|
|
880
|
+
printf '\\n' >> .gtd/TODO.md
|
|
881
|
+
fi
|
|
882
|
+
printf '%s\\n' ${[
|
|
883
|
+
`## Deferred review notes (${round.slice(0, 7)})`,
|
|
884
|
+
"",
|
|
885
|
+
...notes.flatMap((n) => wrapItem(`- ${noteHead(n)}: ${n.text.replace(/\s+/g, " ")}`))
|
|
886
|
+
].map(quote).join(" ")} >> .gtd/TODO.md
|
|
887
|
+
`;
|
|
888
|
+
};
|
|
889
|
+
/** The gate right after a deferral: landing clean signs off without these notes. */
|
|
890
|
+
const buildReviewAwaitAfterDeferMessage = (base, deferred) => `${buildReviewAwaitReviewMessage(base)}
|
|
891
|
+
Deferred to \`.gtd/TODO.md\` just now, NOT acted on in this process — landing
|
|
892
|
+
clean signs off without them:
|
|
893
|
+
|
|
894
|
+
${deferred.map((n) => `- ${noteHead(n)}: ${n.text.replace(/\s*\n\s*/g, " ")}`).join("\n")}
|
|
895
|
+
`;
|
|
860
896
|
const buildReviewTriageMessage = () => `Judging each note the human added to \`.gtd/REVIEW.md\` (a line note, a
|
|
861
897
|
footnote, or prose under a chunk) with one verdict: \`edit\` (a change
|
|
862
898
|
request), \`question\`, \`nit\` (a small fix needing no re-plan) or
|
|
863
899
|
\`praise\`. Run \`gtd judge answer\` and pipe a choice per note — or land
|
|
864
900
|
untouched, which treats every note as \`edit\` (the conservative
|
|
865
901
|
default; a skipped judgment never dismisses a note).
|
|
902
|
+
|
|
903
|
+
Also judge each note's severity, one id per note: \`note-<n>.security\`,
|
|
904
|
+
\`note-<n>.reversibility\`, \`note-<n>.blastRadius\` and
|
|
905
|
+
\`note-<n>.newConcept\`.
|
|
866
906
|
`;
|
|
867
907
|
const buildReviewCollectingPrompt = (capture) => `${styleBlock}
|
|
868
908
|
|
|
@@ -883,6 +923,8 @@ ${footnoteFoldIn}${codeThreadReplies()}
|
|
|
883
923
|
- When the capture names an answering commit, carry each answered
|
|
884
924
|
question into \`.gtd/REQUIREMENTS.md\`'s \`## Answered Questions\` as
|
|
885
925
|
\`### <the note>\` plus its answer — the lap replaces \`.gtd/REVIEW.md\`
|
|
926
|
+
- Keep capture order, and carry each note's \`(tier: reason)\`
|
|
927
|
+
label into the concern folded from it
|
|
886
928
|
- Nit notes were already fixed: never re-raise a fixed nit as a concern
|
|
887
929
|
- Finish by running \`gtd check review .gtd/REVIEW.md\` and fix
|
|
888
930
|
what it reports
|
|
@@ -1109,12 +1151,13 @@ const fixRisks = (notes) => agentWithSkills("review.fix.risks.fixing", buildRevi
|
|
|
1109
1151
|
system: reviewerSystem(),
|
|
1110
1152
|
allowEmpty: true
|
|
1111
1153
|
});
|
|
1112
|
-
const awaitReview = (base, message = buildReviewAwaitReviewMessage(base)) => human("review.await-review", {
|
|
1154
|
+
const awaitReview = (base, message = buildReviewAwaitReviewMessage(base), acceptClean = false) => human("review.await-review", {
|
|
1113
1155
|
message,
|
|
1114
1156
|
label: "Awaiting your review",
|
|
1115
1157
|
file: REVIEW,
|
|
1116
1158
|
mode: "review",
|
|
1117
|
-
base
|
|
1159
|
+
base,
|
|
1160
|
+
...acceptClean ? { acceptClean } : {}
|
|
1118
1161
|
});
|
|
1119
1162
|
/** The round at commit `round` left no `.gtd/REVIEW.md`. */
|
|
1120
1163
|
const reviewMissing = (round) => human("review.review-missing", {
|
|
@@ -1214,6 +1257,7 @@ const defaults = {
|
|
|
1214
1257
|
judgeBudgetBytes: "32768",
|
|
1215
1258
|
judgeIdenticalMinP: "0.7",
|
|
1216
1259
|
reviewNoteActionable: "0.7",
|
|
1260
|
+
reviewSeverityMinP: "0.7",
|
|
1217
1261
|
qualityReviews: "correctness, owasp-security, ponytail-review, test-audit, conventions, spec-challenge"
|
|
1218
1262
|
};
|
|
1219
1263
|
/** Environment settings. */
|
|
@@ -1231,6 +1275,7 @@ const ShippedOptions = Struct({
|
|
|
1231
1275
|
judgeBudgetBytes: setting,
|
|
1232
1276
|
judgeIdenticalMinP: setting,
|
|
1233
1277
|
reviewNoteActionable: setting,
|
|
1278
|
+
reviewSeverityMinP: setting,
|
|
1234
1279
|
qualityReviews: setting,
|
|
1235
1280
|
testCommand: setting,
|
|
1236
1281
|
fastTestCommand: setting,
|
|
@@ -1317,12 +1362,6 @@ const tail = (fixFirst, finish, built) => scope("build", async () => {
|
|
|
1317
1362
|
else await repair();
|
|
1318
1363
|
return finish(escalations, frozen);
|
|
1319
1364
|
});
|
|
1320
|
-
const handOffScript = (reviewed) => [
|
|
1321
|
-
"#!/usr/bin/env sh",
|
|
1322
|
-
"set -e",
|
|
1323
|
-
`git show ${quote(`${reviewed}:${REVIEW}`)} > ${quote(REVIEW_ORIG)}`,
|
|
1324
|
-
""
|
|
1325
|
-
].join("\n");
|
|
1326
1365
|
/**
|
|
1327
1366
|
* The review and fix workflows' human review since `base`: a sign-off closes
|
|
1328
1367
|
* `.gtd/REVIEW.md`; a note leaves it, with the reviewer's own version as
|
|
@@ -1336,7 +1375,7 @@ const handOffReview = (base, escalations) => reviewToHandOff(base, {
|
|
|
1336
1375
|
awaitReview: (b) => awaitReview(b, handOffAwaitReviewMessage(b)),
|
|
1337
1376
|
reviewMissing,
|
|
1338
1377
|
close: closeReview,
|
|
1339
|
-
handOff: (reviewed) => run("review.handing-off",
|
|
1378
|
+
handOff: (reviewed) => run("review.handing-off", showScript(reviewed, REVIEW, REVIEW_ORIG), { label: "Handing your notes to fix" }),
|
|
1340
1379
|
threadsPending: "gtd land: open-threads: a thread in .gtd/REVIEW.md or an `H:` code comment waits on the agent — nothing answers it here; delete it, or turn it into a note for fix.",
|
|
1341
1380
|
unparsed: "gtd land: review-doc: your edit to .gtd/REVIEW.md contains no note fix can read — restore the deleted chunk, or write the note as a line under its entry.",
|
|
1342
1381
|
deleted: "gtd land: review-doc: .gtd/REVIEW.md was deleted — restore it, or leave a note to hand to fix."
|
|
@@ -1372,6 +1411,7 @@ const access = (vars) => ({
|
|
|
1372
1411
|
architecture: { write: [".gtd/REQUIREMENTS.md"] },
|
|
1373
1412
|
"architecture.decompose": { write: [".gtd/packages/**", ".gtd/ARCHITECTURE.md"] },
|
|
1374
1413
|
"build.review": { write: [".gtd/REVIEW.md"] },
|
|
1414
|
+
"build.review.defer": { write: [".gtd/TODO.md"] },
|
|
1375
1415
|
"build.review.fix.nits": {},
|
|
1376
1416
|
"build.review.fix.risks": {},
|
|
1377
1417
|
...lensScopes("build.quality", lensesOf(vars.qualityReviews ?? ""), () => ({ write: [] }))
|
|
@@ -1385,4 +1425,4 @@ const steering = {
|
|
|
1385
1425
|
[REVIEW]: "review"
|
|
1386
1426
|
};
|
|
1387
1427
|
//#endregion
|
|
1388
|
-
export {
|
|
1428
|
+
export { startGateBlockedMessage as $, architectureAuthorPrompt as A, designGateAnswerMessage as B, fixCheck as C, reviewing as D, reviewMissing as E, buildReviewCollectingPrompt as F, idleMessage as G, designTriagePrompt as H, buildReviewFixNitsPrompt as I, reviewEditNotesCapture as J, packagesItemBuildingPrompt as K, buildReviewTriageMessage as L, architectureGateAnswerMessage as M, buildReviewAnswerQuestionsPrompt as N, agentWithSkills as O, buildReviewAwaitAfterDeferMessage as P, reviewerSystem as Q, builderSystem as R, coder as S, planner as T, finisherSystem as U, designSystem as V, fixReviewNotesPrompt as W, reviewGateBlockedMessage as X, reviewEditsCapture as Y, reviewNotesCapture as Z, ARCHITECTURE as _, REVIEW_ORIG as a, REVIEW as b, refusePendingHandOff as c, shipped as d, unwindFailedMessage as et, defaults as f, healthy as g, gate as h, skills as i, architectureDecomposePrompt as j, architectSystem as k, tail as l, baseline as m, summary as n, closeReview as o, envDefaults as p, packagesItemFixSuitePrompt as q, access as r, handOffTail as s, steering as t, unwindFailure as tt, guarded as u, FEEDBACK as v, fixRisks as w, awaitReview as x, REQUIREMENTS as y, deferNotesScript as z };
|
|
@@ -1,4 +1,12 @@
|
|
|
1
1
|
import { t as __exportAll } from "./rolldown-runtime-8H4AJuhK.mjs";
|
|
2
|
+
//#region src/steering/SteeringFormat.ts
|
|
3
|
+
/** `file:line:col: message` — the shape editors and grep already jump on. Stored 0-based, printed 1-based. A finding with a `line` but no `range`, or none at all, degrades to the shorter forms. */
|
|
4
|
+
const formatFinding = (file, finding) => {
|
|
5
|
+
if (finding.line === void 0) return finding.message;
|
|
6
|
+
const col = finding.range !== void 0 ? `:${finding.range.start.character + 1}` : "";
|
|
7
|
+
return `${file}:${finding.line + 1}${col}: ${finding.message}`;
|
|
8
|
+
};
|
|
9
|
+
//#endregion
|
|
2
10
|
//#region node_modules/mdast-util-to-string/lib/index.js
|
|
3
11
|
/**
|
|
4
12
|
* @typedef {import('mdast').Nodes} Nodes
|
|
@@ -15912,10 +15920,13 @@ const reviewNotes = (before, after) => {
|
|
|
15912
15920
|
return [...parseReviewDoc(after).changesets.flatMap((chunk) => {
|
|
15913
15921
|
const prior = oldChunks.get(chunk.title);
|
|
15914
15922
|
return [...chunkNote(chunk, prior), ...pointerNotes(chunk, prior)];
|
|
15915
|
-
}), ...footnoteNotes(before, after)].sort((a, b) => a.line - b.line).map((f, i) =>
|
|
15916
|
-
|
|
15917
|
-
|
|
15918
|
-
|
|
15923
|
+
}), ...footnoteNotes(before, after)].sort((a, b) => a.line - b.line).map((f, i) => {
|
|
15924
|
+
return {
|
|
15925
|
+
id: `note-${i + 1}`,
|
|
15926
|
+
...f.note,
|
|
15927
|
+
forced: /^!(\s|$)/.test(f.note.text)
|
|
15928
|
+
};
|
|
15929
|
+
});
|
|
15919
15930
|
};
|
|
15920
15931
|
/** Pointer notes the reviewer opened with `Risk:`, in document order — the ones the workflow fixes before the human gate. */
|
|
15921
15932
|
const reviewRisks = (content) => parseReviewDoc(content).changesets.flatMap((chunk) => chunk.files.flatMap((file) => file.note?.startsWith("Risk:") ? [{
|
|
@@ -16293,4 +16304,4 @@ const viewOf = (format, content) => ({
|
|
|
16293
16304
|
/** Every OPEN thread — last entry is the agent's — as `{ name, line, firstMe }` (0-based line). `[]` for a format with no thread concept. */
|
|
16294
16305
|
const openThreads = (format, content) => format.openThreads?.(content) ?? [];
|
|
16295
16306
|
//#endregion
|
|
16296
|
-
export { headingSections as C, headingSectionBodies as S, openThreadFindings as _, unansweredQuestions as a, parseThreadsWithFindings as b, stripCodeThreads as c, reviewNotes as d, reviewRisks as f, THREAD_REPLY_ACTION_TITLE as g, FOOTNOTE_ACTION_TITLE as h, steeringFormatOrFreeForm as i, freeFormFormat as l, isAnswered as m, openThreads as n, viewOf as o, FREE_TEXT_PLACEHOLDER as p, steeringFormatFor as r, parseCodeThreads as s, BUILT_IN_MODE_NAMES as t, applySteeringEdits as u, parseFootnotes as v, getParseCount as x, parseThreads as y };
|
|
16307
|
+
export { headingSections as C, headingSectionBodies as S, openThreadFindings as _, unansweredQuestions as a, parseThreadsWithFindings as b, stripCodeThreads as c, reviewNotes as d, reviewRisks as f, THREAD_REPLY_ACTION_TITLE as g, FOOTNOTE_ACTION_TITLE as h, steeringFormatOrFreeForm as i, freeFormFormat as l, isAnswered as m, openThreads as n, viewOf as o, FREE_TEXT_PLACEHOLDER as p, steeringFormatFor as r, parseCodeThreads as s, BUILT_IN_MODE_NAMES as t, applySteeringEdits as u, parseFootnotes as v, formatFinding as w, getParseCount as x, parseThreads as y };
|
package/dist/flows.mjs
CHANGED
|
@@ -237,6 +237,13 @@ const moveScript = (from, to) => [
|
|
|
237
237
|
`mv -- ${quote(from)} ${quote(to)}`,
|
|
238
238
|
""
|
|
239
239
|
].join("\n");
|
|
240
|
+
/** Write `path` as committed at `ref` to `to`. */
|
|
241
|
+
const showScript = (ref, path, to) => [
|
|
242
|
+
"#!/usr/bin/env sh",
|
|
243
|
+
"set -e",
|
|
244
|
+
`git show ${quote(`${ref}:${path}`)} > ${quote(to)}`,
|
|
245
|
+
""
|
|
246
|
+
].join("\n");
|
|
240
247
|
//#endregion
|
|
241
248
|
//#region src/flows/helpers.ts
|
|
242
249
|
/** Whether a judge answered `expected` at a probability of at least `minP`. */
|
|
@@ -515,6 +522,7 @@ var flows_exports = /* @__PURE__ */ __exportAll({
|
|
|
515
522
|
scope: () => scope,
|
|
516
523
|
sectionBodies: () => sectionBodies,
|
|
517
524
|
sections: () => sections,
|
|
525
|
+
showScript: () => showScript,
|
|
518
526
|
skillsFor: () => skillsFor,
|
|
519
527
|
start: () => start,
|
|
520
528
|
threads: () => threads,
|
|
@@ -522,4 +530,4 @@ var flows_exports = /* @__PURE__ */ __exportAll({
|
|
|
522
530
|
wrote: () => wrote
|
|
523
531
|
});
|
|
524
532
|
//#endregion
|
|
525
|
-
export { FLOWS_PROTOCOL, FlowsProtocolError, accessFor, agent, answered, changes, changesSince, check, checkScript, codeChanges, codeThreads, configProblems, configure, defineConfig, defineWorkflow, env, glob, hasThreadFor, head, human, installContext, isFactoryWorkflow, judge, moveScript, numeric, openQuestions, protocolMismatch, quote, read, refuse, removeScript, requireAnswers, requireProgress, requireReplies, requireRevert, requireThreadsClosed, restart, restoreScript, revertScript, run, scope, sectionBodies, sections, skillsFor, start, flows_exports as t, threads, vars, wrote };
|
|
533
|
+
export { FLOWS_PROTOCOL, FlowsProtocolError, accessFor, agent, answered, changes, changesSince, check, checkScript, codeChanges, codeThreads, configProblems, configure, defineConfig, defineWorkflow, env, glob, hasThreadFor, head, human, installContext, isFactoryWorkflow, judge, moveScript, numeric, openQuestions, protocolMismatch, quote, read, refuse, removeScript, requireAnswers, requireProgress, requireReplies, requireRevert, requireThreadsClosed, restart, restoreScript, revertScript, run, scope, sectionBodies, sections, showScript, skillsFor, start, flows_exports as t, threads, vars, wrote };
|