@pmelab/gtd 62.1.0 → 63.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 +7 -3
- package/dist/chunks/{testkit-CPzWnl3P.mjs → testkit-DwypA7cH.mjs} +137 -36
- package/dist/gtd.bundle.mjs +137 -36
- package/dist/gtd.core.mjs +84 -84
- package/dist/testkit/gherkin.mjs +1 -1
- package/dist/testkit.mjs +1 -1
- package/package.json +1 -1
- package/src/cli/Cli.ts +1 -1
- package/src/cli/Program.test.ts +6 -6
- package/src/cli/Program.ts +25 -15
- package/src/edge/Edge.test.ts +3 -3
- package/src/edge/Edge.ts +68 -3
- package/src/edge/index.ts +1 -0
- package/src/replay/Format.test.ts +26 -7
- package/src/replay/Format.ts +3 -2
- package/src/replay/Replay.test.ts +87 -0
- package/src/replay/Replay.ts +111 -23
- package/src/replay/Trailers.test.ts +10 -10
- package/src/replay/Trailers.ts +9 -0
- package/src/replay/index.ts +1 -0
- package/src/step/planStart.test.ts +2 -2
- package/src/step/planStep.test.ts +4 -4
package/README.md
CHANGED
|
@@ -641,7 +641,8 @@ writing `gtd bundle`'s output to `.gtd/process.gtdpin`, and its last, once the
|
|
|
641
641
|
flow has ended, an unpin beat removing it — the commit that closes the process.
|
|
642
642
|
Both are ordinary `script` beats a driver runs unattended; see
|
|
643
643
|
[Pin and unpin beats](https://github.com/pmelab/gtd/blob/main/docs/driver.md#pin-and-unpin-beats).
|
|
644
|
-
Histories with pin commits are history format 2, which an older gtd
|
|
644
|
+
Histories with pin commits are history format 2 or later, which an older gtd
|
|
645
|
+
refuses.
|
|
645
646
|
|
|
646
647
|
While a process is pinned, the `gtd` on your `PATH` hands every command over to
|
|
647
648
|
the pin, so editing the workflow or upgrading gtd mid-process changes nothing;
|
|
@@ -650,8 +651,11 @@ gtd notes on stderr when the live config differs from the pinned one. `ui`,
|
|
|
650
651
|
always run live, and `gtd --live <command>` runs the live engine for any other,
|
|
651
652
|
at the risk of misreading the process. To fix a workflow mid-process, edit it
|
|
652
653
|
and run `gtd repin`: its script commits a fresh pin from the live engine and
|
|
653
|
-
config, and the process continues on it.
|
|
654
|
-
|
|
654
|
+
config, and the process continues on it. Where the edited workflow no longer
|
|
655
|
+
replays the process, the repin continues from the divergence: the commits it
|
|
656
|
+
cannot replay stay as files, and the process rests at the step the edited
|
|
657
|
+
workflow reached (history format 3). A pin in a format your gtd does not know is
|
|
658
|
+
refused: upgrade gtd. See
|
|
655
659
|
[Pinned processes](https://github.com/pmelab/gtd/blob/main/docs/cli.md#pinned-processes).
|
|
656
660
|
|
|
657
661
|
## License
|
|
@@ -22810,9 +22810,9 @@ const withoutPath = (tree, hidden) => {
|
|
|
22810
22810
|
const OLDEST_READABLE_FORMAT = 1;
|
|
22811
22811
|
const short$1 = (hash) => hash.slice(0, 7);
|
|
22812
22812
|
const formatFault = (commits) => {
|
|
22813
|
-
const foreign = commits.find((c) => c.format < OLDEST_READABLE_FORMAT || c.format >
|
|
22813
|
+
const foreign = commits.find((c) => c.format < OLDEST_READABLE_FORMAT || c.format > 3);
|
|
22814
22814
|
if (foreign === void 0) return void 0;
|
|
22815
|
-
return `gtd: commit ${short$1(foreign.hash)} is written in history format ${foreign.format}, but this gtd reads formats ${OLDEST_READABLE_FORMAT} to
|
|
22815
|
+
return `gtd: commit ${short$1(foreign.hash)} is written in history format ${foreign.format}, but this gtd reads formats ${OLDEST_READABLE_FORMAT} to 3 — run a gtd release that reads format ${foreign.format} to continue this process`;
|
|
22816
22816
|
};
|
|
22817
22817
|
//#endregion
|
|
22818
22818
|
//#region src/replay/Trailers.ts
|
|
@@ -22943,6 +22943,9 @@ const TRAILER_READERS = {
|
|
|
22943
22943
|
"Gtd-Pin": (value, into) => {
|
|
22944
22944
|
if (value === "add" || value === "remove") into.pin ??= value;
|
|
22945
22945
|
},
|
|
22946
|
+
"Gtd-Anchor": (value, into) => {
|
|
22947
|
+
if (HASH_RE.test(value)) into.anchor ??= value;
|
|
22948
|
+
},
|
|
22946
22949
|
"Gtd-Format": (value, into) => {
|
|
22947
22950
|
if (FORMAT_RE.test(value)) into.format ??= Number(value);
|
|
22948
22951
|
},
|
|
@@ -22966,6 +22969,7 @@ const parseCommitMessage = (message) => {
|
|
|
22966
22969
|
kept: void 0,
|
|
22967
22970
|
approved: void 0,
|
|
22968
22971
|
pin: void 0,
|
|
22972
|
+
anchor: void 0,
|
|
22969
22973
|
judge: [],
|
|
22970
22974
|
marks: [],
|
|
22971
22975
|
vars: {},
|
|
@@ -22995,9 +22999,10 @@ const formatCommitMessage = (spec) => {
|
|
|
22995
22999
|
...(spec.marks ?? []).map((m) => `Gtd-Mark: ${m.name}=${m.value}`),
|
|
22996
23000
|
...trailer("Approved", spec.approved && encodeApproval(spec.approved)),
|
|
22997
23001
|
...trailer("Pin", spec.pin),
|
|
23002
|
+
...trailer("Anchor", spec.anchor),
|
|
22998
23003
|
...trailer("Branch", spec.branch),
|
|
22999
23004
|
...trailer("Version", spec.version),
|
|
23000
|
-
`Gtd-Format:
|
|
23005
|
+
`Gtd-Format: 3`
|
|
23001
23006
|
];
|
|
23002
23007
|
return `${formatSubject(spec.actor, spec.to, spec.from)}\n\n${lines.join("\n")}`;
|
|
23003
23008
|
};
|
|
@@ -23091,6 +23096,27 @@ const budgeted = (evidence, budgetBytes) => {
|
|
|
23091
23096
|
};
|
|
23092
23097
|
const sameSkills = (a, b) => (a?.length ?? 0) === (b?.length ?? 0) && (a ?? []).every((skill, i) => skill === b?.[i]);
|
|
23093
23098
|
const samePersona = (a, b) => a.model === b.model && a.system === b.system && sameSkills(a.skills, b.skills) && sameAccess(a.access, b.access);
|
|
23099
|
+
const anchorOf = (parsed) => parsed.pin === "add" ? parsed.anchor : void 0;
|
|
23100
|
+
const isAnchor = (commit) => anchorOf(commit.parsed) !== void 0;
|
|
23101
|
+
/**
|
|
23102
|
+
* The commits every anchor sets aside — those between the commit it names and
|
|
23103
|
+
* itself — or a fault for an anchor naming no commit still read.
|
|
23104
|
+
*/
|
|
23105
|
+
const setAsideBy = (base, all) => {
|
|
23106
|
+
const setAside = /* @__PURE__ */ new Set();
|
|
23107
|
+
if (all.every((c) => anchorOf(c.parsed) === void 0)) return { setAside };
|
|
23108
|
+
let kept = [];
|
|
23109
|
+
for (const commit of all) {
|
|
23110
|
+
const anchor = anchorOf(commit.parsed);
|
|
23111
|
+
if (anchor !== void 0) {
|
|
23112
|
+
const at = anchor === base ? -1 : kept.indexOf(anchor);
|
|
23113
|
+
if (anchor !== base && at === -1) return { fault: `gtd: commit ${short(commit.hash)} anchors the process at ${short(anchor)}, which is no commit replay reads in this episode` };
|
|
23114
|
+
for (const hash of kept.splice(at + 1)) setAside.add(hash);
|
|
23115
|
+
}
|
|
23116
|
+
kept.push(commit.hash);
|
|
23117
|
+
}
|
|
23118
|
+
return { setAside };
|
|
23119
|
+
};
|
|
23094
23120
|
/**
|
|
23095
23121
|
* Replay one episode: run its entry flow, answering each step from the
|
|
23096
23122
|
* episode's commits in order, until a step has no commit left — the rest.
|
|
@@ -23164,19 +23190,39 @@ const withoutPin = (input) => {
|
|
|
23164
23190
|
};
|
|
23165
23191
|
const replay = async (raw) => {
|
|
23166
23192
|
const input = withoutPin(raw);
|
|
23167
|
-
const
|
|
23193
|
+
const all = input.episode.commits.map((c) => ({
|
|
23168
23194
|
...c,
|
|
23169
23195
|
parsed: parseCommitMessage(c.message)
|
|
23170
23196
|
}));
|
|
23197
|
+
const resolved = setAsideBy(input.episode.base.hash, all);
|
|
23198
|
+
if ("fault" in resolved) return {
|
|
23199
|
+
kind: "failed",
|
|
23200
|
+
message: resolved.fault
|
|
23201
|
+
};
|
|
23202
|
+
const setAside = resolved.setAside;
|
|
23203
|
+
const commits = all.filter((c) => !setAside.has(c.hash));
|
|
23204
|
+
const pinned = (raw.episode.commits.at(-1)?.tree ?? raw.episode.base.tree).paths().includes(PIN_PATH);
|
|
23171
23205
|
const treeAt = (hash) => hash === input.episode.base.hash ? input.episode.base.tree : commits.find((c) => c.hash === hash)?.tree ?? (hash === input.start ? input.startTree : void 0);
|
|
23172
23206
|
let cursor = 0;
|
|
23173
23207
|
let pendingUsed = false;
|
|
23174
23208
|
let position = input.episode.base;
|
|
23175
23209
|
const skipPins = () => {
|
|
23176
|
-
while (cursor < commits.length && commits[cursor].parsed.pin !== void 0)
|
|
23210
|
+
while (cursor < commits.length && commits[cursor].parsed.pin !== void 0) {
|
|
23211
|
+
const commit = commits[cursor];
|
|
23212
|
+
cursor++;
|
|
23213
|
+
if (!isAnchor(commit)) continue;
|
|
23214
|
+
position = commit;
|
|
23215
|
+
const to = commit.parsed.parsed?.to;
|
|
23216
|
+
expectedNext = to === void 0 || to === UNPIN_STATE ? void 0 : {
|
|
23217
|
+
name: to,
|
|
23218
|
+
hash: commit.hash
|
|
23219
|
+
};
|
|
23220
|
+
anchorPending = expectedNext !== void 0;
|
|
23221
|
+
}
|
|
23177
23222
|
};
|
|
23178
23223
|
let previousPosition = input.episode.base;
|
|
23179
23224
|
let expectedNext;
|
|
23225
|
+
let anchorPending = false;
|
|
23180
23226
|
const occurrences = /* @__PURE__ */ new Map();
|
|
23181
23227
|
const personas = /* @__PURE__ */ new Map();
|
|
23182
23228
|
const scopes = [];
|
|
@@ -23319,14 +23365,18 @@ const replay = async (raw) => {
|
|
|
23319
23365
|
}
|
|
23320
23366
|
return samePersona(seen, identity) ? void 0 : `gtd: "${step.name}" runs with a different model, system prompt, skills or file access than an earlier agent step in its conversation in memory scope "${step.memoryScope || "root"}" — a conversation has one identity`;
|
|
23321
23367
|
};
|
|
23322
|
-
const DIVERGED = "the workflow changed under this process; run `gtd abandon` to start over";
|
|
23368
|
+
const DIVERGED = pinned ? "the workflow changed under this process; run `gtd repin` to continue under it from there, or `gtd abandon` to start over" : "the workflow changed under this process; run `gtd abandon` to start over";
|
|
23369
|
+
const diverged = (message, reached) => ({
|
|
23370
|
+
kind: "divergence",
|
|
23371
|
+
message: `${message} — ${DIVERGED}`,
|
|
23372
|
+
at: position.hash,
|
|
23373
|
+
reached: reached?.name
|
|
23374
|
+
});
|
|
23323
23375
|
const blockerAt = (step) => {
|
|
23324
23376
|
const expected = expectedNext;
|
|
23325
23377
|
expectedNext = void 0;
|
|
23326
|
-
|
|
23327
|
-
|
|
23328
|
-
message: `gtd: commit ${short(expected.hash)} names "${expected.name}" as the next step, but replay reached "${step.name}" — ${DIVERGED}`
|
|
23329
|
-
};
|
|
23378
|
+
anchorPending = false;
|
|
23379
|
+
if (expected !== void 0 && expected.name !== step.name) return diverged(`gtd: commit ${short(expected.hash)} names "${expected.name}" as the next step, but replay reached "${step.name}"`, step);
|
|
23330
23380
|
const error = unknownOptions(step) ?? approveConflict(step) ?? modeWithoutFile(step) ?? checkIdentity(step);
|
|
23331
23381
|
return error === void 0 ? void 0 : {
|
|
23332
23382
|
kind: "failed",
|
|
@@ -23337,33 +23387,30 @@ const replay = async (raw) => {
|
|
|
23337
23387
|
if (step.request.kind !== "human" || step.request.options.approve !== true) return void 0;
|
|
23338
23388
|
const approved = commit.parsed.approved;
|
|
23339
23389
|
if (approved !== void 0 && formatStepId(approved.step) === expected) return void 0;
|
|
23340
|
-
return {
|
|
23341
|
-
kind: "divergence",
|
|
23342
|
-
message: `gtd: commit ${short(commit.hash)} completes approval gate "${expected}" but records no Gtd-Approved for it — ${DIVERGED}`
|
|
23343
|
-
};
|
|
23390
|
+
return diverged(`gtd: commit ${short(commit.hash)} completes approval gate "${expected}" but records no Gtd-Approved for it`, step);
|
|
23344
23391
|
};
|
|
23345
23392
|
const consumeCommit = (step, commit) => {
|
|
23346
23393
|
const recorded = commit.parsed.step;
|
|
23347
23394
|
const expected = formatStepId(step.id);
|
|
23348
23395
|
if (recorded === void 0 || formatStepId(recorded) !== expected) {
|
|
23349
23396
|
const what = recorded === void 0 ? "records no step" : `records step "${formatStepId(recorded)}"`;
|
|
23350
|
-
return finish({
|
|
23351
|
-
kind: "divergence",
|
|
23352
|
-
message: `gtd: commit ${short(commit.hash)} ${what}, but replay expected "${expected}" — ${DIVERGED}`
|
|
23353
|
-
});
|
|
23397
|
+
return finish(diverged(`gtd: commit ${short(commit.hash)} ${what}, but replay expected "${expected}"`, step));
|
|
23354
23398
|
}
|
|
23355
23399
|
const unapproved = missingApproval(step, commit, expected);
|
|
23356
23400
|
if (unapproved !== void 0) return finish(unapproved);
|
|
23357
23401
|
cursor++;
|
|
23358
23402
|
advance(commit);
|
|
23403
|
+
expectNextOf(commit);
|
|
23359
23404
|
skipPins();
|
|
23360
23405
|
noteKept(step, commit);
|
|
23406
|
+
return replyFor(step.request, commit.parsed.judge);
|
|
23407
|
+
};
|
|
23408
|
+
const expectNextOf = (commit) => {
|
|
23361
23409
|
const to = commit.parsed.parsed?.to;
|
|
23362
23410
|
if (to !== void 0 && to !== UNPIN_STATE) expectedNext = {
|
|
23363
23411
|
name: to,
|
|
23364
23412
|
hash: commit.hash
|
|
23365
23413
|
};
|
|
23366
|
-
return replyFor(step.request, commit.parsed.judge);
|
|
23367
23414
|
};
|
|
23368
23415
|
const noteKept = (step, commit) => {
|
|
23369
23416
|
if (commit.parsed.kept === void 0) return;
|
|
@@ -23435,10 +23482,8 @@ const replay = async (raw) => {
|
|
|
23435
23482
|
};
|
|
23436
23483
|
const endOfFlow = (via) => {
|
|
23437
23484
|
skipPins();
|
|
23438
|
-
|
|
23439
|
-
|
|
23440
|
-
message: `gtd: the flow ended, but commit ${short(commits[cursor].hash)} continues the episode — ${DIVERGED}`
|
|
23441
|
-
} : {
|
|
23485
|
+
if (anchorPending && cursor === commits.length) return diverged(`gtd: the flow ended, but commit ${short(expectedNext.hash)} continues the process at "${expectedNext.name}"`, void 0);
|
|
23486
|
+
return cursor < commits.length ? diverged(`gtd: the flow ended, but commit ${short(commits[cursor].hash)} continues the episode`, void 0) : {
|
|
23442
23487
|
kind: "ended",
|
|
23443
23488
|
via,
|
|
23444
23489
|
trace,
|
|
@@ -23448,6 +23493,7 @@ const replay = async (raw) => {
|
|
|
23448
23493
|
};
|
|
23449
23494
|
const changesSince = (hash) => {
|
|
23450
23495
|
const tree = treeAt(hash);
|
|
23496
|
+
if (setAside.has(hash)) throw new Error(`gtd: changesSince(${hash}): a repin set ${short(hash)} aside — pass a hash this run read from head() or start()`);
|
|
23451
23497
|
if (tree === void 0) throw new Error(`gtd: changesSince(${hash}): ${hash} is not the episode base or one of its commits — pass a hash this run read from head() or start(), not one captured earlier, read from state, or from another branch`);
|
|
23452
23498
|
return changesBetween(tree, position.tree);
|
|
23453
23499
|
};
|
|
@@ -25378,11 +25424,21 @@ const approvalEntryOf = (hash, message) => message.approved === void 0 ? [] : [{
|
|
|
25378
25424
|
email: message.approved.email,
|
|
25379
25425
|
commit: hash
|
|
25380
25426
|
}];
|
|
25427
|
+
const setAsideIn = (history, location) => {
|
|
25428
|
+
const anchored = setAsideBy(hashAt(history, location.baseIndex) ?? EMPTY_TREE$1, history.slice(location.baseIndex + 1).map((c) => ({
|
|
25429
|
+
hash: c.hash,
|
|
25430
|
+
parsed: parseCommitMessage(c.message)
|
|
25431
|
+
})));
|
|
25432
|
+
return "setAside" in anchored ? anchored.setAside : /* @__PURE__ */ new Set();
|
|
25433
|
+
};
|
|
25381
25434
|
const buildRun = (history, location, closingHash) => {
|
|
25382
25435
|
const allProcessCommits = history.slice(location.processStart);
|
|
25383
25436
|
const allParsed = allProcessCommits.map((c) => parseCommitMessage(c.message));
|
|
25384
25437
|
const processCommits = allProcessCommits.filter((_, i) => allParsed[i].pin !== "add");
|
|
25385
25438
|
const parsed = allParsed.filter((m) => m.pin !== "add");
|
|
25439
|
+
const setAside = setAsideIn(history, location);
|
|
25440
|
+
const answering = parsed.filter((_, i) => !setAside.has(processCommits[i].hash));
|
|
25441
|
+
const answeringCommits = processCommits.filter((c) => !setAside.has(c.hash));
|
|
25386
25442
|
const first = parsed[0];
|
|
25387
25443
|
const startParentHash = hashAt(history, location.processStart - 1) ?? EMPTY_TREE$1;
|
|
25388
25444
|
const head = history[history.length - 1];
|
|
@@ -25394,10 +25450,10 @@ const buildRun = (history, location, closingHash) => {
|
|
|
25394
25450
|
diffBase: first?.reviewBase ?? startParentHash,
|
|
25395
25451
|
trace: processCommits.map((c, i) => traceEntryOf(c.hash, parsed[i])),
|
|
25396
25452
|
costEntries: parsed.flatMap((m, i) => costEntriesOf(processCommits[i].hash, m)),
|
|
25397
|
-
judgeVerdicts:
|
|
25453
|
+
judgeVerdicts: answering.flatMap((m) => m.judge),
|
|
25398
25454
|
pinCommitted: allParsed.some((m) => m.pin === "add"),
|
|
25399
|
-
marks:
|
|
25400
|
-
approvals:
|
|
25455
|
+
marks: answering.flatMap((m) => m.marks),
|
|
25456
|
+
approvals: answeringCommits.flatMap((c, i) => approvalEntryOf(c.hash, answering[i])),
|
|
25401
25457
|
pinnedVars: { ...first?.vars },
|
|
25402
25458
|
headTurn: headTurnOf(head),
|
|
25403
25459
|
closingHash,
|
|
@@ -25826,6 +25882,43 @@ const restAt = (ref, startOverrides = {}) => gen(function* () {
|
|
|
25826
25882
|
};
|
|
25827
25883
|
});
|
|
25828
25884
|
const currentRest = restAt(void 0);
|
|
25885
|
+
/**
|
|
25886
|
+
* Where `gtd repin` anchors a pinned process the live workflow no longer
|
|
25887
|
+
* replays: the commit replay last stood on, and the step the live workflow
|
|
25888
|
+
* reaches from there on HEAD's tree — the unpin when the flow ends instead.
|
|
25889
|
+
* `undefined` when there is nothing to anchor: no divergence, or no pin.
|
|
25890
|
+
*/
|
|
25891
|
+
const repinAnchor = gen(function* () {
|
|
25892
|
+
const git = yield* GitService;
|
|
25893
|
+
const config = yield* (yield* ConfigService).load;
|
|
25894
|
+
const workspace = yield* Workspace;
|
|
25895
|
+
const run = yield* computeProcessRun(git, config.workflow);
|
|
25896
|
+
const { def, setup } = yield* setupFor(config, run, workspace, yield* Host);
|
|
25897
|
+
const diverged = yield* promise(() => replayFor(setup));
|
|
25898
|
+
if (diverged.kind !== "divergence" || !headHasPin(workspace, run)) return void 0;
|
|
25899
|
+
const trial = {
|
|
25900
|
+
hash: "",
|
|
25901
|
+
message: formatCommitMessage({
|
|
25902
|
+
actor: "check",
|
|
25903
|
+
from: PIN_STATES.add,
|
|
25904
|
+
to: PIN_STATES.add,
|
|
25905
|
+
pin: "add",
|
|
25906
|
+
anchor: diverged.at
|
|
25907
|
+
}),
|
|
25908
|
+
tree: setup.episode.at(-1)?.tree ?? setup.base.tree
|
|
25909
|
+
};
|
|
25910
|
+
const outcome = yield* promise(() => replayFor({
|
|
25911
|
+
...setup,
|
|
25912
|
+
episode: [...setup.episode, trial]
|
|
25913
|
+
}));
|
|
25914
|
+
if (outcome.kind !== "divergence") return yield* fail$4(replayError(outcome) ?? /* @__PURE__ */ new Error("gtd repin: replay found no step"));
|
|
25915
|
+
const to = outcome.reached ?? PIN_STATES.remove;
|
|
25916
|
+
if (run.workflow === void 0 && to === def.initial) return yield* fail$4(/* @__PURE__ */ new Error(`gtd repin: the live workflow continues this process at "${to}", the default workflow's first step, where a finished process waits — run \`gtd abandon\` to start over`));
|
|
25917
|
+
return {
|
|
25918
|
+
at: diverged.at,
|
|
25919
|
+
to
|
|
25920
|
+
};
|
|
25921
|
+
});
|
|
25829
25922
|
/** The step the process rests at and the trace that reached it — at an unpin, the flow's last step. */
|
|
25830
25923
|
const restingOn = (outcome, pin) => {
|
|
25831
25924
|
if (outcome.kind === "rest") return {
|
|
@@ -27339,20 +27432,28 @@ const runRestoreCommand = (json, out) => gen(function* () {
|
|
|
27339
27432
|
]).required;
|
|
27340
27433
|
yield* writeScript(json, out, combinedScript(required, ""));
|
|
27341
27434
|
});
|
|
27342
|
-
/**
|
|
27343
|
-
|
|
27344
|
-
* so reading the rest first refuses a live workflow that cannot replay the
|
|
27345
|
-
* process before any pin is written.
|
|
27346
|
-
*/
|
|
27347
|
-
const runRepinCommand = (json, out) => gen(function* () {
|
|
27435
|
+
/** The step an ordinary repin rests at — the one the process rests at now. */
|
|
27436
|
+
const repinTarget = gen(function* () {
|
|
27348
27437
|
const rest = yield* currentRest;
|
|
27349
27438
|
const refusal = rest.pin === "add" ? "the next beat pins the process already — run it (`gtd next --json=content`), then land" : rest.pin === "remove" ? "the flow has ended — its unpin beat is next, so there is nothing to repin" : noProcessUnderway(rest) ? "no gtd process is underway — nothing to repin" : void 0;
|
|
27350
27439
|
if (refusal !== void 0) return yield* fail$4(/* @__PURE__ */ new Error(`gtd repin: ${refusal}`));
|
|
27440
|
+
return rest.step.name;
|
|
27441
|
+
});
|
|
27442
|
+
/**
|
|
27443
|
+
* `gtd repin`: the pin beat's script and landing, on demand. It runs live: a
|
|
27444
|
+
* pinned process the live workflow no longer replays is anchored where it
|
|
27445
|
+
* diverged, and any other failure to read the rest refuses before any pin is
|
|
27446
|
+
* written.
|
|
27447
|
+
*/
|
|
27448
|
+
const runRepinCommand = (json, out) => gen(function* () {
|
|
27449
|
+
const anchor = yield* repinAnchor;
|
|
27450
|
+
const to = anchor?.to ?? (yield* repinTarget);
|
|
27351
27451
|
const message = formatCommitMessage({
|
|
27352
27452
|
actor: "check",
|
|
27353
27453
|
from: PIN_STATES.add,
|
|
27354
|
-
to
|
|
27355
|
-
pin: "add"
|
|
27454
|
+
to,
|
|
27455
|
+
pin: "add",
|
|
27456
|
+
...anchor !== void 0 ? { anchor: anchor.at } : {}
|
|
27356
27457
|
});
|
|
27357
27458
|
yield* writeScript(json, out, combinedScript(emitScripts([
|
|
27358
27459
|
{
|
|
@@ -27365,7 +27466,7 @@ const runRepinCommand = (json, out) => gen(function* () {
|
|
|
27365
27466
|
},
|
|
27366
27467
|
{
|
|
27367
27468
|
kind: "outcome",
|
|
27368
|
-
command: transitionOutcome(PIN_STATES.add,
|
|
27469
|
+
command: transitionOutcome(PIN_STATES.add, to)
|
|
27369
27470
|
}
|
|
27370
27471
|
]).required, ""));
|
|
27371
27472
|
});
|
|
@@ -28039,7 +28140,7 @@ With no <name>, list every workflow and door it can start. An unknown <name>, th
|
|
|
28039
28140
|
arity: "none",
|
|
28040
28141
|
group: "Start and stop",
|
|
28041
28142
|
summary: "Pin the process to the live gtd and workflow",
|
|
28042
|
-
details: `Switch the process underway to the live engine and workflow, the way out when the workflow needs a fix mid-process: the printed script runs the pin beat again, writing \`gtd bundle\`'s output to .gtd/process.gtdpin and committing that file alone. Always runs on the gtd on PATH, never on the pin. Refuses when no process is underway, when the next beat is a pin or unpin beat already, or when the live workflow
|
|
28143
|
+
details: `Switch the process underway to the live engine and workflow, the way out when the workflow needs a fix mid-process: the printed script runs the pin beat again, writing \`gtd bundle\`'s output to .gtd/process.gtdpin and committing that file alone. Always runs on the gtd on PATH, never on the pin. When the live workflow no longer replays the process, the commit continues from the divergence: a Gtd-Anchor trailer names the last commit it replays, the later commits stay as files only, and the process rests at the step the live workflow reached there. Refuses when no process is underway, when the next beat is a pin or unpin beat already, when an unpinned process diverges, or when the live workflow would continue at the default workflow's first step.
|
|
28043
28144
|
|
|
28044
28145
|
s=$(gtd repin --json=script) && sh -c "$s"`
|
|
28045
28146
|
},
|