@pmelab/gtd 27.8.0 → 28.0.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 +59 -30
- package/claude/hooks/doors.ts +12 -0
- package/claude/hooks/register.tsx +2 -4
- package/claude/hooks/ship.ts +115 -20
- package/dist/chunks/{reserved-UdSuHDUJ.mjs → reserved-BIsngMGL.mjs} +168 -27
- package/dist/chunks/{tail-fj60rQ3y.mjs → tail-DHEaOHyp.mjs} +1 -1
- package/dist/chunks/{wire-C12dahrH.mjs → wire-D3Rc5r22.mjs} +14 -1
- package/dist/gtd.bundle.mjs +174 -48
- package/dist/testkit.mjs +158 -49
- package/dist/wire.mjs +2 -2
- package/dist/workflows/cleanup.mjs +3 -4
- package/dist/workflows/feature.mjs +2 -2
- package/dist/workflows/fix.mjs +2 -2
- package/dist/workflows/review.mjs +2 -2
- package/dist/workflows/rollout.mjs +19 -4
- package/dist/workflows.mjs +186 -127
- package/package.json +1 -1
- package/skills/authoring/SKILL.md +4 -3
- package/src/cli/Cli.test.ts +39 -3
- package/src/cli/Cli.ts +27 -6
- package/src/cli/Program.test.ts +3 -1
- package/src/cli/Program.ts +22 -0
- package/src/edge/Edge.test.ts +209 -54
- package/src/edge/Edge.ts +121 -42
- package/src/edge/index.ts +1 -0
- package/src/install/ConfigScaffold.ts +4 -1
- package/src/install/Install.test.ts +10 -0
- package/src/install/Install.ts +5 -2
- package/src/replay/Replay.test.ts +32 -0
- package/src/replay/Replay.ts +3 -1
- package/src/replay/Trailers.test.ts +11 -0
- package/src/replay/Trailers.ts +3 -1
- package/src/replay/index.ts +1 -1
- package/src/wire/Slices.ts +25 -0
- package/src/wire/WireSchema.test.ts +18 -0
- package/src/wire/golden/1/slices.json +11 -0
- package/src/wire/index.ts +1 -0
- package/src/workflow/load.test.ts +82 -5
- package/src/workflow/load.ts +40 -16
- package/src/workflows/cleanup/cleanup.feature +21 -0
- package/src/workflows/cleanup/cleanup.ts +1 -1
- package/src/workflows/index.ts +2 -1
- package/src/workflows/rollout/rollout.feature +282 -2
- package/src/workflows/rollout/rollout.test.ts +96 -2
- package/src/workflows/rollout/rollout.ts +41 -3
- package/src/workflows/shared/options.test.ts +25 -2
- package/src/workflows/shared/options.ts +7 -4
- package/src/workflows/shared/registry.test.ts +41 -1
- package/src/workflows/shared/registry.ts +26 -1
- package/src/workflows/shared/text.test.ts +252 -2
- package/src/workflows/shared/text.ts +44 -1
- package/src/workflows/shared/vars.ts +2 -0
- package/dist/chunks/registry-CP3YuV9j.mjs +0 -101
package/dist/testkit.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { checkScript, configProblems, configure, installContext, isFactoryWorkflow, moveScript, protocolMismatch, removeScript, restoreScript, revertScript, showScript, t as flows_exports } from "./flows.mjs";
|
|
2
|
-
import {
|
|
2
|
+
import { O as ACCESS_REFUSAL, a as selectPath, d as renderLandPlain, g as statusOf, h as renderBeatJson, i as resolveSession, j as renderFormat, l as noopText, m as beatDocument, n as slicesDocument, o as judgeJson, p as renderBeatPlain, r as globMatches, s as landFields, t as renderSlicesJson, u as renderLandJson, v as beatKindOf, y as demandOf } from "./chunks/wire-D3Rc5r22.mjs";
|
|
3
3
|
import { C as headingSections, S as headingSectionBodies, a as unansweredQuestions, b as parseThreadsWithFindings, n as openThreads, r as steeringFormatFor, s as parseCodeThreads, t as BUILT_IN_MODE_NAMES, w as formatFinding } from "./chunks/steering-DVILanVf.mjs";
|
|
4
4
|
import { $ as logWarning, $i as NodeInspectSymbol, $n as map$5, $r as make$19, $t as provide$1, A as catchIf, Ai as add, An as scopeMake, Ar as flatMap$3, At as suspend$5, B as flatMap$4, Bi as getOrElse, Bn as get$3, Br as sync$1, Bt as unsafeMakeLatch, C as all$1, Ci as toArray, Cn as isInterrupted, Cr as exitFail, Ct as sandbox, D as async, Di as GenericTag, Dn as squash, Dr as exitZipRight, Dt as scopedWith$1, E as asVoid$1, Ei as unsafeHead, En as reduceWithContext, Er as exitVoid, Et as scoped$4, F as ensuring$2, Fi as fromIterable, Fn as join$3, Fr as map$3, Ft as tryMap, G as forkIn, Gi as none, Gn as TypeId$10, Gr as whenEffect, Gt as zipLeft, H as fnUntraced, Hi as isSome, Hn as modify$1, Hr as uninterruptible, Ht as when, I as exit, Ii as splitAt, In as sequential, Ir as onInterrupt, It as tryPromise, J as ignore, Ji as left, Jn as all, Jr as DeferredTypeId$1, Jt as spaced$1, K as forkScoped, Ki as some, Kn as Class, Kr as withFiberRuntime, Kt as zipRight$3, L as fail$5, Li as filter$1, Ln as bind, Lr as scopeClose, Lt as try_, M as die$2, Mi as getOption, Mn as _await$2, Mr as interruptAsFiber, Mt as tap, N as dieMessage, Ni as omit, Nn as inheritAll$1, Nr as interruptFiber, Nt as tapErrorCause, O as catchAll$1, Oi as Reference, On as ensuring$3, Or as fiberId, Ot as serviceOption, P as either, Pi as empty$5, Pn as interruptAllAs$1, Pr as interruptible$1, Pt as timeout, Q as isEffect, Qi as BaseProto, Qn as isFailure, Qr as ids, Qt as mergeAll$2, R as failCause$4, Ri as flatMap$5, Rn as bindTo, Rr as succeed$4, Rt as uninterruptible$1, S as addFinalizer$1, Si as takeRight, Sn as isDieType, Sr as exitAs, St as runtime, T as as, Ti as unsafeGet, Tn as map$6, Tr as exitSucceed, Tt as scopeWith, U as forEach$2, Ui as map$8, Un as set$1, Ur as uninterruptibleMask$1, Ut as withFiberRuntime$1, V as flatten$4, Vi as isNone, Vn as make$16, Vr as tap$1, Vt as unsafeMakeSemaphore$1, W as forkDaemon, Wi as match$4, Wn as update$1, Wr as void_$2, Wt as zip$1, X as interruptible, Xi as right, Xn as fail$4, Xr as combine, Xt as tagged, Y as interrupt$4, Yi as match$2, Yn as die, Yr as empty$6, Yt as TaggedError$1, Z as intoDeferred, Zi as pipeArguments, Zn as failCause$3, Zr as combineAll, Zt as fromEffect$5, _ as decodeSync, _i as of, _n as fail$3, _r as deferredIsDone, _t as raceWith, a as Literal, aa as constTrue, ai as size$2, an as updateContext$1, ar as zipRight$2, at as matchCause, b as acquireRelease, bi as reverse, bn as interrupt$3, br as deferredUnsafeDone, bt as runPromise, c as String$, ca as dual, ci as append$1, cn as Scope, cr as asyncInterrupt, ct as negate, d as Union, di as empty$4, dn as close, dr as deferredAwait, dt as orElse, ea as format, ei as none$1, en as provideMerge$1, er as mapBoth, et as makeSemaphore, f as between, fi as filter, fn as extend, fr as deferredComplete, ft as orElseSucceed, gi as map$7, gn as die$1, gr as deferredInterruptWith, gt as race, hi as isEmpty$2, hn as NoSuchElementException, hr as deferredInterrupt, ht as provideService, i as Defect, ia as constFalse, ii as has, in as unsafeRunPromiseExit, ir as zip$2, it as match$5, j as catchTag, ji as get$5, jn as scopeTag, jr as interrupt$2, jt as sync$2, k as catchAllCause, ki as Tag, kn as forEachConcurrentDiscard, kr as fiberRefLocally, kt as succeed$6, l as Struct, la as identity, li as appendAll, ln as addFinalizer, lr as catchAll, lt as onError, m as optional$1, mi as isChunk, mn as make$18, mr as deferredFailCause, mt as provide$2, n as Boolean$, na as equals, ni as make$17, nn as succeed$3, nr as succeed$5, nt as mapBoth$1, o as Never, oa as constUndefined, oi as decode, on as forkIn$1, or as as$1, ot as matchCauseEffect, p as decodeUnknown, pi as fromIterable$1, pn as fork, pr as deferredFail, pt as promise, q as gen, qi as isLeft, qn as spanTag, qr as zipRight$1, qt as zipWith, r as Cause, ra as hasProperty, ri as set$2, rn as unsafeFork, rr as void_$3, rt as mapError, s as Number$, sa as constVoid, si as isFinite, sn as unsafeMakeSemaphore, sr as asVoid, st as matchEffect, t as Array$, ta as toJSON, ti as get$4, tn as scoped$3, tr as match$3, tt as map$4, u as TaggedError, ua as pipe, ui as drop, un as addFinalizerExit, ur as currentContext$1, ut as orDie, v as encodeSync, vi as prepend, vn as failureOrCause, vr as deferredMake, vt as repeat, w as annotateLogs, wi as unsafeFromArray, wn as isInterruptedOnly, wr as exitFailCause, wt as scope, x as acquireUseRelease$2, xi as take$2, xn as interruptors, xr as deferredUnsafeMake, xt as runPromiseExit$1, y as _void, yi as reduce, yn as flipCauseOption, yr as deferredSucceed, yt as retry, z as fiberIdWith, zi as fromNullable, zn as sleep, zr as suspend$4, zt as uninterruptibleMask } from "./chunks/Schema-DO5ttJaI.mjs";
|
|
5
5
|
import { createRequire } from "node:module";
|
|
@@ -6647,8 +6647,11 @@ hands off to \`gtd-build\`, which halts at the blocked gate and prints it,
|
|
|
6647
6647
|
rather than starting the review; \`gtd-door fix\` on a GREEN suite is a no-op
|
|
6648
6648
|
straight back to \`idle\` — the exec'd \`gtd-build\` exits immediately on it.
|
|
6649
6649
|
\`gtd doors\` lists the doors a repository offers (those of the workflows its
|
|
6650
|
-
config exports — the bundled \`fix\` and \`review [base]
|
|
6651
|
-
|
|
6650
|
+
config exports — the bundled \`fix\` and \`review [base]\`, plus the opt-in
|
|
6651
|
+
\`rollout <flag>\` (turn a release flag default-on) and \`cleanup <flag>\`
|
|
6652
|
+
(remove it), which a config adds by exporting \`rollout\` from
|
|
6653
|
+
\`@pmelab/gtd/workflows/rollout\` and \`cleanup\` from
|
|
6654
|
+
\`@pmelab/gtd/workflows/cleanup\` — plus any it declares). Refuses with
|
|
6652
6655
|
\`usage: gtd-door <name> [args...]\` on stderr and exit \`2\` when the name is
|
|
6653
6656
|
missing — nothing was even attempted. Default path: \`~/.local/bin/gtd-door\`.
|
|
6654
6657
|
|
|
@@ -6758,7 +6761,10 @@ const renderBriefing = () => HEADER() + BEAT_PROTOCOL + JSON_FIELD_REFERENCE + D
|
|
|
6758
6761
|
const CONFIG_SCAFFOLD = `// The workflows gtd runs here: gtd's shipped feature, review and fix.
|
|
6759
6762
|
// Every exported function is startable with \`gtd --workflow <name>\`; the
|
|
6760
6763
|
// default export is what a change in the tree starts. Each workflow brings
|
|
6761
|
-
// its own doors: \`gtd door review [base]\` and \`gtd door fix\`.
|
|
6764
|
+
// its own doors: \`gtd door review [base]\` and \`gtd door fix\`. Export \`rollout\`
|
|
6765
|
+
// from \`@pmelab/gtd/workflows/rollout\` and \`cleanup\` from
|
|
6766
|
+
// \`@pmelab/gtd/workflows/cleanup\` to add \`gtd door rollout <flag>\` and
|
|
6767
|
+
// \`gtd door cleanup <flag>\`.
|
|
6762
6768
|
export {
|
|
6763
6769
|
feature as default,
|
|
6764
6770
|
feature,
|
|
@@ -6999,7 +7005,7 @@ const TRAILER_READERS = {
|
|
|
6999
7005
|
if (value !== "") into.branch ??= value;
|
|
7000
7006
|
},
|
|
7001
7007
|
"Gtd-Version": (value, into) => {
|
|
7002
|
-
if (value !== "") into.version ??= value;
|
|
7008
|
+
if (value !== "" && !/[\u0000-\u001f\u007f-\u009f]/.test(value)) into.version ??= value;
|
|
7003
7009
|
},
|
|
7004
7010
|
"Gtd-Kept": (value, into) => {
|
|
7005
7011
|
if (HASH_RE.test(value)) into.kept ??= value;
|
|
@@ -7437,7 +7443,8 @@ const replay = async (input) => {
|
|
|
7437
7443
|
via,
|
|
7438
7444
|
trace,
|
|
7439
7445
|
landed,
|
|
7440
|
-
marks
|
|
7446
|
+
marks,
|
|
7447
|
+
slices: slices.map((s) => ({ ...s }))
|
|
7441
7448
|
};
|
|
7442
7449
|
const changesSince = (hash) => {
|
|
7443
7450
|
const tree = treeAt(hash);
|
|
@@ -8103,10 +8110,10 @@ const doorsFrom = (exports) => {
|
|
|
8103
8110
|
};
|
|
8104
8111
|
/**
|
|
8105
8112
|
* Each exported workflow's own doors, onto the name it is exported under. The
|
|
8106
|
-
* `doors` export adds to them and
|
|
8113
|
+
* `doors` export adds to them and declares a name several workflows clash on.
|
|
8107
8114
|
*/
|
|
8108
8115
|
const derivedDoors = (catalogue, declared) => {
|
|
8109
|
-
const
|
|
8116
|
+
const builders = /* @__PURE__ */ new Map();
|
|
8110
8117
|
for (const [workflow, loaded] of catalogue) for (const [name, spec] of Object.entries(loaded.doors ?? {})) {
|
|
8111
8118
|
const door = {
|
|
8112
8119
|
...spec,
|
|
@@ -8114,12 +8121,22 @@ const derivedDoors = (catalogue, declared) => {
|
|
|
8114
8121
|
};
|
|
8115
8122
|
const problem = doorProblem(name, door);
|
|
8116
8123
|
if (problem !== void 0) throw new Error(`workflow "${workflow}" builds door "${name}", which ${problem}`);
|
|
8117
|
-
|
|
8118
|
-
|
|
8119
|
-
|
|
8124
|
+
builders.set(name, [...builders.get(name) ?? [], door]);
|
|
8125
|
+
}
|
|
8126
|
+
const doors = /* @__PURE__ */ new Map();
|
|
8127
|
+
const clashes = /* @__PURE__ */ new Map();
|
|
8128
|
+
for (const [name, built] of builders) {
|
|
8129
|
+
const owner = ownerOf(name, built);
|
|
8130
|
+
if (owner !== void 0) doors.set(name, owner);
|
|
8131
|
+
else if (!declared.has(name)) clashes.set(name, built.map((door) => door.workflow));
|
|
8120
8132
|
}
|
|
8121
|
-
return
|
|
8133
|
+
return {
|
|
8134
|
+
doors: new Map([...doors, ...declared]),
|
|
8135
|
+
clashes
|
|
8136
|
+
};
|
|
8122
8137
|
};
|
|
8138
|
+
/** The export named after the door owns it, else the sole builder; a clash of several builds no door. */
|
|
8139
|
+
const ownerOf = (name, built) => built.find((door) => door.workflow === name) ?? (built.length === 1 ? built[0] : void 0);
|
|
8123
8140
|
/** Sort the doors and check each names a startable workflow. */
|
|
8124
8141
|
const checkDoors = (declared, catalogue) => {
|
|
8125
8142
|
const doors = new Map([...declared].sort(([a], [b]) => a.localeCompare(b)));
|
|
@@ -8150,16 +8167,28 @@ const loadWorkflows = (module, shipped) => {
|
|
|
8150
8167
|
return workflow === void 0 ? [] : [[name, workflow]];
|
|
8151
8168
|
}));
|
|
8152
8169
|
const defaultWorkflow = built.get(value);
|
|
8170
|
+
const derived = rejections.length > 0 ? {
|
|
8171
|
+
doors: /* @__PURE__ */ new Map(),
|
|
8172
|
+
clashes: /* @__PURE__ */ new Map()
|
|
8173
|
+
} : derivedDoors(catalogue, doorsFrom(exports));
|
|
8153
8174
|
return {
|
|
8154
8175
|
file,
|
|
8155
8176
|
catalogue,
|
|
8156
|
-
doors:
|
|
8177
|
+
doors: checkDoors(derived.doors, catalogue),
|
|
8157
8178
|
default: defaultWorkflow && {
|
|
8158
8179
|
...defaultWorkflow,
|
|
8159
8180
|
name: defaultName
|
|
8160
8181
|
},
|
|
8161
8182
|
config: compileConfig(exports["config"], module.filepath),
|
|
8162
|
-
diagnostics: nameRejections(rejections)
|
|
8183
|
+
diagnostics: [...nameRejections(rejections), ...[...derived.clashes].map(([name, builders]) => {
|
|
8184
|
+
const names = [...builders].sort().map((b) => JSON.stringify(b));
|
|
8185
|
+
return {
|
|
8186
|
+
severity: "warning",
|
|
8187
|
+
message: `door "${name}" is built by workflows ${names.slice(0, -1).join(", ")} and ${names.at(-1)} and none is exported under that name — declare it in the "doors" export`,
|
|
8188
|
+
path: ["doors", name],
|
|
8189
|
+
origin: file.origin
|
|
8190
|
+
};
|
|
8191
|
+
})]
|
|
8163
8192
|
};
|
|
8164
8193
|
};
|
|
8165
8194
|
const evaluateFromDisk = (module, shipped) => {
|
|
@@ -8867,22 +8896,14 @@ const runAtTip = (git, def, tip) => gen(function* () {
|
|
|
8867
8896
|
if (closing.length === 0) closing = yield* git.commitHistory(void 0, tip.hash);
|
|
8868
8897
|
return yield* runOf([...before, ...closing], { ...location }, tip.hash);
|
|
8869
8898
|
});
|
|
8870
|
-
|
|
8871
|
-
const summaryRun = gen(function* () {
|
|
8872
|
-
const git = yield* GitService;
|
|
8873
|
-
const def = (yield* (yield* ConfigService).load).workflow;
|
|
8899
|
+
const runAtHead = (git, def) => gen(function* () {
|
|
8874
8900
|
return yield* runAtTip(git, def, (yield* git.subjectHistory(1, 0))[0]);
|
|
8875
8901
|
});
|
|
8876
|
-
/** The process
|
|
8877
|
-
const
|
|
8902
|
+
/** The process HEAD closes or sits inside — `gtd summary`'s run. */
|
|
8903
|
+
const summaryRun = gen(function* () {
|
|
8878
8904
|
const git = yield* GitService;
|
|
8879
8905
|
const def = (yield* (yield* ConfigService).load).workflow;
|
|
8880
|
-
|
|
8881
|
-
const subject = yield* git.lastCommitSubject(hash);
|
|
8882
|
-
return yield* runAtTip(git, def, {
|
|
8883
|
-
hash,
|
|
8884
|
-
subject
|
|
8885
|
-
});
|
|
8906
|
+
return yield* runAtHead(git, def);
|
|
8886
8907
|
});
|
|
8887
8908
|
const commitTree = (workspace, hash) => {
|
|
8888
8909
|
let entries;
|
|
@@ -9211,12 +9232,54 @@ const restAt = (ref, startOverrides = {}) => gen(function* () {
|
|
|
9211
9232
|
total: run.costEntries.reduce((sum, entry) => sum + entry.cost, 0),
|
|
9212
9233
|
byModel: costByModel(run.costEntries)
|
|
9213
9234
|
},
|
|
9214
|
-
setup
|
|
9215
|
-
slices: sliceRangesOf(outcome.slices, run)
|
|
9235
|
+
setup
|
|
9216
9236
|
};
|
|
9217
9237
|
});
|
|
9218
9238
|
const currentRest = restAt(void 0);
|
|
9219
9239
|
/**
|
|
9240
|
+
* The slices of the process HEAD closes or sits inside, current episode only,
|
|
9241
|
+
* stack order. Reads like `historyTrail`, never `restAt`: it narrates and
|
|
9242
|
+
* writes nothing, and returns a version warning for a closed run.
|
|
9243
|
+
*/
|
|
9244
|
+
const processSlices = gen(function* () {
|
|
9245
|
+
const git = yield* GitService;
|
|
9246
|
+
const config = yield* (yield* ConfigService).load;
|
|
9247
|
+
const workspace = yield* Workspace;
|
|
9248
|
+
const host = yield* Host;
|
|
9249
|
+
const run = yield* runAtHead(git, config.workflow);
|
|
9250
|
+
if (run.trace.length === 0) return yield* fail$5(/* @__PURE__ */ new Error("gtd slices: refused — HEAD neither closes nor sits inside a process"));
|
|
9251
|
+
const closing = run.closingHash;
|
|
9252
|
+
let outcome;
|
|
9253
|
+
let versionWarning;
|
|
9254
|
+
if (closing !== void 0) {
|
|
9255
|
+
const replayed = yield* replayFinishedProcess(config, run, closing, {
|
|
9256
|
+
command: "gtd slices",
|
|
9257
|
+
configReason: "HEAD's process replays under today's config; revert or stash the edit to list its slices",
|
|
9258
|
+
notEnded: (o) => slicesDivergence(run, o)
|
|
9259
|
+
});
|
|
9260
|
+
outcome = replayed.outcome;
|
|
9261
|
+
versionWarning = replayed.versionWarning;
|
|
9262
|
+
} else {
|
|
9263
|
+
const { setup } = yield* setupFor(config, run, workspace, host);
|
|
9264
|
+
outcome = yield* promise(() => replayFor(setup));
|
|
9265
|
+
if (outcome.kind !== "rest") return yield* fail$5(slicesDivergence(run, outcome));
|
|
9266
|
+
}
|
|
9267
|
+
return {
|
|
9268
|
+
slices: sliceRangesOf(outcome.slices, run).map(({ name, base, tip, open }) => ({
|
|
9269
|
+
name,
|
|
9270
|
+
base,
|
|
9271
|
+
tip,
|
|
9272
|
+
closed: closing !== void 0 || !open
|
|
9273
|
+
})),
|
|
9274
|
+
...versionWarning !== void 0 ? { versionWarning } : {}
|
|
9275
|
+
};
|
|
9276
|
+
});
|
|
9277
|
+
/** Every `gtd slices` refusal reads `gtd slices: refused — <reason>`; replay's own `gtd: ` lead is dropped. */
|
|
9278
|
+
const slicesDivergence = (run, outcome) => {
|
|
9279
|
+
const reason = outcome.kind === "rest" && run.closingHash !== void 0 ? `${run.closingHash.slice(0, 7)} closes a process that does not end under today's config` : replayError(outcome)?.message.replace(/^gtd: /, "") ?? "replay failed";
|
|
9280
|
+
return /* @__PURE__ */ new Error(`gtd slices: refused — ${reason}`);
|
|
9281
|
+
};
|
|
9282
|
+
/**
|
|
9220
9283
|
* Whether `gtd --workflow <name>` can start a process here: the workflow must
|
|
9221
9284
|
* reach a step from the tree the opening commit would capture. That step is
|
|
9222
9285
|
* what the opening commit's subject names.
|
|
@@ -9450,7 +9513,7 @@ const inRepoConfigPaths = (root, files, recorded) => {
|
|
|
9450
9513
|
for (const file of files) paths.add(relative(root, resolve(root, file)).split(sep).join("/"));
|
|
9451
9514
|
return [...paths].filter((p) => p !== "" && !p.startsWith("..") && !isAbsolute(p)).sort();
|
|
9452
9515
|
};
|
|
9453
|
-
const configRefusal = (tip, paths) => /* @__PURE__ */ new Error(
|
|
9516
|
+
const configRefusal = (command, tip, paths, reason = "A history ref replays under today's config, so it is unreadable after the config changes") => /* @__PURE__ */ new Error(`${command}: refused — config differs at ${tip.slice(0, 7)}: ${paths.join(", ")}. ${reason}`);
|
|
9454
9517
|
const differingConfigPaths = (git, workspace, root, config, run, hash) => gen(function* () {
|
|
9455
9518
|
const differing = [];
|
|
9456
9519
|
for (const path of inRepoConfigPaths(root, Object.keys(config.fingerprint), run.pinnedConfig?.files ?? {})) if ((yield* orElseSucceed(git.readFileAtRef(hash, path), () => void 0)) !== workspace.readSync(path)) differing.push(path);
|
|
@@ -9481,22 +9544,19 @@ const versionWarningFor = (recorded) => recorded === GTD_VERSION ? void 0 : `gtd
|
|
|
9481
9544
|
const historyTrail = (tip) => gen(function* () {
|
|
9482
9545
|
const git = yield* GitService;
|
|
9483
9546
|
const config = yield* (yield* ConfigService).load;
|
|
9484
|
-
const workspace = yield* Workspace;
|
|
9485
|
-
const host = yield* Host;
|
|
9486
9547
|
const hash = yield* git.resolveRef(tip);
|
|
9487
|
-
const
|
|
9488
|
-
const
|
|
9489
|
-
|
|
9490
|
-
|
|
9491
|
-
|
|
9492
|
-
const outcome = yield*
|
|
9493
|
-
|
|
9494
|
-
|
|
9495
|
-
|
|
9496
|
-
const versionWarning = versionWarningFor(recorded);
|
|
9548
|
+
const subject = yield* git.lastCommitSubject(hash);
|
|
9549
|
+
const run = yield* runAtTip(git, config.workflow, {
|
|
9550
|
+
hash,
|
|
9551
|
+
subject
|
|
9552
|
+
});
|
|
9553
|
+
const { outcome, recorded, versionWarning } = yield* replayFinishedProcess(config, run, hash, {
|
|
9554
|
+
command: "gtd history",
|
|
9555
|
+
notEnded: (o) => notClosedError(hash, o)
|
|
9556
|
+
});
|
|
9497
9557
|
return {
|
|
9498
9558
|
tip: hash,
|
|
9499
|
-
steps,
|
|
9559
|
+
steps: historySteps(run),
|
|
9500
9560
|
ended: outcome.via,
|
|
9501
9561
|
version: {
|
|
9502
9562
|
recorded,
|
|
@@ -9505,6 +9565,24 @@ const historyTrail = (tip) => gen(function* () {
|
|
|
9505
9565
|
...versionWarning !== void 0 ? { versionWarning } : {}
|
|
9506
9566
|
};
|
|
9507
9567
|
});
|
|
9568
|
+
/** Refuses on config drift before replaying, so a stale config never runs the flow. */
|
|
9569
|
+
const replayFinishedProcess = (config, run, hash, how) => gen(function* () {
|
|
9570
|
+
const git = yield* GitService;
|
|
9571
|
+
const workspace = yield* Workspace;
|
|
9572
|
+
const host = yield* Host;
|
|
9573
|
+
const differing = yield* differingConfigPaths(git, workspace, host.root, config, run, hash);
|
|
9574
|
+
if (differing.length > 0) return yield* fail$5(configRefusal(how.command, hash, differing, how.configReason));
|
|
9575
|
+
const { setup } = yield* setupFor(config, run, workspace, host);
|
|
9576
|
+
const outcome = yield* promise(() => replayFor(setup));
|
|
9577
|
+
if (outcome.kind !== "ended") return yield* fail$5(how.notEnded(outcome));
|
|
9578
|
+
const recorded = parseCommitMessage(run.episode.commits.find((c) => c.hash === hash)?.message ?? "").version;
|
|
9579
|
+
const versionWarning = versionWarningFor(recorded);
|
|
9580
|
+
return {
|
|
9581
|
+
outcome,
|
|
9582
|
+
recorded,
|
|
9583
|
+
...versionWarning !== void 0 ? { versionWarning } : {}
|
|
9584
|
+
};
|
|
9585
|
+
});
|
|
9508
9586
|
//#endregion
|
|
9509
9587
|
//#region src/emit/GitScript.ts
|
|
9510
9588
|
const shellQuote = (value) => `'${value.replace(/'/g, "'\\''")}'`;
|
|
@@ -10373,6 +10451,17 @@ const runDoorsCommand = (json, out) => gen(function* () {
|
|
|
10373
10451
|
if (json.kind === "document") out.write(`${JSON.stringify(document)}\n`);
|
|
10374
10452
|
else yield* writeSelection(out, document, json.path);
|
|
10375
10453
|
});
|
|
10454
|
+
const runSlicesCommand = (json, out) => gen(function* () {
|
|
10455
|
+
const { slices, versionWarning } = yield* processSlices;
|
|
10456
|
+
if (versionWarning !== void 0) yield* (yield* Narrator).warn(versionWarning);
|
|
10457
|
+
if (json.kind === "off") {
|
|
10458
|
+
out.write(slices.map((s) => `${s.name} ${s.base}..${s.tip}\n`).join(""));
|
|
10459
|
+
return;
|
|
10460
|
+
}
|
|
10461
|
+
const document = slicesDocument(slices);
|
|
10462
|
+
if (json.kind === "document") out.write(renderSlicesJson(document));
|
|
10463
|
+
else yield* writeSelection(out, document, json.path);
|
|
10464
|
+
});
|
|
10376
10465
|
/**
|
|
10377
10466
|
* Ends the process underway without completing it. Nothing is discarded: a
|
|
10378
10467
|
* `git reset --mixed` to the process's start commit leaves every turn
|
|
@@ -10708,6 +10797,7 @@ const dispatchVoidCommand = (command, json, out) => {
|
|
|
10708
10797
|
case "start": return runStartCommand(command.actor, command.workflow, command.vars, out, command.label);
|
|
10709
10798
|
case "door": return runDoorCommand(command.name, command.args, out);
|
|
10710
10799
|
case "doors": return runDoorsCommand(json, out);
|
|
10800
|
+
case "slices": return runSlicesCommand(json, out);
|
|
10711
10801
|
case "abandon": return runAbandonCommand(out);
|
|
10712
10802
|
case "restore": return runRestoreCommand(out);
|
|
10713
10803
|
case "next": return runNextCommand(json, out);
|
|
@@ -10764,12 +10854,12 @@ const FLAGS = [
|
|
|
10764
10854
|
name: "--json",
|
|
10765
10855
|
arity: "optional",
|
|
10766
10856
|
repeatable: false,
|
|
10767
|
-
scope: (kind) => kind === "next" || kind === "land" || kind === "judge" || kind === "judgeAnswer" || kind === "doors" || kind === "historyList" || kind === "historyShow" || kind === "strays",
|
|
10857
|
+
scope: (kind) => kind === "next" || kind === "land" || kind === "judge" || kind === "judgeAnswer" || kind === "doors" || kind === "slices" || kind === "historyList" || kind === "historyShow" || kind === "strays",
|
|
10768
10858
|
decode: ([raw]) => right(raw ?? ""),
|
|
10769
|
-
scopeError: "gtd: --json is only valid for `gtd next`/`gtd land`/`gtd judge`/`gtd judge answer`/`gtd doors`/`gtd history list`/`gtd history show`/`gtd strays` — every other command prints plain text; see `gtd install` for the driver protocol briefing",
|
|
10859
|
+
scopeError: "gtd: --json is only valid for `gtd next`/`gtd land`/`gtd judge`/`gtd judge answer`/`gtd doors`/`gtd slices`/`gtd history list`/`gtd history show`/`gtd strays` — every other command prints plain text; see `gtd install` for the driver protocol briefing",
|
|
10770
10860
|
valueHint: "<path>",
|
|
10771
10861
|
help: [
|
|
10772
|
-
"(gtd next/gtd land/gtd judge/gtd judge answer/gtd doors/gtd history",
|
|
10862
|
+
"(gtd next/gtd land/gtd judge/gtd judge answer/gtd doors/gtd slices/gtd history",
|
|
10773
10863
|
"list/gtd history show/gtd strays only) output structured JSON. Bare --json prints the whole document;",
|
|
10774
10864
|
"--json=<path> (a dotted key path into that document, e.g.",
|
|
10775
10865
|
"kind, content, session.id) prints just that value: a",
|
|
@@ -11222,10 +11312,10 @@ const COMMAND_ROWS = [
|
|
|
11222
11312
|
"positional args to that workflow's process settings — the",
|
|
11223
11313
|
"same start `gtd --workflow` makes, printed the same way.",
|
|
11224
11314
|
"Each workflow the config exports brings its own doors (gtd's",
|
|
11225
|
-
"`fix
|
|
11226
|
-
"the config's `doors` export. An
|
|
11227
|
-
"too many args, is a usage error
|
|
11228
|
-
"the door's synopsis"
|
|
11315
|
+
"`fix`, `review [base]`, `rollout <flag>` and",
|
|
11316
|
+
"`cleanup <flag>`), plus the config's `doors` export. An",
|
|
11317
|
+
"unknown door, or too few or too many args, is a usage error",
|
|
11318
|
+
"(exit 2) naming the doors or the door's synopsis"
|
|
11229
11319
|
]
|
|
11230
11320
|
},
|
|
11231
11321
|
{
|
|
@@ -11239,6 +11329,25 @@ const COMMAND_ROWS = [
|
|
|
11239
11329
|
"value off it. Writes nothing"
|
|
11240
11330
|
]
|
|
11241
11331
|
},
|
|
11332
|
+
{
|
|
11333
|
+
token: "slices",
|
|
11334
|
+
kind: "slices",
|
|
11335
|
+
arity: "none",
|
|
11336
|
+
details: [
|
|
11337
|
+
"List the slices of the process HEAD closes or sits inside,",
|
|
11338
|
+
"first slice first: one `name base..tip` line each (full",
|
|
11339
|
+
"hashes; plain output carries no promise). --json prints",
|
|
11340
|
+
"{ schema: 1, slices: [{ name, base, tip, closed }] };",
|
|
11341
|
+
"--json=<path> reads one value off it. Writes nothing: no",
|
|
11342
|
+
"git, no state transition, no session identity. When HEAD",
|
|
11343
|
+
"closes a process, every slice is listed as closed; a gtd",
|
|
11344
|
+
"version mismatch only warns on stderr. Refuses (exit 1)",
|
|
11345
|
+
"when HEAD neither closes nor sits inside a process, or when",
|
|
11346
|
+
"today's config disagrees with history about the process:",
|
|
11347
|
+
"HEAD closes it and its config files differ from the working",
|
|
11348
|
+
"tree, or the replay diverged"
|
|
11349
|
+
]
|
|
11350
|
+
},
|
|
11242
11351
|
{
|
|
11243
11352
|
token: "judge",
|
|
11244
11353
|
kind: "judge",
|
package/dist/wire.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { C as
|
|
2
|
-
export { ACCESS_REFUSAL, BEAT_KINDS, DEMAND_BRIEFING, EXIT_CODES, EXIT_OK, EXIT_RUNTIME_ERROR, EXIT_SIGINT, EXIT_SIGTERM, EXIT_STRAYS_FOUND, EXIT_USAGE_ERROR, UNATTRIBUTED_MODEL, WIRE_SCHEMA, beatDocument, beatKindOf, demandOf, globMatches, judgeJson, landFields, landProseText, noopText, renderBeatJson, renderBeatPlain, renderFormat, renderLandJson, renderLandPlain, resolveSession, selectPath, stallDiagnosis, statusOf };
|
|
1
|
+
import { A as WIRE_SCHEMA, C as EXIT_RUNTIME_ERROR, D as EXIT_USAGE_ERROR, E as EXIT_STRAYS_FOUND, O as ACCESS_REFUSAL, S as EXIT_OK, T as EXIT_SIGTERM, _ as BEAT_KINDS, a as selectPath, b as stallDiagnosis, c as landProseText, d as renderLandPlain, f as DEMAND_BRIEFING, g as statusOf, h as renderBeatJson, i as resolveSession, j as renderFormat, k as UNATTRIBUTED_MODEL, l as noopText, m as beatDocument, n as slicesDocument, o as judgeJson, p as renderBeatPlain, r as globMatches, s as landFields, t as renderSlicesJson, u as renderLandJson, v as beatKindOf, w as EXIT_SIGINT, x as EXIT_CODES, y as demandOf } from "./chunks/wire-D3Rc5r22.mjs";
|
|
2
|
+
export { ACCESS_REFUSAL, BEAT_KINDS, DEMAND_BRIEFING, EXIT_CODES, EXIT_OK, EXIT_RUNTIME_ERROR, EXIT_SIGINT, EXIT_SIGTERM, EXIT_STRAYS_FOUND, EXIT_USAGE_ERROR, UNATTRIBUTED_MODEL, WIRE_SCHEMA, beatDocument, beatKindOf, demandOf, globMatches, judgeJson, landFields, landProseText, noopText, renderBeatJson, renderBeatPlain, renderFormat, renderLandJson, renderLandPlain, renderSlicesJson, resolveSession, selectPath, slicesDocument, stallDiagnosis, statusOf };
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { agent, changes, glob, read, refuse, scope, start, vars } from "../flows.mjs";
|
|
2
|
-
import { a as flagShipped,
|
|
3
|
-
import { i as refusePendingHandOff, r as handOffTail } from "../chunks/tail-
|
|
4
|
-
import { i as requireFlag, r as registryEntry, t as flagReferences } from "../chunks/registry-CP3YuV9j.mjs";
|
|
2
|
+
import { a as flagShipped, ct as defaults, dt as flagReferences, i as skills, lt as envDefaults, mt as requireFlag, n as summary, pt as registryEntry, r as access, t as steering, v as coder } from "../chunks/reserved-BIsngMGL.mjs";
|
|
3
|
+
import { i as refusePendingHandOff, r as handOffTail } from "../chunks/tail-DHEaOHyp.mjs";
|
|
5
4
|
//#region src/workflows/cleanup/cleanup.ts
|
|
6
5
|
const removingPrompt = (name, registry) => `Clean up the release flag "${name}", which is default-on: delete its entry in ${registry}, and remove every \`GTD_FLAG_${name.toUpperCase()}\` check from the shipped sources. Keep the flag-on path; delete code only the off path reached, including tests that pin the off path.`;
|
|
7
6
|
const cleanupFlow = async () => {
|
|
@@ -29,7 +28,7 @@ const cleanupFlow = async () => {
|
|
|
29
28
|
});
|
|
30
29
|
await handOffTail(false, start(), guard);
|
|
31
30
|
};
|
|
32
|
-
const cleanup = flagShipped(cleanupFlow);
|
|
31
|
+
const cleanup = flagShipped(cleanupFlow, "cleanup");
|
|
33
32
|
//#endregion
|
|
34
33
|
//#region src/workflows/cleanup/index.ts
|
|
35
34
|
var cleanup_default = cleanup;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { answered, changes, changesSince, glob, hasThreadFor, head, human, judge, judgeDiffShape, kept, mark, numeric, quote, read, refuse, removeScript, requireAnswers, requireProgress, requireReplies, requireRevert, requireThreadsClosed, restoreScript, revertScript, run, scope, seamGate, start, vars } from "../flows.mjs";
|
|
2
2
|
import { review as review$1 } from "../workflow-kit.mjs";
|
|
3
|
-
import { $ as
|
|
4
|
-
import { a as tail, i as refusePendingHandOff, n as closeReview } from "../chunks/tail-
|
|
3
|
+
import { $ as reviewEditsCapture, A as architectureGateAnswerMessage, B as designTriagePrompt, C as reviewMissing, D as architectSystem, E as agentWithSkills, F as buildReviewTriageMessage, G as implementBuildingPrompt, I as builderSystem, J as packagesItemBuildingPrompt, K as implementFixPrompt, L as deferNotesScript, M as buildReviewAwaitAfterDeferMessage, N as buildReviewCollectingPrompt, O as architectureAuthorPrompt, P as buildReviewFixNitsPrompt, Q as reviewEditNotesCapture, R as designGateAnswerMessage, S as planner, T as reviewing, W as idleMessage, X as refactorBuildingPrompt, Y as packagesItemFixSuitePrompt, Z as refactorFixPrompt, _ as awaitReview, at as startGateBlockedMessage, ct as defaults, d as healthy, f as ARCHITECTURE, g as REVIEW, h as REQUIREMENTS, i as skills, it as seamSteerMessage, j as buildReviewAnswerQuestionsPrompt, k as architectureDecomposePrompt, l as gate, lt as envDefaults, n as summary, nt as reviewerSystem, o as shipped, ot as unwindFailedMessage, p as FEEDBACK, q as naivePassPrompt, r as access, rt as seamByHandMessage, st as unwindFailure, t as steering, tt as reviewNotesCapture, v as coder, x as fixRisks, y as fixCheck, z as designSystem } from "../chunks/reserved-BIsngMGL.mjs";
|
|
4
|
+
import { a as tail, i as refusePendingHandOff, n as closeReview } from "../chunks/tail-DHEaOHyp.mjs";
|
|
5
5
|
//#region src/workflows/feature/tier.ts
|
|
6
6
|
/** GTD_FLAG_NAIVE_PASS, GTD_FLAG_RISK_TIER: both pinned at process start, so a mid-process flip never moves a process. */
|
|
7
7
|
const riskTierOn = () => vars.flag_naive_pass === "1" && vars.flag_risk_tier === "1";
|
package/dist/workflows/fix.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { read, refuse, removeScript, run, scope, start } from "../flows.mjs";
|
|
2
2
|
import { d as reviewNotes, f as reviewRisks } from "../chunks/steering-DVILanVf.mjs";
|
|
3
|
-
import {
|
|
4
|
-
import { r as handOffTail, t as REVIEW_ORIG } from "../chunks/tail-
|
|
3
|
+
import { E as agentWithSkills, H as fixReviewNotesPrompt, V as finisherSystem, ct as defaults, g as REVIEW, i as skills, lt as envDefaults, n as summary, o as shipped, r as access, s as baseline, t as steering, v as coder } from "../chunks/reserved-BIsngMGL.mjs";
|
|
4
|
+
import { r as handOffTail, t as REVIEW_ORIG } from "../chunks/tail-DHEaOHyp.mjs";
|
|
5
5
|
//#region src/workflows/fix/fix.ts
|
|
6
6
|
/**
|
|
7
7
|
* Fix every note a human left in a handed-off `.gtd/REVIEW.md` and every
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { start } from "../flows.mjs";
|
|
2
|
-
import {
|
|
3
|
-
import { i as refusePendingHandOff, r as handOffTail } from "../chunks/tail-
|
|
2
|
+
import { ct as defaults, et as reviewGateBlockedMessage, i as skills, l as gate, lt as envDefaults, n as summary, o as shipped, r as access, t as steering } from "../chunks/reserved-BIsngMGL.mjs";
|
|
3
|
+
import { i as refusePendingHandOff, r as handOffTail } from "../chunks/tail-DHEaOHyp.mjs";
|
|
4
4
|
//#region src/workflows/review/review.ts
|
|
5
5
|
/**
|
|
6
6
|
* Pure review of everything since `reviewBase`: a green gate, the quality
|
|
@@ -1,9 +1,23 @@
|
|
|
1
|
-
import { agent, changesSince, head, read, refuse, scope, vars } from "../flows.mjs";
|
|
2
|
-
import { a as flagShipped,
|
|
3
|
-
import { i as requireFlag, n as flipProblems } from "../chunks/registry-CP3YuV9j.mjs";
|
|
1
|
+
import { agent, changesSince, glob, head, mark, read, refuse, removeScript, run, scope, vars } from "../flows.mjs";
|
|
2
|
+
import { a as flagShipped, ct as defaults, ft as flipProblems, i as skills, l as gate, lt as envDefaults, mt as requireFlag, n as summary, p as FEEDBACK, r as access, t as steering, ut as flagMentions, v as coder } from "../chunks/reserved-BIsngMGL.mjs";
|
|
4
3
|
//#region src/workflows/rollout/rollout.ts
|
|
5
4
|
const flippingPrompt = (name, registry) => `Roll out the release flag "${name}": in ${registry}, set \`defaultOn: true\` on its entry. Change nothing else — no other entry, no other field, no other file, no comments.`;
|
|
6
5
|
const suiteBlockedMessage = () => "The suite is red after the rollout. Fix it by hand — a rollout may only touch the flag's registry entry — then land.";
|
|
6
|
+
const MARKED_MENTIONS = 50;
|
|
7
|
+
const listMentions = async (registry, name, guard) => {
|
|
8
|
+
await run("mentions", removeScript([FEEDBACK]), { label: "Listing flag mentions" });
|
|
9
|
+
guard();
|
|
10
|
+
const mentions = flagMentions(glob("**").filter((path) => path !== registry && path !== ".gtd" && !path.startsWith(".gtd/")).sort().map((path) => ({
|
|
11
|
+
path,
|
|
12
|
+
text: read(path) ?? ""
|
|
13
|
+
})), name);
|
|
14
|
+
mark("flag-mentions", String(mentions.length));
|
|
15
|
+
for (const m of mentions.slice(0, MARKED_MENTIONS)) try {
|
|
16
|
+
mark("flag-mention", `${m.path}:${m.line}`);
|
|
17
|
+
} catch {
|
|
18
|
+
continue;
|
|
19
|
+
}
|
|
20
|
+
};
|
|
7
21
|
const rolloutFlow = async () => {
|
|
8
22
|
const registry = vars.flagRegistry ?? "";
|
|
9
23
|
const name = vars.flag ?? "";
|
|
@@ -23,9 +37,10 @@ const rolloutFlow = async () => {
|
|
|
23
37
|
});
|
|
24
38
|
guard();
|
|
25
39
|
await gate("suite", suiteBlockedMessage(), guard);
|
|
40
|
+
await listMentions(registry, name, guard);
|
|
26
41
|
});
|
|
27
42
|
};
|
|
28
|
-
const rollout = flagShipped(rolloutFlow);
|
|
43
|
+
const rollout = flagShipped(rolloutFlow, "rollout");
|
|
29
44
|
//#endregion
|
|
30
45
|
//#region src/workflows/rollout/index.ts
|
|
31
46
|
var rollout_default = rollout;
|