@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/dist/gtd.bundle.mjs
CHANGED
|
@@ -29501,9 +29501,9 @@ const withoutPath = (tree, hidden) => {
|
|
|
29501
29501
|
const OLDEST_READABLE_FORMAT = 1;
|
|
29502
29502
|
const short$1 = (hash) => hash.slice(0, 7);
|
|
29503
29503
|
const formatFault = (commits) => {
|
|
29504
|
-
const foreign = commits.find((c) => c.format < OLDEST_READABLE_FORMAT || c.format >
|
|
29504
|
+
const foreign = commits.find((c) => c.format < OLDEST_READABLE_FORMAT || c.format > 3);
|
|
29505
29505
|
if (foreign === void 0) return void 0;
|
|
29506
|
-
return `gtd: commit ${short$1(foreign.hash)} is written in history format ${foreign.format}, but this gtd reads formats ${OLDEST_READABLE_FORMAT} to
|
|
29506
|
+
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`;
|
|
29507
29507
|
};
|
|
29508
29508
|
//#endregion
|
|
29509
29509
|
//#region src/replay/Trailers.ts
|
|
@@ -29634,6 +29634,9 @@ const TRAILER_READERS = {
|
|
|
29634
29634
|
"Gtd-Pin": (value, into) => {
|
|
29635
29635
|
if (value === "add" || value === "remove") into.pin ??= value;
|
|
29636
29636
|
},
|
|
29637
|
+
"Gtd-Anchor": (value, into) => {
|
|
29638
|
+
if (HASH_RE.test(value)) into.anchor ??= value;
|
|
29639
|
+
},
|
|
29637
29640
|
"Gtd-Format": (value, into) => {
|
|
29638
29641
|
if (FORMAT_RE.test(value)) into.format ??= Number(value);
|
|
29639
29642
|
},
|
|
@@ -29657,6 +29660,7 @@ const parseCommitMessage = (message) => {
|
|
|
29657
29660
|
kept: void 0,
|
|
29658
29661
|
approved: void 0,
|
|
29659
29662
|
pin: void 0,
|
|
29663
|
+
anchor: void 0,
|
|
29660
29664
|
judge: [],
|
|
29661
29665
|
marks: [],
|
|
29662
29666
|
vars: {},
|
|
@@ -29686,9 +29690,10 @@ const formatCommitMessage = (spec) => {
|
|
|
29686
29690
|
...(spec.marks ?? []).map((m) => `Gtd-Mark: ${m.name}=${m.value}`),
|
|
29687
29691
|
...trailer("Approved", spec.approved && encodeApproval(spec.approved)),
|
|
29688
29692
|
...trailer("Pin", spec.pin),
|
|
29693
|
+
...trailer("Anchor", spec.anchor),
|
|
29689
29694
|
...trailer("Branch", spec.branch),
|
|
29690
29695
|
...trailer("Version", spec.version),
|
|
29691
|
-
`Gtd-Format:
|
|
29696
|
+
`Gtd-Format: 3`
|
|
29692
29697
|
];
|
|
29693
29698
|
return `${formatSubject(spec.actor, spec.to, spec.from)}\n\n${lines.join("\n")}`;
|
|
29694
29699
|
};
|
|
@@ -29782,6 +29787,27 @@ const budgeted = (evidence, budgetBytes) => {
|
|
|
29782
29787
|
};
|
|
29783
29788
|
const sameSkills = (a, b) => (a?.length ?? 0) === (b?.length ?? 0) && (a ?? []).every((skill, i) => skill === b?.[i]);
|
|
29784
29789
|
const samePersona = (a, b) => a.model === b.model && a.system === b.system && sameSkills(a.skills, b.skills) && sameAccess(a.access, b.access);
|
|
29790
|
+
const anchorOf = (parsed) => parsed.pin === "add" ? parsed.anchor : void 0;
|
|
29791
|
+
const isAnchor = (commit) => anchorOf(commit.parsed) !== void 0;
|
|
29792
|
+
/**
|
|
29793
|
+
* The commits every anchor sets aside — those between the commit it names and
|
|
29794
|
+
* itself — or a fault for an anchor naming no commit still read.
|
|
29795
|
+
*/
|
|
29796
|
+
const setAsideBy = (base, all) => {
|
|
29797
|
+
const setAside = /* @__PURE__ */ new Set();
|
|
29798
|
+
if (all.every((c) => anchorOf(c.parsed) === void 0)) return { setAside };
|
|
29799
|
+
let kept = [];
|
|
29800
|
+
for (const commit of all) {
|
|
29801
|
+
const anchor = anchorOf(commit.parsed);
|
|
29802
|
+
if (anchor !== void 0) {
|
|
29803
|
+
const at = anchor === base ? -1 : kept.indexOf(anchor);
|
|
29804
|
+
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` };
|
|
29805
|
+
for (const hash of kept.splice(at + 1)) setAside.add(hash);
|
|
29806
|
+
}
|
|
29807
|
+
kept.push(commit.hash);
|
|
29808
|
+
}
|
|
29809
|
+
return { setAside };
|
|
29810
|
+
};
|
|
29785
29811
|
/**
|
|
29786
29812
|
* Replay one episode: run its entry flow, answering each step from the
|
|
29787
29813
|
* episode's commits in order, until a step has no commit left — the rest.
|
|
@@ -29855,19 +29881,39 @@ const withoutPin = (input) => {
|
|
|
29855
29881
|
};
|
|
29856
29882
|
const replay = async (raw) => {
|
|
29857
29883
|
const input = withoutPin(raw);
|
|
29858
|
-
const
|
|
29884
|
+
const all = input.episode.commits.map((c) => ({
|
|
29859
29885
|
...c,
|
|
29860
29886
|
parsed: parseCommitMessage(c.message)
|
|
29861
29887
|
}));
|
|
29888
|
+
const resolved = setAsideBy(input.episode.base.hash, all);
|
|
29889
|
+
if ("fault" in resolved) return {
|
|
29890
|
+
kind: "failed",
|
|
29891
|
+
message: resolved.fault
|
|
29892
|
+
};
|
|
29893
|
+
const setAside = resolved.setAside;
|
|
29894
|
+
const commits = all.filter((c) => !setAside.has(c.hash));
|
|
29895
|
+
const pinned = (raw.episode.commits.at(-1)?.tree ?? raw.episode.base.tree).paths().includes(PIN_PATH);
|
|
29862
29896
|
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);
|
|
29863
29897
|
let cursor = 0;
|
|
29864
29898
|
let pendingUsed = false;
|
|
29865
29899
|
let position = input.episode.base;
|
|
29866
29900
|
const skipPins = () => {
|
|
29867
|
-
while (cursor < commits.length && commits[cursor].parsed.pin !== void 0)
|
|
29901
|
+
while (cursor < commits.length && commits[cursor].parsed.pin !== void 0) {
|
|
29902
|
+
const commit = commits[cursor];
|
|
29903
|
+
cursor++;
|
|
29904
|
+
if (!isAnchor(commit)) continue;
|
|
29905
|
+
position = commit;
|
|
29906
|
+
const to = commit.parsed.parsed?.to;
|
|
29907
|
+
expectedNext = to === void 0 || to === UNPIN_STATE ? void 0 : {
|
|
29908
|
+
name: to,
|
|
29909
|
+
hash: commit.hash
|
|
29910
|
+
};
|
|
29911
|
+
anchorPending = expectedNext !== void 0;
|
|
29912
|
+
}
|
|
29868
29913
|
};
|
|
29869
29914
|
let previousPosition = input.episode.base;
|
|
29870
29915
|
let expectedNext;
|
|
29916
|
+
let anchorPending = false;
|
|
29871
29917
|
const occurrences = /* @__PURE__ */ new Map();
|
|
29872
29918
|
const personas = /* @__PURE__ */ new Map();
|
|
29873
29919
|
const scopes = [];
|
|
@@ -30010,14 +30056,18 @@ const replay = async (raw) => {
|
|
|
30010
30056
|
}
|
|
30011
30057
|
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`;
|
|
30012
30058
|
};
|
|
30013
|
-
const DIVERGED = "the workflow changed under this process; run `gtd abandon` to start over";
|
|
30059
|
+
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";
|
|
30060
|
+
const diverged = (message, reached) => ({
|
|
30061
|
+
kind: "divergence",
|
|
30062
|
+
message: `${message} — ${DIVERGED}`,
|
|
30063
|
+
at: position.hash,
|
|
30064
|
+
reached: reached?.name
|
|
30065
|
+
});
|
|
30014
30066
|
const blockerAt = (step) => {
|
|
30015
30067
|
const expected = expectedNext;
|
|
30016
30068
|
expectedNext = void 0;
|
|
30017
|
-
|
|
30018
|
-
|
|
30019
|
-
message: `gtd: commit ${short(expected.hash)} names "${expected.name}" as the next step, but replay reached "${step.name}" — ${DIVERGED}`
|
|
30020
|
-
};
|
|
30069
|
+
anchorPending = false;
|
|
30070
|
+
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);
|
|
30021
30071
|
const error = unknownOptions(step) ?? approveConflict(step) ?? modeWithoutFile(step) ?? checkIdentity(step);
|
|
30022
30072
|
return error === void 0 ? void 0 : {
|
|
30023
30073
|
kind: "failed",
|
|
@@ -30028,33 +30078,30 @@ const replay = async (raw) => {
|
|
|
30028
30078
|
if (step.request.kind !== "human" || step.request.options.approve !== true) return void 0;
|
|
30029
30079
|
const approved = commit.parsed.approved;
|
|
30030
30080
|
if (approved !== void 0 && formatStepId(approved.step) === expected) return void 0;
|
|
30031
|
-
return {
|
|
30032
|
-
kind: "divergence",
|
|
30033
|
-
message: `gtd: commit ${short(commit.hash)} completes approval gate "${expected}" but records no Gtd-Approved for it — ${DIVERGED}`
|
|
30034
|
-
};
|
|
30081
|
+
return diverged(`gtd: commit ${short(commit.hash)} completes approval gate "${expected}" but records no Gtd-Approved for it`, step);
|
|
30035
30082
|
};
|
|
30036
30083
|
const consumeCommit = (step, commit) => {
|
|
30037
30084
|
const recorded = commit.parsed.step;
|
|
30038
30085
|
const expected = formatStepId(step.id);
|
|
30039
30086
|
if (recorded === void 0 || formatStepId(recorded) !== expected) {
|
|
30040
30087
|
const what = recorded === void 0 ? "records no step" : `records step "${formatStepId(recorded)}"`;
|
|
30041
|
-
return finish({
|
|
30042
|
-
kind: "divergence",
|
|
30043
|
-
message: `gtd: commit ${short(commit.hash)} ${what}, but replay expected "${expected}" — ${DIVERGED}`
|
|
30044
|
-
});
|
|
30088
|
+
return finish(diverged(`gtd: commit ${short(commit.hash)} ${what}, but replay expected "${expected}"`, step));
|
|
30045
30089
|
}
|
|
30046
30090
|
const unapproved = missingApproval(step, commit, expected);
|
|
30047
30091
|
if (unapproved !== void 0) return finish(unapproved);
|
|
30048
30092
|
cursor++;
|
|
30049
30093
|
advance(commit);
|
|
30094
|
+
expectNextOf(commit);
|
|
30050
30095
|
skipPins();
|
|
30051
30096
|
noteKept(step, commit);
|
|
30097
|
+
return replyFor(step.request, commit.parsed.judge);
|
|
30098
|
+
};
|
|
30099
|
+
const expectNextOf = (commit) => {
|
|
30052
30100
|
const to = commit.parsed.parsed?.to;
|
|
30053
30101
|
if (to !== void 0 && to !== UNPIN_STATE) expectedNext = {
|
|
30054
30102
|
name: to,
|
|
30055
30103
|
hash: commit.hash
|
|
30056
30104
|
};
|
|
30057
|
-
return replyFor(step.request, commit.parsed.judge);
|
|
30058
30105
|
};
|
|
30059
30106
|
const noteKept = (step, commit) => {
|
|
30060
30107
|
if (commit.parsed.kept === void 0) return;
|
|
@@ -30126,10 +30173,8 @@ const replay = async (raw) => {
|
|
|
30126
30173
|
};
|
|
30127
30174
|
const endOfFlow = (via) => {
|
|
30128
30175
|
skipPins();
|
|
30129
|
-
|
|
30130
|
-
|
|
30131
|
-
message: `gtd: the flow ended, but commit ${short(commits[cursor].hash)} continues the episode — ${DIVERGED}`
|
|
30132
|
-
} : {
|
|
30176
|
+
if (anchorPending && cursor === commits.length) return diverged(`gtd: the flow ended, but commit ${short(expectedNext.hash)} continues the process at "${expectedNext.name}"`, void 0);
|
|
30177
|
+
return cursor < commits.length ? diverged(`gtd: the flow ended, but commit ${short(commits[cursor].hash)} continues the episode`, void 0) : {
|
|
30133
30178
|
kind: "ended",
|
|
30134
30179
|
via,
|
|
30135
30180
|
trace,
|
|
@@ -30139,6 +30184,7 @@ const replay = async (raw) => {
|
|
|
30139
30184
|
};
|
|
30140
30185
|
const changesSince = (hash) => {
|
|
30141
30186
|
const tree = treeAt(hash);
|
|
30187
|
+
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()`);
|
|
30142
30188
|
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`);
|
|
30143
30189
|
return changesBetween(tree, position.tree);
|
|
30144
30190
|
};
|
|
@@ -32096,11 +32142,21 @@ const approvalEntryOf = (hash, message) => message.approved === void 0 ? [] : [{
|
|
|
32096
32142
|
email: message.approved.email,
|
|
32097
32143
|
commit: hash
|
|
32098
32144
|
}];
|
|
32145
|
+
const setAsideIn = (history, location) => {
|
|
32146
|
+
const anchored = setAsideBy(hashAt(history, location.baseIndex) ?? EMPTY_TREE$1, history.slice(location.baseIndex + 1).map((c) => ({
|
|
32147
|
+
hash: c.hash,
|
|
32148
|
+
parsed: parseCommitMessage(c.message)
|
|
32149
|
+
})));
|
|
32150
|
+
return "setAside" in anchored ? anchored.setAside : /* @__PURE__ */ new Set();
|
|
32151
|
+
};
|
|
32099
32152
|
const buildRun = (history, location, closingHash) => {
|
|
32100
32153
|
const allProcessCommits = history.slice(location.processStart);
|
|
32101
32154
|
const allParsed = allProcessCommits.map((c) => parseCommitMessage(c.message));
|
|
32102
32155
|
const processCommits = allProcessCommits.filter((_, i) => allParsed[i].pin !== "add");
|
|
32103
32156
|
const parsed = allParsed.filter((m) => m.pin !== "add");
|
|
32157
|
+
const setAside = setAsideIn(history, location);
|
|
32158
|
+
const answering = parsed.filter((_, i) => !setAside.has(processCommits[i].hash));
|
|
32159
|
+
const answeringCommits = processCommits.filter((c) => !setAside.has(c.hash));
|
|
32104
32160
|
const first = parsed[0];
|
|
32105
32161
|
const startParentHash = hashAt(history, location.processStart - 1) ?? EMPTY_TREE$1;
|
|
32106
32162
|
const head = history[history.length - 1];
|
|
@@ -32112,10 +32168,10 @@ const buildRun = (history, location, closingHash) => {
|
|
|
32112
32168
|
diffBase: first?.reviewBase ?? startParentHash,
|
|
32113
32169
|
trace: processCommits.map((c, i) => traceEntryOf(c.hash, parsed[i])),
|
|
32114
32170
|
costEntries: parsed.flatMap((m, i) => costEntriesOf(processCommits[i].hash, m)),
|
|
32115
|
-
judgeVerdicts:
|
|
32171
|
+
judgeVerdicts: answering.flatMap((m) => m.judge),
|
|
32116
32172
|
pinCommitted: allParsed.some((m) => m.pin === "add"),
|
|
32117
|
-
marks:
|
|
32118
|
-
approvals:
|
|
32173
|
+
marks: answering.flatMap((m) => m.marks),
|
|
32174
|
+
approvals: answeringCommits.flatMap((c, i) => approvalEntryOf(c.hash, answering[i])),
|
|
32119
32175
|
pinnedVars: { ...first?.vars },
|
|
32120
32176
|
headTurn: headTurnOf(head),
|
|
32121
32177
|
closingHash,
|
|
@@ -32544,6 +32600,43 @@ const restAt = (ref, startOverrides = {}) => gen(function* () {
|
|
|
32544
32600
|
};
|
|
32545
32601
|
});
|
|
32546
32602
|
const currentRest = restAt(void 0);
|
|
32603
|
+
/**
|
|
32604
|
+
* Where `gtd repin` anchors a pinned process the live workflow no longer
|
|
32605
|
+
* replays: the commit replay last stood on, and the step the live workflow
|
|
32606
|
+
* reaches from there on HEAD's tree — the unpin when the flow ends instead.
|
|
32607
|
+
* `undefined` when there is nothing to anchor: no divergence, or no pin.
|
|
32608
|
+
*/
|
|
32609
|
+
const repinAnchor = gen(function* () {
|
|
32610
|
+
const git = yield* GitService;
|
|
32611
|
+
const config = yield* (yield* ConfigService).load;
|
|
32612
|
+
const workspace = yield* Workspace;
|
|
32613
|
+
const run = yield* computeProcessRun(git, config.workflow);
|
|
32614
|
+
const { def, setup } = yield* setupFor(config, run, workspace, yield* Host);
|
|
32615
|
+
const diverged = yield* promise(() => replayFor(setup));
|
|
32616
|
+
if (diverged.kind !== "divergence" || !headHasPin(workspace, run)) return void 0;
|
|
32617
|
+
const trial = {
|
|
32618
|
+
hash: "",
|
|
32619
|
+
message: formatCommitMessage({
|
|
32620
|
+
actor: "check",
|
|
32621
|
+
from: PIN_STATES.add,
|
|
32622
|
+
to: PIN_STATES.add,
|
|
32623
|
+
pin: "add",
|
|
32624
|
+
anchor: diverged.at
|
|
32625
|
+
}),
|
|
32626
|
+
tree: setup.episode.at(-1)?.tree ?? setup.base.tree
|
|
32627
|
+
};
|
|
32628
|
+
const outcome = yield* promise(() => replayFor({
|
|
32629
|
+
...setup,
|
|
32630
|
+
episode: [...setup.episode, trial]
|
|
32631
|
+
}));
|
|
32632
|
+
if (outcome.kind !== "divergence") return yield* fail$8(replayError(outcome) ?? /* @__PURE__ */ new Error("gtd repin: replay found no step"));
|
|
32633
|
+
const to = outcome.reached ?? PIN_STATES.remove;
|
|
32634
|
+
if (run.workflow === void 0 && to === def.initial) return yield* fail$8(/* @__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`));
|
|
32635
|
+
return {
|
|
32636
|
+
at: diverged.at,
|
|
32637
|
+
to
|
|
32638
|
+
};
|
|
32639
|
+
});
|
|
32547
32640
|
/** The step the process rests at and the trace that reached it — at an unpin, the flow's last step. */
|
|
32548
32641
|
const restingOn = (outcome, pin) => {
|
|
32549
32642
|
if (outcome.kind === "rest") return {
|
|
@@ -34066,20 +34159,28 @@ const runRestoreCommand = (json, out) => gen(function* () {
|
|
|
34066
34159
|
]).required;
|
|
34067
34160
|
yield* writeScript(json, out, combinedScript(required, ""));
|
|
34068
34161
|
});
|
|
34069
|
-
/**
|
|
34070
|
-
|
|
34071
|
-
* so reading the rest first refuses a live workflow that cannot replay the
|
|
34072
|
-
* process before any pin is written.
|
|
34073
|
-
*/
|
|
34074
|
-
const runRepinCommand = (json, out) => gen(function* () {
|
|
34162
|
+
/** The step an ordinary repin rests at — the one the process rests at now. */
|
|
34163
|
+
const repinTarget = gen(function* () {
|
|
34075
34164
|
const rest = yield* currentRest;
|
|
34076
34165
|
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;
|
|
34077
34166
|
if (refusal !== void 0) return yield* fail$8(/* @__PURE__ */ new Error(`gtd repin: ${refusal}`));
|
|
34167
|
+
return rest.step.name;
|
|
34168
|
+
});
|
|
34169
|
+
/**
|
|
34170
|
+
* `gtd repin`: the pin beat's script and landing, on demand. It runs live: a
|
|
34171
|
+
* pinned process the live workflow no longer replays is anchored where it
|
|
34172
|
+
* diverged, and any other failure to read the rest refuses before any pin is
|
|
34173
|
+
* written.
|
|
34174
|
+
*/
|
|
34175
|
+
const runRepinCommand = (json, out) => gen(function* () {
|
|
34176
|
+
const anchor = yield* repinAnchor;
|
|
34177
|
+
const to = anchor?.to ?? (yield* repinTarget);
|
|
34078
34178
|
const message = formatCommitMessage({
|
|
34079
34179
|
actor: "check",
|
|
34080
34180
|
from: PIN_STATES.add,
|
|
34081
|
-
to
|
|
34082
|
-
pin: "add"
|
|
34181
|
+
to,
|
|
34182
|
+
pin: "add",
|
|
34183
|
+
...anchor !== void 0 ? { anchor: anchor.at } : {}
|
|
34083
34184
|
});
|
|
34084
34185
|
yield* writeScript(json, out, combinedScript(emitScripts([
|
|
34085
34186
|
{
|
|
@@ -34092,7 +34193,7 @@ const runRepinCommand = (json, out) => gen(function* () {
|
|
|
34092
34193
|
},
|
|
34093
34194
|
{
|
|
34094
34195
|
kind: "outcome",
|
|
34095
|
-
command: transitionOutcome(PIN_STATES.add,
|
|
34196
|
+
command: transitionOutcome(PIN_STATES.add, to)
|
|
34096
34197
|
}
|
|
34097
34198
|
]).required, ""));
|
|
34098
34199
|
});
|
|
@@ -34767,7 +34868,7 @@ With no <name>, list every workflow and door it can start. An unknown <name>, th
|
|
|
34767
34868
|
arity: "none",
|
|
34768
34869
|
group: "Start and stop",
|
|
34769
34870
|
summary: "Pin the process to the live gtd and workflow",
|
|
34770
|
-
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
|
|
34871
|
+
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.
|
|
34771
34872
|
|
|
34772
34873
|
s=$(gtd repin --json=script) && sh -c "$s"`
|
|
34773
34874
|
},
|