@pmelab/gtd 27.3.0 → 27.5.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 +22 -1
- package/dist/chunks/{Schema-CyImoHTj.mjs → Schema-DO5ttJaI.mjs} +1 -1
- package/dist/chunks/{reserved-BPdVZkgv.mjs → reserved-DM5W_LAt.mjs} +64 -3
- package/dist/chunks/{tail-iLAMoigl.mjs → tail-6gvLXXcg.mjs} +1 -1
- package/dist/chunks/{wire-BY1S-obW.mjs → wire-C12dahrH.mjs} +6 -4
- package/dist/flows.mjs +27 -2
- package/dist/gtd.bundle.mjs +218 -32
- package/dist/testkit.mjs +358 -37
- package/dist/wire.mjs +1 -1
- package/dist/workflows/cleanup.mjs +2 -2
- package/dist/workflows/feature.mjs +140 -64
- package/dist/workflows/fix.mjs +2 -2
- package/dist/workflows/review.mjs +2 -2
- package/dist/workflows/rollout.mjs +1 -1
- package/dist/workflows.mjs +346 -107
- package/package.json +1 -1
- package/src/cli/Cli.test.ts +49 -1
- package/src/cli/Cli.ts +25 -8
- package/src/cli/History.ts +5 -0
- package/src/cli/Program.ts +1 -0
- package/src/edge/Edge.test.ts +205 -1
- package/src/edge/Edge.ts +105 -12
- package/src/emit/GitScript.test.ts +39 -3
- package/src/emit/GitScript.ts +25 -1
- package/src/emit/index.ts +2 -0
- package/src/flags/index.test.ts +14 -1
- package/src/flags/index.ts +14 -3
- package/src/flows/helpers.ts +34 -2
- package/src/flows/runtime.ts +7 -0
- package/src/install/Install.ts +2 -1
- package/src/platform/Git.test.ts +25 -1
- package/src/platform/Git.ts +30 -0
- package/src/platform/index.ts +1 -0
- package/src/replay/Format.test.ts +16 -1
- package/src/replay/Replay.test.ts +91 -0
- package/src/replay/Replay.ts +22 -2
- package/src/replay/Trailers.test.ts +110 -1
- package/src/replay/Trailers.ts +37 -1
- package/src/replay/index.ts +2 -0
- package/src/replay/judgeDiffShape.test.ts +43 -0
- package/src/replay/seamGate.test.ts +62 -0
- package/src/step/LandStep.ts +1 -0
- package/src/step/planStart.ts +3 -2
- package/src/step/planStep.test.ts +32 -0
- package/src/step/planStep.ts +3 -1
- package/src/testkit/EmittedScriptRecognizer.test.ts +27 -0
- package/src/testkit/EmittedScriptRecognizer.ts +12 -0
- package/src/testkit/FakeGitOperations.ts +5 -0
- package/src/testkit/GitTiers.ts +158 -0
- package/src/testkit/InMemRepo.ts +38 -3
- package/src/testkit/TestRepo.ts +3 -0
- package/src/testkit/steps.ts +18 -0
- package/src/wire/BeatDocument.test.ts +25 -0
- package/src/wire/BeatDocument.ts +4 -2
- package/src/wire/BeatPlain.test.ts +1 -0
- package/src/wire/BeatStatus.test.ts +2 -0
- package/src/wire/BeatStatus.ts +13 -2
- package/src/wire/WireSchema.test.ts +10 -0
- package/src/wire/types.ts +8 -0
- package/src/workflow/Workflow.ts +1 -0
- package/src/workflow/index.ts +8 -1
- package/src/workflow/vars.test.ts +23 -1
- package/src/workflow/vars.ts +24 -0
- package/src/workflows/feature/feature.ts +9 -2
- package/src/workflows/feature/index.ts +1 -0
- package/src/workflows/feature/seam-pin.feature +52 -0
- package/src/workflows/feature/seam.feature +387 -0
- package/src/workflows/feature/seam.test.ts +88 -0
- package/src/workflows/feature/seam.ts +110 -0
- package/src/workflows/index.ts +1 -0
- package/src/workflows/shared/options.test.ts +12 -1
- package/src/workflows/shared/options.ts +1 -0
- package/src/workflows/shared/skills.test.ts +20 -2
- package/src/workflows/shared/skills.ts +6 -0
- package/src/workflows/shared/text.test.ts +59 -0
- package/src/workflows/shared/text.ts +86 -0
- package/src/workflows/shared/vars.ts +4 -0
- package/src/workflows/shared/workflows.steps.ts +39 -1
package/dist/gtd.bundle.mjs
CHANGED
|
@@ -20269,6 +20269,13 @@ const makeGitImpl = (executor, root) => {
|
|
|
20269
20269
|
hasCommits: () => exec("git", "rev-parse", "--verify", "HEAD").pipe(map$4(() => true), catchAll(() => succeed$3(false))),
|
|
20270
20270
|
readFileAtRef: (ref, path) => exec("git", "show", `${ref}:${path}`),
|
|
20271
20271
|
diffPaths: (base, tip) => exec("git", "diff", "--name-only", "-z", "--no-renames", "--no-relative", "--ignore-submodules=none", "--end-of-options", base, tip, "--").pipe(map$4(splitNul)),
|
|
20272
|
+
authorIdent: () => exec("git", "var", "GIT_AUTHOR_IDENT").pipe(map$4((out) => {
|
|
20273
|
+
const match = /^(.*?) <([^<>]*)> \d+ [+-]\d{4}$/.exec(out.trim());
|
|
20274
|
+
return match === null || match[1] === "" || match[2] === "" ? none$4() : some({
|
|
20275
|
+
name: match[1],
|
|
20276
|
+
email: match[2]
|
|
20277
|
+
});
|
|
20278
|
+
}), catchAll(() => succeed$3(none$4()))),
|
|
20272
20279
|
changedPaths: (base) => gen(function* () {
|
|
20273
20280
|
const ref = base ?? "HEAD";
|
|
20274
20281
|
const nameStatusOut = yield* exec("git", "diff", "--name-status", "-z", ref).pipe(catchAll(() => succeed$3("")));
|
|
@@ -20286,6 +20293,7 @@ const makeGitImpl = (executor, root) => {
|
|
|
20286
20293
|
}
|
|
20287
20294
|
return all;
|
|
20288
20295
|
}),
|
|
20296
|
+
stagedPaths: () => exec("git", "diff", "--cached", "--name-status", "--no-renames", "-z", "HEAD").pipe(catchAll(() => succeed$3("")), map$4((out) => parseNameStatus(splitNul(out)))),
|
|
20289
20297
|
resolveRef: (ref) => exec("git", "rev-parse", "--verify", "--end-of-options", ref).pipe(map$4((s) => s.trim()), flatMap$4((hash) => resolvedRefOrCorrupted(ref, hash))),
|
|
20290
20298
|
readRefOption: (ref) => exec("git", "rev-parse", "--verify", "--quiet", ref).pipe(map$4((s) => some(s.trim())), catchAll(() => succeed$3(none$4()))),
|
|
20291
20299
|
isAncestor: (a, b) => run$4(root, "git", "merge-base", "--is-ancestor", a, b).pipe(provide$1(succeed$2(CommandExecutor, executor)), map$4(() => true), catchAll(() => succeed$3(false))),
|
|
@@ -20962,13 +20970,23 @@ const restorability = (git, headHash, tipHash) => gen(function* () {
|
|
|
20962
20970
|
});
|
|
20963
20971
|
//#endregion
|
|
20964
20972
|
//#region src/flags/index.ts
|
|
20965
|
-
const FLAGS$1 = [
|
|
20973
|
+
const FLAGS$1 = [{
|
|
20974
|
+
name: "naive_pass",
|
|
20975
|
+
owner: "pmelab",
|
|
20976
|
+
expiry: "2027-01-15",
|
|
20977
|
+
kind: "release"
|
|
20978
|
+
}];
|
|
20966
20979
|
const BOOL = {
|
|
20967
20980
|
"0": false,
|
|
20968
20981
|
false: false,
|
|
20969
20982
|
"1": true,
|
|
20970
20983
|
true: true
|
|
20971
20984
|
};
|
|
20985
|
+
/** A `flag_<name>` setting's value as the pinned `"1"`/`"0"`, or undefined when garbled. */
|
|
20986
|
+
const flagSettingValue = (raw) => {
|
|
20987
|
+
const key = raw.toLowerCase();
|
|
20988
|
+
return Object.hasOwn(BOOL, key) ? BOOL[key] ? "1" : "0" : void 0;
|
|
20989
|
+
};
|
|
20972
20990
|
const resolveFlags = (env, registry = FLAGS$1) => {
|
|
20973
20991
|
const out = {};
|
|
20974
20992
|
for (const { name, defaultOn } of registry) {
|
|
@@ -20983,6 +21001,7 @@ const resolveFlags = (env, registry = FLAGS$1) => {
|
|
|
20983
21001
|
}
|
|
20984
21002
|
return out;
|
|
20985
21003
|
};
|
|
21004
|
+
const flagSettings = (flags) => Object.fromEntries(Object.entries(flags).map(([name, on]) => [`flag_${name}`, on ? "1" : "0"]));
|
|
20986
21005
|
//#endregion
|
|
20987
21006
|
//#region src/install/Version.ts
|
|
20988
21007
|
const _require = createRequire(import.meta.url);
|
|
@@ -21231,7 +21250,8 @@ skills, \`skills\` (an array of the skill names, already trimmed and split —
|
|
|
21231
21250
|
(\`{read, write}\`, each an array of globs or \`null\` for unrestricted). When set: \`model\`, \`memory\`,
|
|
21232
21251
|
\`label\`, \`file\`, \`mode\`, \`edges\`, \`judge\` (the pending judgment's rendered
|
|
21233
21252
|
document). When a cost has been recorded (a prior \`gtd land --cost=<n>\`):
|
|
21234
|
-
\`cost\`, \`costByModel\`.
|
|
21253
|
+
\`cost\`, \`costByModel\`. Always \`approvals\` (an array of
|
|
21254
|
+
\`{step, name, email, commit}\`, \`[]\` when none).
|
|
21235
21255
|
|
|
21236
21256
|
\`gtd land\` fields, in fixed order: \`schema\`, \`script\` (the POSIX sh to run), \`settled\`
|
|
21237
21257
|
(true for a no-op at a \`script\` rest — stop immediately, nothing more to
|
|
@@ -21967,7 +21987,8 @@ const strayContent = (strays) => strays.map((c) => [
|
|
|
21967
21987
|
*/
|
|
21968
21988
|
const judgeDiffShape = async (name, options) => {
|
|
21969
21989
|
const { changes: all, allow } = options;
|
|
21970
|
-
const
|
|
21990
|
+
const exempt = options.exempt ?? [];
|
|
21991
|
+
const isStray = (c) => !exempt.includes(c.path) && !allow.some((pattern) => matches(c.path, pattern));
|
|
21971
21992
|
const strayChanges = all.filter(isStray).sort((a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0).map(({ path, status, before, after }) => ({
|
|
21972
21993
|
path,
|
|
21973
21994
|
status,
|
|
@@ -22013,6 +22034,28 @@ const judgeDiffShape = async (name, options) => {
|
|
|
22013
22034
|
judgment
|
|
22014
22035
|
};
|
|
22015
22036
|
};
|
|
22037
|
+
const displayPath = (path) => /[\x00-\x1f\x7f-\x9f]/.test(path) ? JSON.stringify(path) : path;
|
|
22038
|
+
const seamReport = (shape) => {
|
|
22039
|
+
const unreadable = new Set(shape.unreadable);
|
|
22040
|
+
return [
|
|
22041
|
+
"Seam report: the judge found the diff's shape needs a refactor first. These paths stray outside the allowlist:",
|
|
22042
|
+
"",
|
|
22043
|
+
...shape.strays.map((path) => `- ${displayPath(path)}${unreadable.has(path) ? " (unreadable — not judged)" : ""}`),
|
|
22044
|
+
"",
|
|
22045
|
+
"Landing a clean tree with `gtd land` approves this diff exactly as judged; edits are refused. To stop instead, run `gtd abandon`."
|
|
22046
|
+
].join("\n");
|
|
22047
|
+
};
|
|
22048
|
+
/**
|
|
22049
|
+
* Rest at an approval gate `name` showing the seam report — only when
|
|
22050
|
+
* `shape.choice` is `refactor-first`. `land` and `reject` reach no step.
|
|
22051
|
+
*/
|
|
22052
|
+
const seamGate = async (name, shape) => {
|
|
22053
|
+
if (shape.choice !== "refactor-first") return;
|
|
22054
|
+
await human(name, {
|
|
22055
|
+
message: seamReport(shape),
|
|
22056
|
+
approve: true
|
|
22057
|
+
});
|
|
22058
|
+
};
|
|
22016
22059
|
//#endregion
|
|
22017
22060
|
//#region src/flows/factory.ts
|
|
22018
22061
|
const FACTORY = Symbol.for("@pmelab/gtd/workflow-factory");
|
|
@@ -22191,6 +22234,7 @@ var flows_exports = /* @__PURE__ */ __exportAll({
|
|
|
22191
22234
|
configure: () => configure$1,
|
|
22192
22235
|
defineConfig: () => defineConfig,
|
|
22193
22236
|
defineWorkflow: () => defineWorkflow,
|
|
22237
|
+
displayPath: () => displayPath,
|
|
22194
22238
|
env: () => env,
|
|
22195
22239
|
glob: () => glob,
|
|
22196
22240
|
hasThreadFor: () => hasThreadFor,
|
|
@@ -22220,6 +22264,7 @@ var flows_exports = /* @__PURE__ */ __exportAll({
|
|
|
22220
22264
|
revertScript: () => revertScript,
|
|
22221
22265
|
run: () => run$3,
|
|
22222
22266
|
scope: () => scope,
|
|
22267
|
+
seamGate: () => seamGate,
|
|
22223
22268
|
sectionBodies: () => sectionBodies,
|
|
22224
22269
|
sections: () => sections,
|
|
22225
22270
|
showScript: () => showScript,
|
|
@@ -37282,7 +37327,7 @@ var init_Demand = __esmMin((() => {
|
|
|
37282
37327
|
var statusOf;
|
|
37283
37328
|
var init_BeatStatus = __esmMin((() => {
|
|
37284
37329
|
statusOf = (input) => {
|
|
37285
|
-
const { rendered, idle, initial, workflow, log, changes, next, cost, costByModel } = input;
|
|
37330
|
+
const { rendered, idle, initial, workflow, log, changes, next, cost, costByModel, approvals } = input;
|
|
37286
37331
|
return {
|
|
37287
37332
|
idle,
|
|
37288
37333
|
initial,
|
|
@@ -37300,7 +37345,8 @@ var init_BeatStatus = __esmMin((() => {
|
|
|
37300
37345
|
next,
|
|
37301
37346
|
cost,
|
|
37302
37347
|
costByModel,
|
|
37303
|
-
judge: rendered.judge
|
|
37348
|
+
judge: rendered.judge,
|
|
37349
|
+
approvals
|
|
37304
37350
|
};
|
|
37305
37351
|
};
|
|
37306
37352
|
}));
|
|
@@ -37360,7 +37406,8 @@ var init_BeatDocument = __esmMin((() => {
|
|
|
37360
37406
|
costByModel: hasCost ? status.costByModel : void 0,
|
|
37361
37407
|
judge: status.judge,
|
|
37362
37408
|
skills,
|
|
37363
|
-
access
|
|
37409
|
+
access,
|
|
37410
|
+
approvals: status.approvals
|
|
37364
37411
|
};
|
|
37365
37412
|
};
|
|
37366
37413
|
renderBeatJson = (document) => JSON.stringify(document) + "\n";
|
|
@@ -37689,12 +37736,38 @@ const TRAILER_RE = /^(Gtd-[A-Za-z-]+):[ \t]*(.*?)[ \t]*$/gm;
|
|
|
37689
37736
|
const COST_RE = /^([0-9]+(?:\.[0-9]+)?)(?:[ \t]+(.+))?$/;
|
|
37690
37737
|
const VAR_RE = /^([^=\s]+)=(.*)$/;
|
|
37691
37738
|
const CONFIG_RE = /^([0-9a-f]{16}) (.+)$/;
|
|
37692
|
-
const CONTROL_CHARACTER$1 = /[\x00-\x1f\x7f-\x9f]/;
|
|
37739
|
+
const CONTROL_CHARACTER$1 = /[\x00-\x1f\x7f-\x9f\u2028\u2029]/;
|
|
37693
37740
|
const encodePath = (path) => path.trim() !== path || path.startsWith("\"") || CONTROL_CHARACTER$1.test(path) ? JSON.stringify(path) : path;
|
|
37694
37741
|
const decodePath = (raw) => {
|
|
37695
37742
|
const path = raw.startsWith("\"") ? parseJson$1(raw) : raw;
|
|
37696
37743
|
return typeof path === "string" && path !== "" && !CONTROL_CHARACTER$1.test(path) ? path : void 0;
|
|
37697
37744
|
};
|
|
37745
|
+
const decodeApproval = (raw) => {
|
|
37746
|
+
const parsed = parseJson$1(raw);
|
|
37747
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) return void 0;
|
|
37748
|
+
const { step, name, email } = parsed;
|
|
37749
|
+
if (typeof step !== "string" || typeof name !== "string" || typeof email !== "string") return void 0;
|
|
37750
|
+
if ([
|
|
37751
|
+
step,
|
|
37752
|
+
name,
|
|
37753
|
+
email
|
|
37754
|
+
].some((field) => CONTROL_CHARACTER$1.test(field))) return void 0;
|
|
37755
|
+
const id = parseStepId(step);
|
|
37756
|
+
return id === void 0 ? void 0 : {
|
|
37757
|
+
step: id,
|
|
37758
|
+
name,
|
|
37759
|
+
email
|
|
37760
|
+
};
|
|
37761
|
+
};
|
|
37762
|
+
const encodeApproval = ({ step, name, email }) => JSON.stringify({
|
|
37763
|
+
step: formatStepId(step),
|
|
37764
|
+
name,
|
|
37765
|
+
email
|
|
37766
|
+
});
|
|
37767
|
+
const isRecordableApproval = (approval) => {
|
|
37768
|
+
const back = decodeApproval(encodeApproval(approval));
|
|
37769
|
+
return back !== void 0 && encodeApproval(back) === encodeApproval(approval);
|
|
37770
|
+
};
|
|
37698
37771
|
const FORMAT_RE = /^[1-9][0-9]*$/;
|
|
37699
37772
|
const HASH_RE = /^(?:[0-9a-f]{40}|[0-9a-f]{64})$/;
|
|
37700
37773
|
const TRAILER_READERS = {
|
|
@@ -37729,6 +37802,9 @@ const TRAILER_READERS = {
|
|
|
37729
37802
|
"Gtd-Kept": (value, into) => {
|
|
37730
37803
|
if (HASH_RE.test(value)) into.kept ??= value;
|
|
37731
37804
|
},
|
|
37805
|
+
"Gtd-Approved": (value, into) => {
|
|
37806
|
+
into.approved ??= decodeApproval(value);
|
|
37807
|
+
},
|
|
37732
37808
|
"Gtd-Format": (value, into) => {
|
|
37733
37809
|
if (FORMAT_RE.test(value)) into.format ??= Number(value);
|
|
37734
37810
|
},
|
|
@@ -37750,6 +37826,7 @@ const parseCommitMessage = (message) => {
|
|
|
37750
37826
|
branch: void 0,
|
|
37751
37827
|
version: void 0,
|
|
37752
37828
|
kept: void 0,
|
|
37829
|
+
approved: void 0,
|
|
37753
37830
|
judge: [],
|
|
37754
37831
|
vars: {},
|
|
37755
37832
|
config: {},
|
|
@@ -37775,6 +37852,7 @@ const formatCommitMessage = (spec) => {
|
|
|
37775
37852
|
for (const [path, hash] of sortedEntries(spec.config)) lines.push(`Gtd-Config: ${hash} ${encodePath(path)}`);
|
|
37776
37853
|
if (spec.cost !== void 0) lines.push(`Gtd-Cost: ${spec.cost.cost}${spec.cost.model !== void 0 ? ` ${spec.cost.model}` : ""}`);
|
|
37777
37854
|
for (const verdict of spec.judge ?? []) lines.push(`Gtd-Judge: ${JSON.stringify(verdict)}`);
|
|
37855
|
+
if (spec.approved !== void 0) lines.push(`Gtd-Approved: ${encodeApproval(spec.approved)}`);
|
|
37778
37856
|
if (spec.branch !== void 0) lines.push(`Gtd-Branch: ${spec.branch}`);
|
|
37779
37857
|
if (spec.version !== void 0) lines.push(`Gtd-Version: ${spec.version}`);
|
|
37780
37858
|
lines.push(`Gtd-Format: 1`);
|
|
@@ -37870,7 +37948,8 @@ const KNOWN_OPTIONS = {
|
|
|
37870
37948
|
human: /* @__PURE__ */ new Set([
|
|
37871
37949
|
...STEERING_OPTIONS,
|
|
37872
37950
|
"message",
|
|
37873
|
-
"acceptClean"
|
|
37951
|
+
"acceptClean",
|
|
37952
|
+
"approve"
|
|
37874
37953
|
]),
|
|
37875
37954
|
run: new Set(STEERING_OPTIONS),
|
|
37876
37955
|
judge: /* @__PURE__ */ new Set([...STEERING_OPTIONS, "message"])
|
|
@@ -37880,6 +37959,13 @@ const unknownOptions = (step) => {
|
|
|
37880
37959
|
if (unknown.length === 0) return void 0;
|
|
37881
37960
|
return `gtd: step "${step.name}": unknown key(s) ${unknown.join(", ")} in ${step.kind}() options`;
|
|
37882
37961
|
};
|
|
37962
|
+
const approveConflict = (step) => {
|
|
37963
|
+
const request = step.request;
|
|
37964
|
+
if (request.kind !== "human" || request.options.approve !== true) return void 0;
|
|
37965
|
+
const keys = ["mode", "file"].filter((key) => request.options[key] !== void 0);
|
|
37966
|
+
if (keys.length === 0) return void 0;
|
|
37967
|
+
return `gtd: step "${step.name}": approve cannot be combined with ${keys.join(", ")} in human() options — an approval records exactly the judged tree, and a steering file is rewritten on landing`;
|
|
37968
|
+
};
|
|
37883
37969
|
const replay = async (input) => {
|
|
37884
37970
|
const commits = input.episode.commits.map((c) => ({
|
|
37885
37971
|
...c,
|
|
@@ -38039,12 +38125,21 @@ const replay = async (input) => {
|
|
|
38039
38125
|
kind: "divergence",
|
|
38040
38126
|
message: `gtd: commit ${short(expected.hash)} names "${expected.name}" as the next step, but replay reached "${step.name}" — ${DIVERGED}`
|
|
38041
38127
|
};
|
|
38042
|
-
const error = unknownOptions(step) ?? checkIdentity(step);
|
|
38128
|
+
const error = unknownOptions(step) ?? approveConflict(step) ?? checkIdentity(step);
|
|
38043
38129
|
return error === void 0 ? void 0 : {
|
|
38044
38130
|
kind: "failed",
|
|
38045
38131
|
message: error
|
|
38046
38132
|
};
|
|
38047
38133
|
};
|
|
38134
|
+
const missingApproval = (step, commit, expected) => {
|
|
38135
|
+
if (step.request.kind !== "human" || step.request.options.approve !== true) return void 0;
|
|
38136
|
+
const approved = commit.parsed.approved;
|
|
38137
|
+
if (approved !== void 0 && formatStepId(approved.step) === expected) return void 0;
|
|
38138
|
+
return {
|
|
38139
|
+
kind: "divergence",
|
|
38140
|
+
message: `gtd: commit ${short(commit.hash)} completes approval gate "${expected}" but records no Gtd-Approved for it — ${DIVERGED}`
|
|
38141
|
+
};
|
|
38142
|
+
};
|
|
38048
38143
|
const consumeCommit = (step, commit) => {
|
|
38049
38144
|
const recorded = commit.parsed.step;
|
|
38050
38145
|
const expected = formatStepId(step.id);
|
|
@@ -38055,6 +38150,8 @@ const replay = async (input) => {
|
|
|
38055
38150
|
message: `gtd: commit ${short(commit.hash)} ${what}, but replay expected "${expected}" — ${DIVERGED}`
|
|
38056
38151
|
});
|
|
38057
38152
|
}
|
|
38153
|
+
const unapproved = missingApproval(step, commit, expected);
|
|
38154
|
+
if (unapproved !== void 0) return finish(unapproved);
|
|
38058
38155
|
cursor++;
|
|
38059
38156
|
advance(commit);
|
|
38060
38157
|
if (commit.parsed.kept !== void 0) if (commit.kept === void 0) keptTips.delete(step.name);
|
|
@@ -38284,7 +38381,10 @@ const compileConfig = (value, origin) => {
|
|
|
38284
38381
|
};
|
|
38285
38382
|
//#endregion
|
|
38286
38383
|
//#region src/workflow/vars.ts
|
|
38384
|
+
init_esm();
|
|
38287
38385
|
const PREFIX = "GTD_";
|
|
38386
|
+
/** The live release flags as `flag_<name>` settings, provided by the CLI; absent means no flags. */
|
|
38387
|
+
var StartFlags = class extends Reference()("StartFlags", { defaultValue: () => ({}) }) {};
|
|
38288
38388
|
/**
|
|
38289
38389
|
* One kind of setting, later wins: the workflow's own defaults, `--var`
|
|
38290
38390
|
* overrides (process settings only; environment settings pass `{}`), then
|
|
@@ -38304,6 +38404,18 @@ const resolveVars = (workflowVars, entryVars, env) => {
|
|
|
38304
38404
|
}
|
|
38305
38405
|
return merged;
|
|
38306
38406
|
};
|
|
38407
|
+
/**
|
|
38408
|
+
* `resolveVars` for a process being STARTED: `flags` land between the workflow
|
|
38409
|
+
* defaults and `--var`, for already-declared names only. A running process never
|
|
38410
|
+
* calls this — it replays its pinned record.
|
|
38411
|
+
*/
|
|
38412
|
+
const resolveStartVars = (workflowVars, flags, entryVars, env) => {
|
|
38413
|
+
const declared = Object.fromEntries(Object.entries(flags).filter(([name]) => Object.hasOwn(workflowVars, name)));
|
|
38414
|
+
return resolveVars({
|
|
38415
|
+
...workflowVars,
|
|
38416
|
+
...declared
|
|
38417
|
+
}, entryVars, env);
|
|
38418
|
+
};
|
|
38307
38419
|
/** The first process setting whose value spans lines — a `Gtd-Var:` trailer cannot carry it. */
|
|
38308
38420
|
const multilineSetting = (vars) => Object.keys(vars).sort().find((name) => /[\r\n]/.test(vars[name]));
|
|
38309
38421
|
const SETTING_NAME = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
@@ -39169,6 +39281,9 @@ const planStep = (snapshot, opts = {}) => {
|
|
|
39169
39281
|
kind: "keepTurn",
|
|
39170
39282
|
ref: landing.keepTurn,
|
|
39171
39283
|
message
|
|
39284
|
+
} : landing.spec.approved !== void 0 ? {
|
|
39285
|
+
kind: "commitClean",
|
|
39286
|
+
message
|
|
39172
39287
|
} : {
|
|
39173
39288
|
kind: "commitAll",
|
|
39174
39289
|
message
|
|
@@ -39234,7 +39349,7 @@ const planStart = (current, actor, start) => gen(function* () {
|
|
|
39234
39349
|
const overrideRefusal = yield* checkOverrides(commandLabel, varOverrides, Object.keys(workflow.vars), Object.keys(workflow.env));
|
|
39235
39350
|
if (overrideRefusal !== void 0) return overrideRefusal;
|
|
39236
39351
|
const baseOf = workflow.def.base;
|
|
39237
|
-
const vars =
|
|
39352
|
+
const vars = resolveStartVars(workflow.vars, yield* StartFlags, varOverrides, (yield* Host).env);
|
|
39238
39353
|
const multiline = multilineSetting(vars);
|
|
39239
39354
|
if (multiline !== void 0) return refusal(`${commandLabel}: process setting "${multiline}" spans several lines — a process setting is pinned in a commit trailer and must be a single line`);
|
|
39240
39355
|
const template = yield* try_({
|
|
@@ -39376,6 +39491,12 @@ const pinnedConfigOf = (history) => {
|
|
|
39376
39491
|
};
|
|
39377
39492
|
}
|
|
39378
39493
|
};
|
|
39494
|
+
const approvalEntryOf = (hash, message) => message.approved === void 0 ? [] : [{
|
|
39495
|
+
step: formatStepId(message.approved.step),
|
|
39496
|
+
name: message.approved.name,
|
|
39497
|
+
email: message.approved.email,
|
|
39498
|
+
commit: hash
|
|
39499
|
+
}];
|
|
39379
39500
|
const buildRun = (history, location, closingHash) => {
|
|
39380
39501
|
const processCommits = history.slice(location.processStart);
|
|
39381
39502
|
const parsed = processCommits.map((c) => parseCommitMessage(c.message));
|
|
@@ -39391,6 +39512,7 @@ const buildRun = (history, location, closingHash) => {
|
|
|
39391
39512
|
trace: processCommits.map((c, i) => traceEntryOf(c.hash, parsed[i])),
|
|
39392
39513
|
costEntries: parsed.flatMap(costEntriesOf),
|
|
39393
39514
|
judgeVerdicts: parsed.flatMap((m) => m.judge),
|
|
39515
|
+
approvals: processCommits.flatMap((c, i) => approvalEntryOf(c.hash, parsed[i])),
|
|
39394
39516
|
pinnedVars: { ...first?.vars },
|
|
39395
39517
|
pinnedConfig: pinnedConfigOf(history.slice(Math.max(location.baseIndex, 0))),
|
|
39396
39518
|
headTurn: headTurnOf(head),
|
|
@@ -39579,9 +39701,9 @@ const pendingTree = (workspace, entries, rewrite) => {
|
|
|
39579
39701
|
* are always live. `startOverrides` are `--var` values, passed only while a
|
|
39580
39702
|
* process is being started.
|
|
39581
39703
|
*/
|
|
39582
|
-
const settingsFor = (workflow, pinnedVars, hostEnv, startOverrides = {}) => ({
|
|
39704
|
+
const settingsFor = (workflow, pinnedVars, hostEnv, startOverrides = {}, startFlags) => ({
|
|
39583
39705
|
vars: {
|
|
39584
|
-
...
|
|
39706
|
+
...resolveStartVars(workflow.vars, startFlags, startOverrides, hostEnv),
|
|
39585
39707
|
...pinnedVars
|
|
39586
39708
|
},
|
|
39587
39709
|
env: resolveVars(workflow.env, {}, hostEnv)
|
|
@@ -39676,7 +39798,8 @@ const messageDef = (common, request, step) => ({
|
|
|
39676
39798
|
...common,
|
|
39677
39799
|
kind: "message",
|
|
39678
39800
|
content: request.options.message ?? request.options.label ?? step.name,
|
|
39679
|
-
...optional$1("acceptClean", request.options.acceptClean)
|
|
39801
|
+
...optional$1("acceptClean", request.options.approve === true || request.options.acceptClean),
|
|
39802
|
+
...optional$1("approve", request.options.approve)
|
|
39680
39803
|
});
|
|
39681
39804
|
const stepDefOf = (step) => {
|
|
39682
39805
|
const request = step.request;
|
|
@@ -39754,7 +39877,8 @@ const costByModel = (entries) => {
|
|
|
39754
39877
|
const setupFor = (config, run, workspace, host, startOverrides = {}) => gen(function* () {
|
|
39755
39878
|
const resolved = yield* workflowOf(config, run);
|
|
39756
39879
|
const def = resolved.def;
|
|
39757
|
-
const
|
|
39880
|
+
const startFlags = run.trace.length === 0 ? yield* StartFlags : {};
|
|
39881
|
+
const { vars, env } = settingsFor(resolved, run.pinnedVars, host.env, startOverrides, startFlags);
|
|
39758
39882
|
const skills = yield* resolveScopeSkills(def, vars);
|
|
39759
39883
|
const access = yield* resolveScopeAccess(def, skills, vars);
|
|
39760
39884
|
const budget = yield* try_({
|
|
@@ -39805,6 +39929,12 @@ const setupFor = (config, run, workspace, host, startOverrides = {}) => gen(func
|
|
|
39805
39929
|
}
|
|
39806
39930
|
};
|
|
39807
39931
|
});
|
|
39932
|
+
const pendingChanges = (git, withIndex) => gen(function* () {
|
|
39933
|
+
const worktree = yield* git.changedPaths();
|
|
39934
|
+
if (!withIndex) return worktree;
|
|
39935
|
+
const seen = new Set(worktree.map((e) => e.path));
|
|
39936
|
+
return [...worktree, ...(yield* git.stagedPaths()).filter((e) => !seen.has(e.path))];
|
|
39937
|
+
});
|
|
39808
39938
|
/** Resolve the rest at `ref`, or at HEAD when `ref` is `undefined`. */
|
|
39809
39939
|
const restAt = (ref, startOverrides = {}) => gen(function* () {
|
|
39810
39940
|
const git = yield* GitService;
|
|
@@ -39820,7 +39950,7 @@ const restAt = (ref, startOverrides = {}) => gen(function* () {
|
|
|
39820
39950
|
yield* (yield* Narrator).narrate(`rest resolved: ${step.name} (awaits ${step.actor})`);
|
|
39821
39951
|
const stepDef = stepDefOf(step);
|
|
39822
39952
|
if (stepDef.mode !== void 0 && def.modes[stepDef.mode] === void 0) return yield* fail$7(/* @__PURE__ */ new Error(`gtd config: step "${step.name}": mode "${stepDef.mode}" is not a mode this workflow knows (${knownModes(def).join(", ")})`));
|
|
39823
|
-
const changes = ref === void 0 ? (yield* git.
|
|
39953
|
+
const changes = ref === void 0 ? (yield* pendingChanges(git, stepDef.approve === true)).map((e) => ({
|
|
39824
39954
|
status: normalizeStatus(e.status),
|
|
39825
39955
|
path: e.path
|
|
39826
39956
|
})) : [];
|
|
@@ -39858,7 +39988,7 @@ const currentRest = restAt(void 0);
|
|
|
39858
39988
|
*/
|
|
39859
39989
|
const startRefusal = (rest, resolved, startVars) => gen(function* () {
|
|
39860
39990
|
const host = yield* Host;
|
|
39861
|
-
const { vars, env } = settingsFor(resolved, {}, host.env, startVars);
|
|
39991
|
+
const { vars, env } = settingsFor(resolved, {}, host.env, startVars, yield* StartFlags);
|
|
39862
39992
|
const skills = yield* resolveScopeSkills(resolved.def, vars);
|
|
39863
39993
|
const access = yield* resolveScopeAccess(resolved.def, skills, vars);
|
|
39864
39994
|
const workspace = rest.setup.workspace;
|
|
@@ -39906,6 +40036,25 @@ const noProcessUnderway = (rest) => rest.run.workflow === void 0 && rest.state =
|
|
|
39906
40036
|
/** `idle` means exactly one thing: no process underway, clean tree. */
|
|
39907
40037
|
const restIsIdle = (rest) => noProcessUnderway(rest) && rest.changes.length === 0;
|
|
39908
40038
|
const isHumanReviewGate = (def) => def.actor === "human" && def.mode === "review";
|
|
40039
|
+
const approvalRefusal = (rest, approver) => {
|
|
40040
|
+
if (rest.stepDef.approve !== true) return void 0;
|
|
40041
|
+
const id = rest.step.id;
|
|
40042
|
+
if (rest.changes.length > 0) return {
|
|
40043
|
+
kind: "refusal",
|
|
40044
|
+
message: `gtd land: approval-gate: ${formatStepId(id)} approves exactly the judged diff — revert your edits and run \`gtd land\` on a clean tree to approve, or run \`gtd abandon\``
|
|
40045
|
+
};
|
|
40046
|
+
if (isNone(approver)) return {
|
|
40047
|
+
kind: "refusal",
|
|
40048
|
+
message: `gtd land: approval-gate: ${formatStepId(id)} records who approved it, but git has no author identity — set user.name and user.email`
|
|
40049
|
+
};
|
|
40050
|
+
if (!isRecordableApproval({
|
|
40051
|
+
step: id,
|
|
40052
|
+
...approver.value
|
|
40053
|
+
})) return {
|
|
40054
|
+
kind: "refusal",
|
|
40055
|
+
message: `gtd land: approval-gate: ${formatStepId(id)} cannot record ${approver.value.name} <${approver.value.email}> as approver — it would not read back unchanged from its Gtd-Approved trailer (a control character, for example)`
|
|
40056
|
+
};
|
|
40057
|
+
};
|
|
39909
40058
|
/** The landing a clean tree decides before any replay, if it decides one. */
|
|
39910
40059
|
const cleanLanding = (rest) => {
|
|
39911
40060
|
if (rest.changes.length > 0) return void 0;
|
|
@@ -39960,7 +40109,11 @@ const closingTrailers = (branch) => ({
|
|
|
39960
40109
|
});
|
|
39961
40110
|
const isSettledNoop = (rest, to) => rest.changes.length === 0 && rest.stepDef.kind === "script" && to === rest.state;
|
|
39962
40111
|
const keptTurn = (rest) => rest.stepDef.keep === true && rest.changes.length > 0 ? { keepTurn: keptRef(rest.run.startHash, rest.state) } : {};
|
|
39963
|
-
const
|
|
40112
|
+
const approvedTrailer = (rest, approver) => isSome(approver) ? { approved: {
|
|
40113
|
+
step: rest.step.id,
|
|
40114
|
+
...approver.value
|
|
40115
|
+
} } : {};
|
|
40116
|
+
const landingTo = (rest, to, branch, approver) => {
|
|
39964
40117
|
if (isSettledNoop(rest, to)) return {
|
|
39965
40118
|
kind: "noop",
|
|
39966
40119
|
settled: true
|
|
@@ -39978,6 +40131,7 @@ const landingTo = (rest, to, branch) => {
|
|
|
39978
40131
|
from: rest.state,
|
|
39979
40132
|
to,
|
|
39980
40133
|
step: rest.step.id,
|
|
40134
|
+
...approvedTrailer(rest, approver),
|
|
39981
40135
|
...starts ? {
|
|
39982
40136
|
vars: rest.vars,
|
|
39983
40137
|
config: rest.config
|
|
@@ -39993,7 +40147,9 @@ const landingTo = (rest, to, branch) => {
|
|
|
39993
40147
|
* replay with the working tree as the pending turn. The target step is
|
|
39994
40148
|
* computed by replaying, never matched.
|
|
39995
40149
|
*/
|
|
39996
|
-
const decideLanding = (rest, verdicts, branch) => gen(function* () {
|
|
40150
|
+
const decideLanding = (rest, verdicts, branch, approver) => gen(function* () {
|
|
40151
|
+
const unapproved = approvalRefusal(rest, approver);
|
|
40152
|
+
if (unapproved !== void 0) return unapproved;
|
|
39997
40153
|
const early = cleanLanding(rest);
|
|
39998
40154
|
if (early !== void 0) return early;
|
|
39999
40155
|
if (rest.stepDef.kind === "prompt" && rest.stepDef.access?.write != null) {
|
|
@@ -40009,14 +40165,16 @@ const decideLanding = (rest, verdicts, branch) => gen(function* () {
|
|
|
40009
40165
|
}));
|
|
40010
40166
|
const to = yield* landingTarget(outcome, rest);
|
|
40011
40167
|
if (typeof to !== "string") return to;
|
|
40012
|
-
return landingTo(rest, to, branch);
|
|
40168
|
+
return landingTo(rest, to, branch, approver);
|
|
40013
40169
|
});
|
|
40014
40170
|
/** Where landing the pending turn would leave the process, or `undefined` when it would land nothing. */
|
|
40015
|
-
const previewLanding = (rest) => decideLanding(rest, void 0, none$4()).pipe(map$4((landing) => landing.kind === "commit" ? landing.to : void 0), catchAll(() => succeed$3(void 0)));
|
|
40171
|
+
const previewLanding = (rest) => decideLanding(rest, void 0, none$4(), none$4()).pipe(map$4((landing) => landing.kind === "commit" ? landing.to : void 0), catchAll(() => succeed$3(void 0)));
|
|
40016
40172
|
/** Every fact the pure landing planner needs, read once: the rest, its steering file, and the landing decision. */
|
|
40017
40173
|
const snapshotFromRest = (rest, verdicts) => gen(function* () {
|
|
40018
|
-
const
|
|
40019
|
-
const
|
|
40174
|
+
const git = yield* GitService;
|
|
40175
|
+
const branch = yield* git.currentBranch();
|
|
40176
|
+
const approver = rest.stepDef.approve === true ? yield* git.authorIdent() : none$4();
|
|
40177
|
+
const landing = yield* decideLanding(rest, verdicts, branch, approver);
|
|
40020
40178
|
return {
|
|
40021
40179
|
stepDef: rest.stepDef,
|
|
40022
40180
|
state: rest.state,
|
|
@@ -40044,7 +40202,7 @@ const summaryFor = (run) => gen(function* () {
|
|
|
40044
40202
|
})),
|
|
40045
40203
|
processCost: run.costEntries.reduce((sum, entry) => sum + entry.cost, 0),
|
|
40046
40204
|
processCostByModel: costByModel(run.costEntries),
|
|
40047
|
-
...settingsFor(resolved, run.pinnedVars, host.env)
|
|
40205
|
+
...settingsFor(resolved, run.pinnedVars, host.env, {}, {})
|
|
40048
40206
|
});
|
|
40049
40207
|
});
|
|
40050
40208
|
/** Repository-relative paths of the config files today's config and the process's own record name; one outside the repository cannot be read at a ref, so it is left out. */
|
|
@@ -40070,7 +40228,8 @@ const historySteps = (run) => {
|
|
|
40070
40228
|
actor: entry.actor,
|
|
40071
40229
|
from: message.parsed?.from,
|
|
40072
40230
|
to: entry.state,
|
|
40073
|
-
verdicts: message.judge
|
|
40231
|
+
verdicts: message.judge,
|
|
40232
|
+
...optional$1("approval", approvalEntryOf(entry.hash, message)[0])
|
|
40074
40233
|
};
|
|
40075
40234
|
});
|
|
40076
40235
|
};
|
|
@@ -40128,8 +40287,26 @@ const commitAllowEmpty = (message) => {
|
|
|
40128
40287
|
`fi`
|
|
40129
40288
|
].join("\n");
|
|
40130
40289
|
};
|
|
40290
|
+
const CHANGED_SINCE_LAND = "gtd: the working tree changed since `gtd land` — revert the edits and run `gtd land` again to approve";
|
|
40131
40291
|
/** `git add -A || exit 1` (an `&&` left side would not abort under `set -e`) then `commitAllowEmpty`. */
|
|
40132
40292
|
const commitAll = (message) => `git add -A || exit 1\n${commitAllowEmpty(message)}`;
|
|
40293
|
+
/**
|
|
40294
|
+
* An approval records exactly the judged tree: HEAD is pinned before the
|
|
40295
|
+
* clean check and the commit is built over its tree with `commit-tree`, so no
|
|
40296
|
+
* hook runs and none can add to it. A HEAD that moved fails the guarded
|
|
40297
|
+
* `update-ref`. `commit-tree` ignores `commit.gpgSign`, so it is read here.
|
|
40298
|
+
*/
|
|
40299
|
+
const commitClean = (message) => [
|
|
40300
|
+
`gtd_head=$(git rev-parse --verify HEAD) || exit 1`,
|
|
40301
|
+
`if [ -n "$(git status --porcelain --untracked-files=all --ignore-submodules=none)" ]; then`,
|
|
40302
|
+
` printf '%s\\n' ${shellQuote$1(CHANGED_SINCE_LAND)} >&2`,
|
|
40303
|
+
` exit 1`,
|
|
40304
|
+
`fi`,
|
|
40305
|
+
`gtd_sign_cfg=$(git config --type=bool --default=false commit.gpgSign) || exit 1`,
|
|
40306
|
+
`if [ "$gtd_sign_cfg" = true ]; then gtd_sign=-S; else gtd_sign=--no-gpg-sign; fi`,
|
|
40307
|
+
`gtd_tip=$(git commit-tree "$gtd_sign" "$gtd_head^{tree}" -p "$gtd_head" -m ${shellQuote$1(message)}) || exit 1`,
|
|
40308
|
+
`git update-ref -m ${shellQuote$1(`commit: ${message}`)} HEAD "$gtd_tip" "$gtd_head" || exit 1`
|
|
40309
|
+
].join("\n");
|
|
40133
40310
|
const mixedResetTo = (ref) => `git reset --mixed ${shellQuote$1(ref)}`;
|
|
40134
40311
|
const hardResetTo = (ref) => `git reset --hard ${shellQuote$1(ref)}`;
|
|
40135
40312
|
/**
|
|
@@ -40155,7 +40332,7 @@ const keepTurn = (refPrefix, message) => {
|
|
|
40155
40332
|
`gtd_kept=$(git commit-tree "$gtd_tree" -p "$gtd_head" -m ${shellQuote$1(message)}) || exit 1`,
|
|
40156
40333
|
`git update-ref ${keptRef} "$gtd_kept" || exit 1`,
|
|
40157
40334
|
`gtd_marker=$(printf '%s\\nGtd-Kept: %s' ${shellQuote$1(message)} "$gtd_kept")`,
|
|
40158
|
-
`if ! gtd_tip=$(git commit-tree "$gtd_sign" "$gtd_head^{tree}" -p "$gtd_head" -m "$gtd_marker") || ! git update-ref HEAD "$gtd_tip" "$gtd_head"; then`,
|
|
40335
|
+
`if ! gtd_tip=$(git commit-tree "$gtd_sign" "$gtd_head^{tree}" -p "$gtd_head" -m "$gtd_marker") || ! git update-ref -m ${shellQuote$1(`commit: ${message}`)} HEAD "$gtd_tip" "$gtd_head"; then`,
|
|
40159
40336
|
` git reset -q && git update-ref -d ${keptRef}`,
|
|
40160
40337
|
` exit 1`,
|
|
40161
40338
|
`fi`,
|
|
@@ -40193,6 +40370,7 @@ const renderOutcome = (outcome) => {
|
|
|
40193
40370
|
const renderGitWrite = (write) => {
|
|
40194
40371
|
switch (write.kind) {
|
|
40195
40372
|
case "commitAll": return commitAll(write.message);
|
|
40373
|
+
case "commitClean": return commitClean(write.message);
|
|
40196
40374
|
case "updateRef": return updateRef(write.ref, write.target);
|
|
40197
40375
|
case "keepTurn": return keepTurn(write.ref, write.message);
|
|
40198
40376
|
}
|
|
@@ -40676,6 +40854,7 @@ const runHistoryShowCommand = (ref, json, out) => gen(function* () {
|
|
|
40676
40854
|
for (const step of trail.steps) {
|
|
40677
40855
|
lines.push(`${seven(step.hash)} ${step.subject}`);
|
|
40678
40856
|
for (const v of step.verdicts) lines.push(` judge ${v.id} = ${v.answer} (p=${v.p})`);
|
|
40857
|
+
if (step.approval !== void 0) lines.push(` approved ${step.approval.step} by ${step.approval.name} <${step.approval.email}>`);
|
|
40679
40858
|
}
|
|
40680
40859
|
lines.push(`ended (${trail.ended})`);
|
|
40681
40860
|
out.write(`${lines.join("\n")}\n`);
|
|
@@ -41215,7 +41394,8 @@ const gatherBeatDocument = (rest, rendered) => gen(function* () {
|
|
|
41215
41394
|
changes: computeStatusChanges(rest.changes),
|
|
41216
41395
|
next: yield* computeNextMatch(rest),
|
|
41217
41396
|
cost: rest.cost.total,
|
|
41218
|
-
costByModel: rest.cost.byModel
|
|
41397
|
+
costByModel: rest.cost.byModel,
|
|
41398
|
+
approvals: rest.run.approvals
|
|
41219
41399
|
}));
|
|
41220
41400
|
});
|
|
41221
41401
|
/**
|
|
@@ -41500,6 +41680,12 @@ const FLAGS = [
|
|
|
41500
41680
|
if (/[\r\n]/.test(value)) return left(`gtd: --var ${name} must be a single-line value`);
|
|
41501
41681
|
if (seen.has(name)) return left(`gtd: --var ${name} specified more than once`);
|
|
41502
41682
|
seen.add(name);
|
|
41683
|
+
if (name.startsWith("flag_")) {
|
|
41684
|
+
const pinned = flagSettingValue(value);
|
|
41685
|
+
if (pinned === void 0) return left(`gtd: --var ${name} must be 1, 0, true or false`);
|
|
41686
|
+
vars[name] = pinned;
|
|
41687
|
+
continue;
|
|
41688
|
+
}
|
|
41503
41689
|
vars[name] = value;
|
|
41504
41690
|
}
|
|
41505
41691
|
return right(vars);
|
|
@@ -41906,11 +42092,11 @@ const COMMAND_ROWS = [
|
|
|
41906
42092
|
arity: { name: "ref" },
|
|
41907
42093
|
details: [
|
|
41908
42094
|
"Replay one kept process under today's config and print its",
|
|
41909
|
-
"steps
|
|
41910
|
-
"(or unique prefix) or a branch name. Refuses
|
|
41911
|
-
"config file differs from its content at the
|
|
41912
|
-
"version mismatch only warns on stderr. --json
|
|
41913
|
-
"trail as one document; writes nothing"
|
|
42095
|
+
"steps, judge verdicts and approvals. <ref> is a full ref, an",
|
|
42096
|
+
"entry hash (or unique prefix) or a branch name. Refuses",
|
|
42097
|
+
"(exit 1) when a config file differs from its content at the",
|
|
42098
|
+
"ref's tip. A version mismatch only warns on stderr. --json",
|
|
42099
|
+
"prints the trail as one document; writes nothing"
|
|
41914
42100
|
]
|
|
41915
42101
|
},
|
|
41916
42102
|
{
|
|
@@ -42337,7 +42523,7 @@ const runCli = (argv, io) => {
|
|
|
42337
42523
|
return flatMap$4(Host, ({ env }) => try_({
|
|
42338
42524
|
try: () => resolveFlags(env, io.flags),
|
|
42339
42525
|
catch: (e) => new GtdError(e.message)
|
|
42340
|
-
})).pipe(flatMap$4(() => runCommand(plan.command, plan.json, out)), map$4((code) => {
|
|
42526
|
+
})).pipe(flatMap$4((flags) => runCommand(plan.command, plan.json, out).pipe(provideService(StartFlags, flagSettings(flags)))), map$4((code) => {
|
|
42341
42527
|
out.flush();
|
|
42342
42528
|
io.exit(code);
|
|
42343
42529
|
}), provide$1(io.layers(plan.verbose)), sandbox, catchAll((cause) => isInterruptedOnly(cause) ? failCause$2(cause) : report(io, plan.json.kind !== "off")(cause)));
|