@pmelab/gtd 27.5.0 → 27.7.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 +12 -2
- package/dist/chunks/{reserved-DM5W_LAt.mjs → reserved-Df8GeSHw.mjs} +37 -9
- package/dist/chunks/{tail-6gvLXXcg.mjs → tail-DmIfEAI4.mjs} +1 -1
- package/dist/flows.mjs +17 -1
- package/dist/gtd.bundle.mjs +56 -11
- package/dist/testkit.mjs +67 -26
- package/dist/workflows/cleanup.mjs +2 -2
- package/dist/workflows/feature.mjs +61 -10
- 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 +110 -16
- package/package.json +1 -1
- package/src/cli/Cli.ts +2 -2
- package/src/edge/Edge.test.ts +111 -0
- package/src/edge/Edge.ts +15 -3
- package/src/flags/index.test.ts +13 -0
- package/src/flags/index.ts +1 -0
- package/src/flows/runtime.test.ts +42 -0
- package/src/flows/runtime.ts +36 -0
- package/src/replay/Replay.test.ts +43 -0
- package/src/replay/Replay.ts +10 -2
- package/src/replay/Trailers.test.ts +54 -0
- package/src/replay/Trailers.ts +15 -1
- package/src/testkit/EmittedScriptRecognizer.test.ts +41 -0
- package/src/testkit/EmittedScriptRecognizer.ts +16 -7
- package/src/testkit/TestRepo.test.ts +79 -1
- package/src/testkit/TestRepo.ts +17 -6
- package/src/testkit/steps.ts +44 -1
- package/src/testkit/testkit.feature +35 -0
- package/src/workflows/cleanup/cleanup-path.feature +94 -0
- package/src/workflows/cleanup/cleanup.feature +0 -29
- package/src/workflows/feature/fast-path.feature +114 -0
- package/src/workflows/feature/feature.ts +3 -1
- package/src/workflows/feature/one-step-land.feature +108 -0
- package/src/workflows/feature/refactor-first.feature +137 -0
- package/src/workflows/feature/risk-tier.feature +238 -0
- package/src/workflows/feature/seam-rejection.feature +146 -0
- package/src/workflows/feature/seam.feature +0 -67
- package/src/workflows/feature/seam.ts +5 -4
- package/src/workflows/feature/steps.ts +3 -2
- package/src/workflows/feature/tier.test.ts +164 -0
- package/src/workflows/feature/tier.ts +74 -0
- package/src/workflows/shared/options.test.ts +6 -1
- package/src/workflows/shared/text.test.ts +129 -0
- package/src/workflows/shared/text.ts +57 -9
- package/src/workflows/shared/vars.ts +2 -0
- package/src/workflows/shared/workflows.steps.ts +13 -4
package/dist/testkit.mjs
CHANGED
|
@@ -6102,6 +6102,11 @@ const FLAGS$1 = [{
|
|
|
6102
6102
|
owner: "pmelab",
|
|
6103
6103
|
expiry: "2027-01-15",
|
|
6104
6104
|
kind: "release"
|
|
6105
|
+
}, {
|
|
6106
|
+
name: "risk_tier",
|
|
6107
|
+
owner: "pmelab",
|
|
6108
|
+
expiry: "2027-01-15",
|
|
6109
|
+
kind: "release"
|
|
6105
6110
|
}];
|
|
6106
6111
|
const BOOL = {
|
|
6107
6112
|
"0": false,
|
|
@@ -6931,6 +6936,7 @@ const isRecordableApproval = (approval) => {
|
|
|
6931
6936
|
const back = decodeApproval(encodeApproval(approval));
|
|
6932
6937
|
return back !== void 0 && encodeApproval(back) === encodeApproval(approval);
|
|
6933
6938
|
};
|
|
6939
|
+
const MARK_RE = /^([a-z][a-z0-9-]*)=(.+)$/;
|
|
6934
6940
|
const FORMAT_RE = /^[1-9][0-9]*$/;
|
|
6935
6941
|
const HASH_RE = /^(?:[0-9a-f]{40}|[0-9a-f]{64})$/;
|
|
6936
6942
|
const TRAILER_READERS = {
|
|
@@ -6944,6 +6950,13 @@ const TRAILER_READERS = {
|
|
|
6944
6950
|
const parsed = parseJson(value);
|
|
6945
6951
|
if (isVerdict(parsed)) into.judge.push(parsed);
|
|
6946
6952
|
},
|
|
6953
|
+
"Gtd-Mark": (value, into) => {
|
|
6954
|
+
const kv = MARK_RE.exec(value);
|
|
6955
|
+
if (kv !== null && !CONTROL_CHARACTER$1.test(value)) into.marks.push({
|
|
6956
|
+
name: kv[1],
|
|
6957
|
+
value: kv[2]
|
|
6958
|
+
});
|
|
6959
|
+
},
|
|
6947
6960
|
"Gtd-Var": (value, into) => {
|
|
6948
6961
|
const kv = VAR_RE.exec(value);
|
|
6949
6962
|
if (kv !== null) into.vars[kv[1]] = kv[2];
|
|
@@ -6991,6 +7004,7 @@ const parseCommitMessage = (message) => {
|
|
|
6991
7004
|
kept: void 0,
|
|
6992
7005
|
approved: void 0,
|
|
6993
7006
|
judge: [],
|
|
7007
|
+
marks: [],
|
|
6994
7008
|
vars: {},
|
|
6995
7009
|
config: {},
|
|
6996
7010
|
cost: [],
|
|
@@ -7015,6 +7029,7 @@ const formatCommitMessage = (spec) => {
|
|
|
7015
7029
|
for (const [path, hash] of sortedEntries(spec.config)) lines.push(`Gtd-Config: ${hash} ${encodePath(path)}`);
|
|
7016
7030
|
if (spec.cost !== void 0) lines.push(`Gtd-Cost: ${spec.cost.cost}${spec.cost.model !== void 0 ? ` ${spec.cost.model}` : ""}`);
|
|
7017
7031
|
for (const verdict of spec.judge ?? []) lines.push(`Gtd-Judge: ${JSON.stringify(verdict)}`);
|
|
7032
|
+
for (const m of spec.marks ?? []) lines.push(`Gtd-Mark: ${m.name}=${m.value}`);
|
|
7018
7033
|
if (spec.approved !== void 0) lines.push(`Gtd-Approved: ${encodeApproval(spec.approved)}`);
|
|
7019
7034
|
if (spec.branch !== void 0) lines.push(`Gtd-Branch: ${spec.branch}`);
|
|
7020
7035
|
if (spec.version !== void 0) lines.push(`Gtd-Version: ${spec.version}`);
|
|
@@ -7148,6 +7163,7 @@ const replay = async (input) => {
|
|
|
7148
7163
|
const trace = [];
|
|
7149
7164
|
const keptTips = /* @__PURE__ */ new Map();
|
|
7150
7165
|
let landed;
|
|
7166
|
+
const marks = [];
|
|
7151
7167
|
let outcome;
|
|
7152
7168
|
let signal;
|
|
7153
7169
|
const settled = new Promise((resolve) => {
|
|
@@ -7353,7 +7369,8 @@ const replay = async (input) => {
|
|
|
7353
7369
|
kind: "rest",
|
|
7354
7370
|
rest: step,
|
|
7355
7371
|
trace,
|
|
7356
|
-
landed
|
|
7372
|
+
landed,
|
|
7373
|
+
marks
|
|
7357
7374
|
});
|
|
7358
7375
|
return landPending(step, input.pending);
|
|
7359
7376
|
};
|
|
@@ -7391,7 +7408,8 @@ const replay = async (input) => {
|
|
|
7391
7408
|
kind: "ended",
|
|
7392
7409
|
via,
|
|
7393
7410
|
trace,
|
|
7394
|
-
landed
|
|
7411
|
+
landed,
|
|
7412
|
+
marks
|
|
7395
7413
|
};
|
|
7396
7414
|
const changesSince = (hash) => {
|
|
7397
7415
|
const tree = treeAt(hash);
|
|
@@ -7449,6 +7467,12 @@ const replay = async (input) => {
|
|
|
7449
7467
|
faults: findings.filter((f) => f.message.startsWith(`Code thread at ${c.path}:${t.line + 1}: `)).map((f) => f.message)
|
|
7450
7468
|
}));
|
|
7451
7469
|
}),
|
|
7470
|
+
mark: (name, value) => {
|
|
7471
|
+
if (pendingUsed) marks.push({
|
|
7472
|
+
name,
|
|
7473
|
+
value
|
|
7474
|
+
});
|
|
7475
|
+
},
|
|
7452
7476
|
kept: (name) => {
|
|
7453
7477
|
const tip = keptTips.get(scoped(name));
|
|
7454
7478
|
return tip === void 0 ? void 0 : {
|
|
@@ -8664,6 +8688,7 @@ const buildRun = (history, location, closingHash) => {
|
|
|
8664
8688
|
trace: processCommits.map((c, i) => traceEntryOf(c.hash, parsed[i])),
|
|
8665
8689
|
costEntries: parsed.flatMap(costEntriesOf),
|
|
8666
8690
|
judgeVerdicts: parsed.flatMap((m) => m.judge),
|
|
8691
|
+
marks: parsed.flatMap((m) => m.marks),
|
|
8667
8692
|
approvals: processCommits.flatMap((c, i) => approvalEntryOf(c.hash, parsed[i])),
|
|
8668
8693
|
pinnedVars: { ...first?.vars },
|
|
8669
8694
|
pinnedConfig: pinnedConfigOf(history.slice(Math.max(location.baseIndex, 0))),
|
|
@@ -9265,7 +9290,7 @@ const approvedTrailer = (rest, approver) => isSome(approver) ? { approved: {
|
|
|
9265
9290
|
step: rest.step.id,
|
|
9266
9291
|
...approver.value
|
|
9267
9292
|
} } : {};
|
|
9268
|
-
const landingTo = (rest, to, branch, approver) => {
|
|
9293
|
+
const landingTo = (rest, to, branch, approver, marks) => {
|
|
9269
9294
|
if (isSettledNoop(rest, to)) return {
|
|
9270
9295
|
kind: "noop",
|
|
9271
9296
|
settled: true
|
|
@@ -9283,17 +9308,20 @@ const landingTo = (rest, to, branch, approver) => {
|
|
|
9283
9308
|
from: rest.state,
|
|
9284
9309
|
to,
|
|
9285
9310
|
step: rest.step.id,
|
|
9311
|
+
...marks.length > 0 && { marks },
|
|
9286
9312
|
...approvedTrailer(rest, approver),
|
|
9287
|
-
...starts
|
|
9288
|
-
vars: rest.vars,
|
|
9289
|
-
config: rest.config
|
|
9290
|
-
} : {},
|
|
9313
|
+
...startTrailers(rest, starts),
|
|
9291
9314
|
...finishes ? closingTrailers(branch) : {}
|
|
9292
9315
|
},
|
|
9293
9316
|
...finishes ? { keepHistory: processHistoryRef(rest.run.startHash) } : {},
|
|
9294
9317
|
...keptTurn(rest)
|
|
9295
9318
|
};
|
|
9296
9319
|
};
|
|
9320
|
+
const startTrailers = (rest, starts) => starts ? {
|
|
9321
|
+
vars: rest.vars,
|
|
9322
|
+
config: rest.config
|
|
9323
|
+
} : {};
|
|
9324
|
+
const outcomeMarks = (outcome) => outcome.kind === "rest" || outcome.kind === "ended" ? outcome.marks : [];
|
|
9297
9325
|
/**
|
|
9298
9326
|
* What landing the pending turn at `rest` does — a pure decision from a
|
|
9299
9327
|
* replay with the working tree as the pending turn. The target step is
|
|
@@ -9317,7 +9345,7 @@ const decideLanding = (rest, verdicts, branch, approver) => gen(function* () {
|
|
|
9317
9345
|
}));
|
|
9318
9346
|
const to = yield* landingTarget(outcome, rest);
|
|
9319
9347
|
if (typeof to !== "string") return to;
|
|
9320
|
-
return landingTo(rest, to, branch, approver);
|
|
9348
|
+
return landingTo(rest, to, branch, approver, outcomeMarks(outcome));
|
|
9321
9349
|
});
|
|
9322
9350
|
/** Where landing the pending turn would leave the process, or `undefined` when it would land nothing. */
|
|
9323
9351
|
const previewLanding = (rest) => decideLanding(rest, void 0, none(), none()).pipe(map$4((landing) => landing.kind === "commit" ? landing.to : void 0), catchAll$1(() => succeed$6(void 0)));
|
|
@@ -9354,6 +9382,7 @@ const summaryFor = (run) => gen(function* () {
|
|
|
9354
9382
|
})),
|
|
9355
9383
|
processCost: run.costEntries.reduce((sum, entry) => sum + entry.cost, 0),
|
|
9356
9384
|
processCostByModel: costByModel(run.costEntries),
|
|
9385
|
+
marks: run.marks,
|
|
9357
9386
|
...settingsFor(resolved, run.pinnedVars, host.env, {}, {})
|
|
9358
9387
|
});
|
|
9359
9388
|
});
|
|
@@ -10796,7 +10825,7 @@ const FLAGS = [
|
|
|
10796
10825
|
help: [
|
|
10797
10826
|
"(gtd land, with --cost) tag that cost's model",
|
|
10798
10827
|
"(gtd judge run, llm answerer) the claude model; omitted:",
|
|
10799
|
-
"GTD_JUDGE_MODEL, then judge.json, then
|
|
10828
|
+
"GTD_JUDGE_MODEL, then judge.json, then sonnet"
|
|
10800
10829
|
]
|
|
10801
10830
|
},
|
|
10802
10831
|
{
|
|
@@ -11189,7 +11218,7 @@ const COMMAND_ROWS = [
|
|
|
11189
11218
|
"--answers <path> or GTD_JUDGE_ANSWERS; questions the file",
|
|
11190
11219
|
"does not cover are left out. --provider jev asks TypeSafe",
|
|
11191
11220
|
"(TYPESAFE_API_KEY). --provider llm asks the `claude` CLI",
|
|
11192
|
-
"on PATH (Claude Code only), model
|
|
11221
|
+
"on PATH (Claude Code only), model sonnet unless --model.",
|
|
11193
11222
|
"No --provider: GTD_JUDGE_PROVIDER, then ~/.config/gtd/",
|
|
11194
11223
|
"judge.json, else jev when TYPESAFE_API_KEY is set and no",
|
|
11195
11224
|
"model is given, else llm; --model likewise falls back to",
|
|
@@ -13047,18 +13076,22 @@ const recognizeGtdUncheck = (repo, block) => {
|
|
|
13047
13076
|
* definition) keeps the `@inmem` tier green.
|
|
13048
13077
|
*/
|
|
13049
13078
|
const recognizeDidNotRunComment = (block) => block === "# gtd emitted this and did NOT run it — pipe it into `sh` to land the turn" ? { kind: "noop" } : void 0;
|
|
13050
|
-
/** `combinedScript`'s optional half.
|
|
13079
|
+
/** `combinedScript`'s optional half. Reports `noop` whatever the inner result, mirroring bash's `( … ) || <warning>`. Exception: under `git: false` any unrecognized inner block fails the outer script (git or not), so the in-memory twin knowingly diverges from `sh` rather than run git unseen. */
|
|
13051
13080
|
const PRESENTATION_FAILURE_TEXT = `${extractQuotedTokens(PRESENTATION_FAILURE_WARNING)[0].replace(/\\n$/, "")}\n`;
|
|
13052
13081
|
const PRESENTATION_SUBSHELL_PREFIX = `${PRESENTATION_ONLY_COMMENT}\n(\n`;
|
|
13053
13082
|
const PRESENTATION_SUBSHELL_SUFFIX = `\n) || ${PRESENTATION_FAILURE_WARNING}`;
|
|
13054
|
-
const recognizePresentationSubshell = (repo, commands, block) => {
|
|
13083
|
+
const recognizePresentationSubshell = (repo, commands, block, git) => {
|
|
13055
13084
|
if (!block.startsWith(PRESENTATION_SUBSHELL_PREFIX) || !block.endsWith(PRESENTATION_SUBSHELL_SUFFIX)) return;
|
|
13056
13085
|
const inner = block.slice(PRESENTATION_SUBSHELL_PREFIX.length, block.length - PRESENTATION_SUBSHELL_SUFFIX.length);
|
|
13057
|
-
const result = applyEmittedScript(repo, commands, inner);
|
|
13086
|
+
const result = applyEmittedScript(repo, commands, inner, { git });
|
|
13058
13087
|
if (result.ok) return {
|
|
13059
13088
|
kind: "noop",
|
|
13060
13089
|
output: result.output
|
|
13061
13090
|
};
|
|
13091
|
+
if (!git && result.error?.startsWith(UNRECOGNIZED_PREFIX)) return {
|
|
13092
|
+
kind: "failed",
|
|
13093
|
+
error: `${result.output}${result.error}`
|
|
13094
|
+
};
|
|
13062
13095
|
return {
|
|
13063
13096
|
kind: "noop",
|
|
13064
13097
|
output: `${result.output}${PRESENTATION_FAILURE_TEXT}`
|
|
@@ -13165,17 +13198,17 @@ const recognizeWriteLines = (repo, block) => {
|
|
|
13165
13198
|
return { kind: "applied" };
|
|
13166
13199
|
};
|
|
13167
13200
|
/** Every recognizer `applyEmittedScript` tries, first match wins — `recognizeScriptedCommand` stays last since it's the exact-hit fallback for anything the rest don't claim. */
|
|
13168
|
-
const recognizersFor = (repo, commands) => [
|
|
13201
|
+
const recognizersFor = (repo, commands, git) => [
|
|
13169
13202
|
(block) => recognizeSetFlags(block),
|
|
13170
13203
|
(block) => recognizeDidNotRunComment(block),
|
|
13171
13204
|
(block) => recognizePrecondition(repo, block),
|
|
13172
13205
|
(block) => recognizeFileExistsGuard(repo, block),
|
|
13173
13206
|
(block) => recognizeBinaryGuard(commands, block),
|
|
13174
|
-
(block) => recognizeGitBuilders(repo, block),
|
|
13207
|
+
(block) => git ? recognizeGitBuilders(repo, block) : void 0,
|
|
13175
13208
|
(block) => recognizeDeferNotes(repo, block),
|
|
13176
13209
|
(block) => recognizeWriteLines(repo, block),
|
|
13177
13210
|
(block) => recognizeFailurePromptWrapper(repo, commands, block),
|
|
13178
|
-
(block) => recognizePresentationSubshell(repo, commands, block),
|
|
13211
|
+
(block) => recognizePresentationSubshell(repo, commands, block, git),
|
|
13179
13212
|
(block) => recognizeModeContradictionSkipNotice(block),
|
|
13180
13213
|
(block) => recognizeModeContradictionCheck(repo, commands, block),
|
|
13181
13214
|
(block) => recognizeGtdCheck(repo, block),
|
|
@@ -13198,7 +13231,7 @@ const recognizeBlock = (recognizers, block) => {
|
|
|
13198
13231
|
* non-zero scripted-command exit — exactly like `set -euo pipefail` stops a
|
|
13199
13232
|
* real script. No block after the failing one is applied.
|
|
13200
13233
|
*/
|
|
13201
|
-
const applyEmittedScript = (repo, commands, script) => {
|
|
13234
|
+
const applyEmittedScript = (repo, commands, script, { git = true } = {}) => {
|
|
13202
13235
|
const flow = applyFlowScript(repo, commands, script);
|
|
13203
13236
|
if (flow !== void 0) return flow.ok ? {
|
|
13204
13237
|
ok: true,
|
|
@@ -13210,10 +13243,10 @@ const applyEmittedScript = (repo, commands, script) => {
|
|
|
13210
13243
|
output: flow.output,
|
|
13211
13244
|
error: flow.error ?? ""
|
|
13212
13245
|
};
|
|
13213
|
-
return script.startsWith("#!") ? unrecognized(script) : applyBlocks(repo, commands, script);
|
|
13246
|
+
return script.startsWith("#!") ? unrecognized(script) : applyBlocks(repo, commands, script, git);
|
|
13214
13247
|
};
|
|
13215
|
-
const applyBlocks = (repo, commands, script) => {
|
|
13216
|
-
const recognizers = recognizersFor(repo, commands);
|
|
13248
|
+
const applyBlocks = (repo, commands, script, git) => {
|
|
13249
|
+
const recognizers = recognizersFor(repo, commands, git);
|
|
13217
13250
|
let output = "";
|
|
13218
13251
|
for (const block of splitBlocks(script)) {
|
|
13219
13252
|
const outcome = recognizeBlock(recognizers, block);
|
|
@@ -13237,11 +13270,12 @@ const failedAt = (outcome, output) => {
|
|
|
13237
13270
|
error: outcome.error
|
|
13238
13271
|
};
|
|
13239
13272
|
};
|
|
13273
|
+
const UNRECOGNIZED_PREFIX = "unrecognized script block: ";
|
|
13240
13274
|
const unrecognized = (block, output = "") => ({
|
|
13241
13275
|
ok: false,
|
|
13242
13276
|
status: 1,
|
|
13243
13277
|
output,
|
|
13244
|
-
error:
|
|
13278
|
+
error: `${UNRECOGNIZED_PREFIX}${block.split("\n")[0]}`
|
|
13245
13279
|
});
|
|
13246
13280
|
//#endregion
|
|
13247
13281
|
//#region src/testkit/TestRepo.ts
|
|
@@ -13293,10 +13327,17 @@ const LINES = [
|
|
|
13293
13327
|
repo.writeFile(dest, content);
|
|
13294
13328
|
}]
|
|
13295
13329
|
];
|
|
13296
|
-
const
|
|
13297
|
-
|
|
13298
|
-
|
|
13299
|
-
|
|
13330
|
+
const knownLine = (line) => LINES.find(([pattern]) => pattern.test(line));
|
|
13331
|
+
const cannotRun = (error) => /* @__PURE__ */ new Error(`testkit: cannot run script in memory — give the step an actor instead\n${error}`);
|
|
13332
|
+
const runLines = (repo, script) => {
|
|
13333
|
+
const lines = script.split("\n").map((line) => line.trim());
|
|
13334
|
+
const known = lines.map(knownLine);
|
|
13335
|
+
if (known.every(Boolean)) {
|
|
13336
|
+
known.forEach((k, i) => k[1](repo, words(lines[i])));
|
|
13337
|
+
return;
|
|
13338
|
+
}
|
|
13339
|
+
const applied = applyEmittedScript(repo, /* @__PURE__ */ new Map(), script, { git: false });
|
|
13340
|
+
if (!applied.ok) throw cannotRun(applied.error ?? "");
|
|
13300
13341
|
};
|
|
13301
13342
|
const failed = (what, result) => /* @__PURE__ */ new Error(`${what} exited ${result.exitCode}\n${result.stderr}${result.stdout}`);
|
|
13302
13343
|
const testRepo = (options) => {
|
|
@@ -13378,7 +13419,7 @@ const testRepo = (options) => {
|
|
|
13378
13419
|
show: (ref, path) => repo.fileAtRef(ref, path) ?? void 0,
|
|
13379
13420
|
runScript: (script) => {
|
|
13380
13421
|
const flow = applyFlowScript(repo, /* @__PURE__ */ new Map(), script);
|
|
13381
|
-
if (flow === void 0)
|
|
13422
|
+
if (flow === void 0) runLines(repo, script);
|
|
13382
13423
|
else if (!flow.ok) throw new Error(`testkit: ${flow.error ?? ""}`);
|
|
13383
13424
|
},
|
|
13384
13425
|
gtd,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { agent, changes, glob, read, refuse, scope, start, vars } from "../flows.mjs";
|
|
2
|
-
import { a as flagShipped, b as coder, c as envDefaults, i as skills, n as summary, r as access, s as defaults, t as steering } from "../chunks/reserved-
|
|
3
|
-
import { i as refusePendingHandOff, r as handOffTail } from "../chunks/tail-
|
|
2
|
+
import { a as flagShipped, b as coder, c as envDefaults, i as skills, n as summary, r as access, s as defaults, t as steering } from "../chunks/reserved-Df8GeSHw.mjs";
|
|
3
|
+
import { i as refusePendingHandOff, r as handOffTail } from "../chunks/tail-DmIfEAI4.mjs";
|
|
4
4
|
import { i as requireFlag, r as registryEntry, t as flagReferences } from "../chunks/registry-CP3YuV9j.mjs";
|
|
5
5
|
//#region src/workflows/cleanup/cleanup.ts
|
|
6
6
|
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.`;
|
|
@@ -1,10 +1,60 @@
|
|
|
1
|
-
import { answered, changes, changesSince, glob, hasThreadFor, head, human, judge, judgeDiffShape, kept, numeric, quote, read, refuse, removeScript, requireAnswers, requireProgress, requireReplies, requireRevert, requireThreadsClosed, restoreScript, revertScript, run, scope, seamGate, start, vars } from "../flows.mjs";
|
|
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 refactorFixPrompt, A as architectureAuthorPrompt, B as designGateAnswerMessage, C as fixRisks, D as reviewing, F as buildReviewCollectingPrompt, H as designTriagePrompt, I as buildReviewFixNitsPrompt, J as implementFixPrompt, K as idleMessage, L as buildReviewTriageMessage, M as architectureGateAnswerMessage, N as buildReviewAnswerQuestionsPrompt, O as agentWithSkills, P as buildReviewAwaitAfterDeferMessage, Q as refactorBuildingPrompt, R as builderSystem, T as reviewMissing, V as designSystem, X as packagesItemBuildingPrompt, Y as naivePassPrompt, Z as packagesItemFixSuitePrompt, _ as REQUIREMENTS, at as seamByHandMessage, b as coder, c as envDefaults, ct as unwindFailedMessage, d as gate, et as reviewEditNotesCapture, h as FEEDBACK, i as skills, it as reviewerSystem, j as architectureDecomposePrompt, k as architectSystem, lt as unwindFailure, m as ARCHITECTURE, n as summary, o as shipped, ot as seamSteerMessage, p as healthy, q as implementBuildingPrompt, r as access, rt as reviewNotesCapture, s as defaults, st as startGateBlockedMessage, t as steering, tt as reviewEditsCapture, v as REVIEW, w as planner, x as fixCheck, y as awaitReview, z as deferNotesScript } from "../chunks/reserved-
|
|
4
|
-
import { a as tail, i as refusePendingHandOff, n as closeReview } from "../chunks/tail-
|
|
3
|
+
import { $ as refactorFixPrompt, A as architectureAuthorPrompt, B as designGateAnswerMessage, C as fixRisks, D as reviewing, F as buildReviewCollectingPrompt, H as designTriagePrompt, I as buildReviewFixNitsPrompt, J as implementFixPrompt, K as idleMessage, L as buildReviewTriageMessage, M as architectureGateAnswerMessage, N as buildReviewAnswerQuestionsPrompt, O as agentWithSkills, P as buildReviewAwaitAfterDeferMessage, Q as refactorBuildingPrompt, R as builderSystem, T as reviewMissing, V as designSystem, X as packagesItemBuildingPrompt, Y as naivePassPrompt, Z as packagesItemFixSuitePrompt, _ as REQUIREMENTS, at as seamByHandMessage, b as coder, c as envDefaults, ct as unwindFailedMessage, d as gate, et as reviewEditNotesCapture, h as FEEDBACK, i as skills, it as reviewerSystem, j as architectureDecomposePrompt, k as architectSystem, lt as unwindFailure, m as ARCHITECTURE, n as summary, o as shipped, ot as seamSteerMessage, p as healthy, q as implementBuildingPrompt, r as access, rt as reviewNotesCapture, s as defaults, st as startGateBlockedMessage, t as steering, tt as reviewEditsCapture, v as REVIEW, w as planner, x as fixCheck, y as awaitReview, z as deferNotesScript } from "../chunks/reserved-Df8GeSHw.mjs";
|
|
4
|
+
import { a as tail, i as refusePendingHandOff, n as closeReview } from "../chunks/tail-DmIfEAI4.mjs";
|
|
5
|
+
//#region src/workflows/feature/tier.ts
|
|
6
|
+
/** GTD_FLAG_NAIVE_PASS, GTD_FLAG_RISK_TIER: both pinned at process start, so a mid-process flip never moves a process. */
|
|
7
|
+
const riskTierOn = () => vars.flag_naive_pass === "1" && vars.flag_risk_tier === "1";
|
|
8
|
+
const markFenced = (text) => {
|
|
9
|
+
let fence;
|
|
10
|
+
return text.split(/\r?\n/).map((line) => {
|
|
11
|
+
const run = /^ {0,3}(`{3,}|~{3,})/.exec(line)?.[1];
|
|
12
|
+
const fenced = fence !== void 0 || run !== void 0;
|
|
13
|
+
if (fence === void 0) fence = run;
|
|
14
|
+
else if (run !== void 0 && run[0] === fence[0] && run.length >= fence.length && /^ {0,3}(`+|~+)[ \t]*$/.test(line)) fence = void 0;
|
|
15
|
+
return {
|
|
16
|
+
line,
|
|
17
|
+
fenced
|
|
18
|
+
};
|
|
19
|
+
});
|
|
20
|
+
};
|
|
21
|
+
const sectionLines = (text, title) => {
|
|
22
|
+
if (text === void 0) return [];
|
|
23
|
+
const heading = new RegExp(`^##[ \\t]+${title}[ \\t]*$`, "i");
|
|
24
|
+
const lines = markFenced(text);
|
|
25
|
+
const start = lines.findIndex((l) => !l.fenced && heading.test(l.line));
|
|
26
|
+
if (start < 0) return [];
|
|
27
|
+
const rest = lines.slice(start + 1);
|
|
28
|
+
const end = rest.findIndex((l) => !l.fenced && /^#{1,2}[ \t]/.test(l.line));
|
|
29
|
+
return end < 0 ? rest : rest.slice(0, end);
|
|
30
|
+
};
|
|
31
|
+
/** Anything but a legible `low` is `high`: under-tiering is the risky direction. */
|
|
32
|
+
const readRiskTier = (requirements) => {
|
|
33
|
+
return (sectionLines(requirements, "Risk Tier").find((l) => !l.fenced && l.line.trim() !== "")?.line)?.trim().split(/\s+/)[0]?.replace(/[`*_]/g, "").toLowerCase() === "low" ? "low" : "high";
|
|
34
|
+
};
|
|
35
|
+
const CONTROL = /[\x00-\x1f\x7f-\x9f\u2028\u2029]/g;
|
|
36
|
+
const tidy = (text) => text.replace(/\s+/g, " ").replace(CONTROL, "").trim();
|
|
37
|
+
const productionSignals = (requirements) => {
|
|
38
|
+
const items = [];
|
|
39
|
+
for (const { line, fenced } of sectionLines(requirements, "Production Signals")) {
|
|
40
|
+
if (fenced) continue;
|
|
41
|
+
const top = /^[-*+][ \t]+(.*)$/.exec(line);
|
|
42
|
+
if (top !== null) items.push(top[1]);
|
|
43
|
+
}
|
|
44
|
+
return items.map(tidy).filter((item) => item !== "");
|
|
45
|
+
};
|
|
46
|
+
/** The tier is always marked, first, so the last `risk-tier` mark opens the current lap's marks. */
|
|
47
|
+
const recordRisk = () => {
|
|
48
|
+
const requirements = read(REQUIREMENTS);
|
|
49
|
+
const tier = readRiskTier(requirements);
|
|
50
|
+
mark("risk-tier", tier);
|
|
51
|
+
for (const signal of productionSignals(requirements)) mark("production-signal", signal);
|
|
52
|
+
return tier;
|
|
53
|
+
};
|
|
54
|
+
//#endregion
|
|
5
55
|
//#region src/workflows/feature/steps.ts
|
|
6
56
|
/** Turn the sketch since `base` into `.gtd/REQUIREMENTS.md`. */
|
|
7
|
-
const triage = (base) => agentWithSkills("triage", designTriagePrompt(base), {
|
|
57
|
+
const triage = (base) => agentWithSkills("triage", designTriagePrompt(base, riskTierOn()), {
|
|
8
58
|
label: "Triaging the change",
|
|
9
59
|
file: REQUIREMENTS,
|
|
10
60
|
mode: "qa",
|
|
@@ -24,7 +74,7 @@ const decompose = () => agentWithSkills("decompose.decomposing", architectureDec
|
|
|
24
74
|
system: architectSystem()
|
|
25
75
|
});
|
|
26
76
|
const answerProductQuestions = () => human("gate.answer", {
|
|
27
|
-
message: designGateAnswerMessage(),
|
|
77
|
+
message: designGateAnswerMessage(riskTierOn()),
|
|
28
78
|
label: "Awaiting your product answers",
|
|
29
79
|
file: REQUIREMENTS,
|
|
30
80
|
mode: "qa",
|
|
@@ -473,8 +523,8 @@ const naivePass = (rejection) => agentWithSkills("naive", naivePassPrompt(reject
|
|
|
473
523
|
model: coder(),
|
|
474
524
|
system: builderSystem()
|
|
475
525
|
});
|
|
476
|
-
const implement = (reference) => scope("implement", async () => {
|
|
477
|
-
await agentWithSkills("building", implementBuildingPrompt(reference), {
|
|
526
|
+
const implement = (reference, tier) => scope("implement", async () => {
|
|
527
|
+
await agentWithSkills("building", implementBuildingPrompt(reference, tier), {
|
|
478
528
|
label: "Implementing",
|
|
479
529
|
model: coder(),
|
|
480
530
|
system: builderSystem()
|
|
@@ -521,14 +571,14 @@ const decide = () => scope("seam", async () => {
|
|
|
521
571
|
retries = 0;
|
|
522
572
|
}
|
|
523
573
|
});
|
|
524
|
-
const seamBuild = async () => {
|
|
574
|
+
const seamBuild = async (tier) => {
|
|
525
575
|
const { reference, shape } = await decide();
|
|
526
576
|
if (shape !== void 0 && !await refactorFirst(shape, { reference })) await scope("seam", () => human("by-hand", {
|
|
527
577
|
message: seamByHandMessage(shape.strays, shape.unreadable, reference),
|
|
528
578
|
label: "Reviewing unreadable files by hand",
|
|
529
579
|
acceptClean: true
|
|
530
580
|
}));
|
|
531
|
-
await implement(reference);
|
|
581
|
+
await implement(reference, tier);
|
|
532
582
|
};
|
|
533
583
|
//#endregion
|
|
534
584
|
//#region src/workflows/feature/feature.ts
|
|
@@ -558,7 +608,8 @@ const planAndBuild = async (firstBase) => {
|
|
|
558
608
|
let base = firstBase;
|
|
559
609
|
for (;;) {
|
|
560
610
|
await design(base);
|
|
561
|
-
|
|
611
|
+
const tier = riskTierOn() ? recordRisk() : void 0;
|
|
612
|
+
if (vars.flag_naive_pass === "1") await seamBuild(tier);
|
|
562
613
|
else {
|
|
563
614
|
await architecture();
|
|
564
615
|
await packages();
|
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 { O as agentWithSkills, U as finisherSystem, W as fixReviewNotesPrompt, b as coder, c as envDefaults, i as skills, l as baseline, n as summary, o as shipped, r as access, s as defaults, t as steering, v as REVIEW } from "../chunks/reserved-
|
|
4
|
-
import { r as handOffTail, t as REVIEW_ORIG } from "../chunks/tail-
|
|
3
|
+
import { O as agentWithSkills, U as finisherSystem, W as fixReviewNotesPrompt, b as coder, c as envDefaults, i as skills, l as baseline, n as summary, o as shipped, r as access, s as defaults, t as steering, v as REVIEW } from "../chunks/reserved-Df8GeSHw.mjs";
|
|
4
|
+
import { r as handOffTail, t as REVIEW_ORIG } from "../chunks/tail-DmIfEAI4.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 { c as envDefaults, d as gate, i as skills, n as summary, nt as reviewGateBlockedMessage, o as shipped, r as access, s as defaults, t as steering } from "../chunks/reserved-
|
|
3
|
-
import { i as refusePendingHandOff, r as handOffTail } from "../chunks/tail-
|
|
2
|
+
import { c as envDefaults, d as gate, i as skills, n as summary, nt as reviewGateBlockedMessage, o as shipped, r as access, s as defaults, t as steering } from "../chunks/reserved-Df8GeSHw.mjs";
|
|
3
|
+
import { i as refusePendingHandOff, r as handOffTail } from "../chunks/tail-DmIfEAI4.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,5 +1,5 @@
|
|
|
1
1
|
import { agent, changesSince, head, read, refuse, scope, vars } from "../flows.mjs";
|
|
2
|
-
import { a as flagShipped, b as coder, c as envDefaults, d as gate, i as skills, n as summary, r as access, s as defaults, t as steering } from "../chunks/reserved-
|
|
2
|
+
import { a as flagShipped, b as coder, c as envDefaults, d as gate, i as skills, n as summary, r as access, s as defaults, t as steering } from "../chunks/reserved-Df8GeSHw.mjs";
|
|
3
3
|
import { i as requireFlag, n as flipProblems } from "../chunks/registry-CP3YuV9j.mjs";
|
|
4
4
|
//#region src/workflows/rollout/rollout.ts
|
|
5
5
|
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.`;
|