@pmelab/gtd 27.4.0 → 27.6.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 +17 -0
- package/dist/chunks/{Schema-CyImoHTj.mjs → Schema-DO5ttJaI.mjs} +1 -1
- package/dist/chunks/{reserved-BPdVZkgv.mjs → reserved-Df8GeSHw.mjs} +97 -8
- package/dist/chunks/{tail-iLAMoigl.mjs → tail-DmIfEAI4.mjs} +1 -1
- package/dist/flows.mjs +20 -2
- package/dist/gtd.bundle.mjs +97 -17
- package/dist/testkit.mjs +80 -18
- package/dist/workflows/cleanup.mjs +2 -2
- package/dist/workflows/feature.mjs +193 -66
- 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 +447 -114
- package/package.json +1 -1
- package/src/cli/Cli.test.ts +49 -1
- package/src/cli/Cli.ts +20 -3
- package/src/edge/Edge.test.ts +156 -1
- package/src/edge/Edge.ts +30 -7
- package/src/flags/index.test.ts +27 -1
- package/src/flags/index.ts +15 -3
- package/src/flows/helpers.ts +7 -3
- 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/replay/judgeDiffShape.test.ts +43 -0
- package/src/step/planStart.ts +3 -2
- package/src/testkit/TestRepo.ts +3 -0
- package/src/testkit/steps.ts +18 -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 +11 -2
- package/src/workflows/feature/index.ts +1 -0
- package/src/workflows/feature/risk-tier.feature +301 -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 +111 -0
- 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/index.ts +1 -0
- package/src/workflows/shared/options.test.ts +17 -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 +188 -0
- package/src/workflows/shared/text.ts +139 -5
- package/src/workflows/shared/vars.ts +6 -0
- package/src/workflows/shared/workflows.steps.ts +47 -1
package/dist/testkit.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { checkScript, configProblems, configure, installContext, isFactoryWorkflow, moveScript, protocolMismatch, removeScript, restoreScript, revertScript, showScript, t as flows_exports } from "./flows.mjs";
|
|
2
2
|
import { E as ACCESS_REFUSAL, _ as demandOf, a as landFields, c as renderLandJson, d as renderBeatPlain, f as beatDocument, g as beatKindOf, i as judgeJson, k as renderFormat, l as renderLandPlain, m as statusOf, n as resolveSession, p as renderBeatJson, r as selectPath, s as noopText, t as globMatches } from "./chunks/wire-C12dahrH.mjs";
|
|
3
3
|
import { C as headingSections, S as headingSectionBodies, a as unansweredQuestions, b as parseThreadsWithFindings, n as openThreads, r as steeringFormatFor, s as parseCodeThreads, t as BUILT_IN_MODE_NAMES, w as formatFinding } from "./chunks/steering-DVILanVf.mjs";
|
|
4
|
-
import { $ as logWarning, $i as
|
|
4
|
+
import { $ as logWarning, $i as NodeInspectSymbol, $n as map$5, $r as make$19, $t as provide$1, A as catchIf, Ai as add, An as scopeMake, Ar as flatMap$3, At as suspend$5, B as flatMap$4, Bi as getOrElse, Bn as get$3, Br as sync$1, Bt as unsafeMakeLatch, C as all$1, Ci as toArray, Cn as isInterrupted, Cr as exitFail, Ct as sandbox, D as async, Di as GenericTag, Dn as squash, Dr as exitZipRight, Dt as scopedWith$1, E as asVoid$1, Ei as unsafeHead, En as reduceWithContext, Er as exitVoid, Et as scoped$4, F as ensuring$2, Fi as fromIterable, Fn as join$3, Fr as map$3, Ft as tryMap, G as forkIn, Gi as none, Gn as TypeId$10, Gr as whenEffect, Gt as zipLeft, H as fnUntraced, Hi as isSome, Hn as modify$1, Hr as uninterruptible, Ht as when, I as exit, Ii as splitAt, In as sequential, Ir as onInterrupt, It as tryPromise, J as ignore, Ji as left, Jn as all, Jr as DeferredTypeId$1, Jt as spaced$1, K as forkScoped, Ki as some, Kn as Class, Kr as withFiberRuntime, Kt as zipRight$3, L as fail$5, Li as filter$1, Ln as bind, Lr as scopeClose, Lt as try_, M as die$2, Mi as getOption, Mn as _await$2, Mr as interruptAsFiber, Mt as tap, N as dieMessage, Ni as omit, Nn as inheritAll$1, Nr as interruptFiber, Nt as tapErrorCause, O as catchAll$1, Oi as Reference, On as ensuring$3, Or as fiberId, Ot as serviceOption, P as either, Pi as empty$5, Pn as interruptAllAs$1, Pr as interruptible$1, Pt as timeout, Q as isEffect, Qi as BaseProto, Qn as isFailure, Qr as ids, Qt as mergeAll$2, R as failCause$4, Ri as flatMap$5, Rn as bindTo, Rr as succeed$4, Rt as uninterruptible$1, S as addFinalizer$1, Si as takeRight, Sn as isDieType, Sr as exitAs, St as runtime, T as as, Ti as unsafeGet, Tn as map$6, Tr as exitSucceed, Tt as scopeWith, U as forEach$2, Ui as map$8, Un as set$1, Ur as uninterruptibleMask$1, Ut as withFiberRuntime$1, V as flatten$4, Vi as isNone, Vn as make$16, Vr as tap$1, Vt as unsafeMakeSemaphore$1, W as forkDaemon, Wi as match$4, Wn as update$1, Wr as void_$2, Wt as zip$1, X as interruptible, Xi as right, Xn as fail$4, Xr as combine, Xt as tagged, Y as interrupt$4, Yi as match$2, Yn as die, Yr as empty$6, Yt as TaggedError$1, Z as intoDeferred, Zi as pipeArguments, Zn as failCause$3, Zr as combineAll, Zt as fromEffect$5, _ as decodeSync, _i as of, _n as fail$3, _r as deferredIsDone, _t as raceWith, a as Literal, aa as constTrue, ai as size$2, an as updateContext$1, ar as zipRight$2, at as matchCause, b as acquireRelease, bi as reverse, bn as interrupt$3, br as deferredUnsafeDone, bt as runPromise, c as String$, ca as dual, ci as append$1, cn as Scope, cr as asyncInterrupt, ct as negate, d as Union, di as empty$4, dn as close, dr as deferredAwait, dt as orElse, ea as format, ei as none$1, en as provideMerge$1, er as mapBoth, et as makeSemaphore, f as between, fi as filter, fn as extend, fr as deferredComplete, ft as orElseSucceed, gi as map$7, gn as die$1, gr as deferredInterruptWith, gt as race, hi as isEmpty$2, hn as NoSuchElementException, hr as deferredInterrupt, ht as provideService, i as Defect, ia as constFalse, ii as has, in as unsafeRunPromiseExit, ir as zip$2, it as match$5, j as catchTag, ji as get$5, jn as scopeTag, jr as interrupt$2, jt as sync$2, k as catchAllCause, ki as Tag, kn as forEachConcurrentDiscard, kr as fiberRefLocally, kt as succeed$6, l as Struct, la as identity, li as appendAll, ln as addFinalizer, lr as catchAll, lt as onError, m as optional$1, mi as isChunk, mn as make$18, mr as deferredFailCause, mt as provide$2, n as Boolean$, na as equals, ni as make$17, nn as succeed$3, nr as succeed$5, nt as mapBoth$1, o as Never, oa as constUndefined, oi as decode, on as forkIn$1, or as as$1, ot as matchCauseEffect, p as decodeUnknown, pi as fromIterable$1, pn as fork, pr as deferredFail, pt as promise, q as gen, qi as isLeft, qn as spanTag, qr as zipRight$1, qt as zipWith, r as Cause, ra as hasProperty, ri as set$2, rn as unsafeFork, rr as void_$3, rt as mapError, s as Number$, sa as constVoid, si as isFinite, sn as unsafeMakeSemaphore, sr as asVoid, st as matchEffect, t as Array$, ta as toJSON, ti as get$4, tn as scoped$3, tr as match$3, tt as map$4, u as TaggedError, ua as pipe, ui as drop, un as addFinalizerExit, ur as currentContext$1, ut as orDie, v as encodeSync, vi as prepend, vn as failureOrCause, vr as deferredMake, vt as repeat, w as annotateLogs, wi as unsafeFromArray, wn as isInterruptedOnly, wr as exitFailCause, wt as scope, x as acquireUseRelease$2, xi as take$2, xn as interruptors, xr as deferredUnsafeMake, xt as runPromiseExit$1, y as _void, yi as reduce, yn as flipCauseOption, yr as deferredSucceed, yt as retry, z as fiberIdWith, zi as fromNullable, zn as sleep, zr as suspend$4, zt as uninterruptibleMask } from "./chunks/Schema-DO5ttJaI.mjs";
|
|
5
5
|
import { createRequire } from "node:module";
|
|
6
6
|
import * as Crypto from "node:crypto";
|
|
7
7
|
import { createHash } from "node:crypto";
|
|
@@ -6097,13 +6097,28 @@ const restorability = (git, headHash, tipHash) => gen(function* () {
|
|
|
6097
6097
|
});
|
|
6098
6098
|
//#endregion
|
|
6099
6099
|
//#region src/flags/index.ts
|
|
6100
|
-
const FLAGS$1 = [
|
|
6100
|
+
const FLAGS$1 = [{
|
|
6101
|
+
name: "naive_pass",
|
|
6102
|
+
owner: "pmelab",
|
|
6103
|
+
expiry: "2027-01-15",
|
|
6104
|
+
kind: "release"
|
|
6105
|
+
}, {
|
|
6106
|
+
name: "risk_tier",
|
|
6107
|
+
owner: "pmelab",
|
|
6108
|
+
expiry: "2027-01-15",
|
|
6109
|
+
kind: "release"
|
|
6110
|
+
}];
|
|
6101
6111
|
const BOOL = {
|
|
6102
6112
|
"0": false,
|
|
6103
6113
|
false: false,
|
|
6104
6114
|
"1": true,
|
|
6105
6115
|
true: true
|
|
6106
6116
|
};
|
|
6117
|
+
/** A `flag_<name>` setting's value as the pinned `"1"`/`"0"`, or undefined when garbled. */
|
|
6118
|
+
const flagSettingValue = (raw) => {
|
|
6119
|
+
const key = raw.toLowerCase();
|
|
6120
|
+
return Object.hasOwn(BOOL, key) ? BOOL[key] ? "1" : "0" : void 0;
|
|
6121
|
+
};
|
|
6107
6122
|
const resolveFlags = (env, registry = FLAGS$1) => {
|
|
6108
6123
|
const out = {};
|
|
6109
6124
|
for (const { name, defaultOn } of registry) {
|
|
@@ -6118,6 +6133,7 @@ const resolveFlags = (env, registry = FLAGS$1) => {
|
|
|
6118
6133
|
}
|
|
6119
6134
|
return out;
|
|
6120
6135
|
};
|
|
6136
|
+
const flagSettings = (flags) => Object.fromEntries(Object.entries(flags).map(([name, on]) => [`flag_${name}`, on ? "1" : "0"]));
|
|
6121
6137
|
//#endregion
|
|
6122
6138
|
//#region src/install/Version.ts
|
|
6123
6139
|
const _require = createRequire(import.meta.url);
|
|
@@ -6920,6 +6936,7 @@ const isRecordableApproval = (approval) => {
|
|
|
6920
6936
|
const back = decodeApproval(encodeApproval(approval));
|
|
6921
6937
|
return back !== void 0 && encodeApproval(back) === encodeApproval(approval);
|
|
6922
6938
|
};
|
|
6939
|
+
const MARK_RE = /^([a-z][a-z0-9-]*)=(.+)$/;
|
|
6923
6940
|
const FORMAT_RE = /^[1-9][0-9]*$/;
|
|
6924
6941
|
const HASH_RE = /^(?:[0-9a-f]{40}|[0-9a-f]{64})$/;
|
|
6925
6942
|
const TRAILER_READERS = {
|
|
@@ -6933,6 +6950,13 @@ const TRAILER_READERS = {
|
|
|
6933
6950
|
const parsed = parseJson(value);
|
|
6934
6951
|
if (isVerdict(parsed)) into.judge.push(parsed);
|
|
6935
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
|
+
},
|
|
6936
6960
|
"Gtd-Var": (value, into) => {
|
|
6937
6961
|
const kv = VAR_RE.exec(value);
|
|
6938
6962
|
if (kv !== null) into.vars[kv[1]] = kv[2];
|
|
@@ -6980,6 +7004,7 @@ const parseCommitMessage = (message) => {
|
|
|
6980
7004
|
kept: void 0,
|
|
6981
7005
|
approved: void 0,
|
|
6982
7006
|
judge: [],
|
|
7007
|
+
marks: [],
|
|
6983
7008
|
vars: {},
|
|
6984
7009
|
config: {},
|
|
6985
7010
|
cost: [],
|
|
@@ -7004,6 +7029,7 @@ const formatCommitMessage = (spec) => {
|
|
|
7004
7029
|
for (const [path, hash] of sortedEntries(spec.config)) lines.push(`Gtd-Config: ${hash} ${encodePath(path)}`);
|
|
7005
7030
|
if (spec.cost !== void 0) lines.push(`Gtd-Cost: ${spec.cost.cost}${spec.cost.model !== void 0 ? ` ${spec.cost.model}` : ""}`);
|
|
7006
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}`);
|
|
7007
7033
|
if (spec.approved !== void 0) lines.push(`Gtd-Approved: ${encodeApproval(spec.approved)}`);
|
|
7008
7034
|
if (spec.branch !== void 0) lines.push(`Gtd-Branch: ${spec.branch}`);
|
|
7009
7035
|
if (spec.version !== void 0) lines.push(`Gtd-Version: ${spec.version}`);
|
|
@@ -7137,6 +7163,7 @@ const replay = async (input) => {
|
|
|
7137
7163
|
const trace = [];
|
|
7138
7164
|
const keptTips = /* @__PURE__ */ new Map();
|
|
7139
7165
|
let landed;
|
|
7166
|
+
const marks = [];
|
|
7140
7167
|
let outcome;
|
|
7141
7168
|
let signal;
|
|
7142
7169
|
const settled = new Promise((resolve) => {
|
|
@@ -7342,7 +7369,8 @@ const replay = async (input) => {
|
|
|
7342
7369
|
kind: "rest",
|
|
7343
7370
|
rest: step,
|
|
7344
7371
|
trace,
|
|
7345
|
-
landed
|
|
7372
|
+
landed,
|
|
7373
|
+
marks
|
|
7346
7374
|
});
|
|
7347
7375
|
return landPending(step, input.pending);
|
|
7348
7376
|
};
|
|
@@ -7380,7 +7408,8 @@ const replay = async (input) => {
|
|
|
7380
7408
|
kind: "ended",
|
|
7381
7409
|
via,
|
|
7382
7410
|
trace,
|
|
7383
|
-
landed
|
|
7411
|
+
landed,
|
|
7412
|
+
marks
|
|
7384
7413
|
};
|
|
7385
7414
|
const changesSince = (hash) => {
|
|
7386
7415
|
const tree = treeAt(hash);
|
|
@@ -7438,6 +7467,12 @@ const replay = async (input) => {
|
|
|
7438
7467
|
faults: findings.filter((f) => f.message.startsWith(`Code thread at ${c.path}:${t.line + 1}: `)).map((f) => f.message)
|
|
7439
7468
|
}));
|
|
7440
7469
|
}),
|
|
7470
|
+
mark: (name, value) => {
|
|
7471
|
+
if (pendingUsed) marks.push({
|
|
7472
|
+
name,
|
|
7473
|
+
value
|
|
7474
|
+
});
|
|
7475
|
+
},
|
|
7441
7476
|
kept: (name) => {
|
|
7442
7477
|
const tip = keptTips.get(scoped(name));
|
|
7443
7478
|
return tip === void 0 ? void 0 : {
|
|
@@ -7533,6 +7568,8 @@ const compileConfig = (value, origin) => {
|
|
|
7533
7568
|
//#endregion
|
|
7534
7569
|
//#region src/workflow/vars.ts
|
|
7535
7570
|
const PREFIX = "GTD_";
|
|
7571
|
+
/** The live release flags as `flag_<name>` settings, provided by the CLI; absent means no flags. */
|
|
7572
|
+
var StartFlags = class extends Reference()("StartFlags", { defaultValue: () => ({}) }) {};
|
|
7536
7573
|
/**
|
|
7537
7574
|
* One kind of setting, later wins: the workflow's own defaults, `--var`
|
|
7538
7575
|
* overrides (process settings only; environment settings pass `{}`), then
|
|
@@ -7552,6 +7589,18 @@ const resolveVars = (workflowVars, entryVars, env) => {
|
|
|
7552
7589
|
}
|
|
7553
7590
|
return merged;
|
|
7554
7591
|
};
|
|
7592
|
+
/**
|
|
7593
|
+
* `resolveVars` for a process being STARTED: `flags` land between the workflow
|
|
7594
|
+
* defaults and `--var`, for already-declared names only. A running process never
|
|
7595
|
+
* calls this — it replays its pinned record.
|
|
7596
|
+
*/
|
|
7597
|
+
const resolveStartVars = (workflowVars, flags, entryVars, env) => {
|
|
7598
|
+
const declared = Object.fromEntries(Object.entries(flags).filter(([name]) => Object.hasOwn(workflowVars, name)));
|
|
7599
|
+
return resolveVars({
|
|
7600
|
+
...workflowVars,
|
|
7601
|
+
...declared
|
|
7602
|
+
}, entryVars, env);
|
|
7603
|
+
};
|
|
7555
7604
|
/** The first process setting whose value spans lines — a `Gtd-Var:` trailer cannot carry it. */
|
|
7556
7605
|
const multilineSetting = (vars) => Object.keys(vars).sort().find((name) => /[\r\n]/.test(vars[name]));
|
|
7557
7606
|
const SETTING_NAME = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
@@ -8480,7 +8529,7 @@ const planStart = (current, actor, start) => gen(function* () {
|
|
|
8480
8529
|
const overrideRefusal = yield* checkOverrides(commandLabel, varOverrides, Object.keys(workflow.vars), Object.keys(workflow.env));
|
|
8481
8530
|
if (overrideRefusal !== void 0) return overrideRefusal;
|
|
8482
8531
|
const baseOf = workflow.def.base;
|
|
8483
|
-
const vars =
|
|
8532
|
+
const vars = resolveStartVars(workflow.vars, yield* StartFlags, varOverrides, (yield* Host).env);
|
|
8484
8533
|
const multiline = multilineSetting(vars);
|
|
8485
8534
|
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`);
|
|
8486
8535
|
const template = yield* try_({
|
|
@@ -8639,6 +8688,7 @@ const buildRun = (history, location, closingHash) => {
|
|
|
8639
8688
|
trace: processCommits.map((c, i) => traceEntryOf(c.hash, parsed[i])),
|
|
8640
8689
|
costEntries: parsed.flatMap(costEntriesOf),
|
|
8641
8690
|
judgeVerdicts: parsed.flatMap((m) => m.judge),
|
|
8691
|
+
marks: parsed.flatMap((m) => m.marks),
|
|
8642
8692
|
approvals: processCommits.flatMap((c, i) => approvalEntryOf(c.hash, parsed[i])),
|
|
8643
8693
|
pinnedVars: { ...first?.vars },
|
|
8644
8694
|
pinnedConfig: pinnedConfigOf(history.slice(Math.max(location.baseIndex, 0))),
|
|
@@ -8828,9 +8878,9 @@ const pendingTree = (workspace, entries, rewrite) => {
|
|
|
8828
8878
|
* are always live. `startOverrides` are `--var` values, passed only while a
|
|
8829
8879
|
* process is being started.
|
|
8830
8880
|
*/
|
|
8831
|
-
const settingsFor = (workflow, pinnedVars, hostEnv, startOverrides = {}) => ({
|
|
8881
|
+
const settingsFor = (workflow, pinnedVars, hostEnv, startOverrides = {}, startFlags) => ({
|
|
8832
8882
|
vars: {
|
|
8833
|
-
...
|
|
8883
|
+
...resolveStartVars(workflow.vars, startFlags, startOverrides, hostEnv),
|
|
8834
8884
|
...pinnedVars
|
|
8835
8885
|
},
|
|
8836
8886
|
env: resolveVars(workflow.env, {}, hostEnv)
|
|
@@ -9004,7 +9054,8 @@ const costByModel = (entries) => {
|
|
|
9004
9054
|
const setupFor = (config, run, workspace, host, startOverrides = {}) => gen(function* () {
|
|
9005
9055
|
const resolved = yield* workflowOf(config, run);
|
|
9006
9056
|
const def = resolved.def;
|
|
9007
|
-
const
|
|
9057
|
+
const startFlags = run.trace.length === 0 ? yield* StartFlags : {};
|
|
9058
|
+
const { vars, env } = settingsFor(resolved, run.pinnedVars, host.env, startOverrides, startFlags);
|
|
9008
9059
|
const skills = yield* resolveScopeSkills(def, vars);
|
|
9009
9060
|
const access = yield* resolveScopeAccess(def, skills, vars);
|
|
9010
9061
|
const budget = yield* try_({
|
|
@@ -9114,7 +9165,7 @@ const currentRest = restAt(void 0);
|
|
|
9114
9165
|
*/
|
|
9115
9166
|
const startRefusal = (rest, resolved, startVars) => gen(function* () {
|
|
9116
9167
|
const host = yield* Host;
|
|
9117
|
-
const { vars, env } = settingsFor(resolved, {}, host.env, startVars);
|
|
9168
|
+
const { vars, env } = settingsFor(resolved, {}, host.env, startVars, yield* StartFlags);
|
|
9118
9169
|
const skills = yield* resolveScopeSkills(resolved.def, vars);
|
|
9119
9170
|
const access = yield* resolveScopeAccess(resolved.def, skills, vars);
|
|
9120
9171
|
const workspace = rest.setup.workspace;
|
|
@@ -9239,7 +9290,7 @@ const approvedTrailer = (rest, approver) => isSome(approver) ? { approved: {
|
|
|
9239
9290
|
step: rest.step.id,
|
|
9240
9291
|
...approver.value
|
|
9241
9292
|
} } : {};
|
|
9242
|
-
const landingTo = (rest, to, branch, approver) => {
|
|
9293
|
+
const landingTo = (rest, to, branch, approver, marks) => {
|
|
9243
9294
|
if (isSettledNoop(rest, to)) return {
|
|
9244
9295
|
kind: "noop",
|
|
9245
9296
|
settled: true
|
|
@@ -9257,17 +9308,20 @@ const landingTo = (rest, to, branch, approver) => {
|
|
|
9257
9308
|
from: rest.state,
|
|
9258
9309
|
to,
|
|
9259
9310
|
step: rest.step.id,
|
|
9311
|
+
...marks.length > 0 && { marks },
|
|
9260
9312
|
...approvedTrailer(rest, approver),
|
|
9261
|
-
...starts
|
|
9262
|
-
vars: rest.vars,
|
|
9263
|
-
config: rest.config
|
|
9264
|
-
} : {},
|
|
9313
|
+
...startTrailers(rest, starts),
|
|
9265
9314
|
...finishes ? closingTrailers(branch) : {}
|
|
9266
9315
|
},
|
|
9267
9316
|
...finishes ? { keepHistory: processHistoryRef(rest.run.startHash) } : {},
|
|
9268
9317
|
...keptTurn(rest)
|
|
9269
9318
|
};
|
|
9270
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 : [];
|
|
9271
9325
|
/**
|
|
9272
9326
|
* What landing the pending turn at `rest` does — a pure decision from a
|
|
9273
9327
|
* replay with the working tree as the pending turn. The target step is
|
|
@@ -9291,7 +9345,7 @@ const decideLanding = (rest, verdicts, branch, approver) => gen(function* () {
|
|
|
9291
9345
|
}));
|
|
9292
9346
|
const to = yield* landingTarget(outcome, rest);
|
|
9293
9347
|
if (typeof to !== "string") return to;
|
|
9294
|
-
return landingTo(rest, to, branch, approver);
|
|
9348
|
+
return landingTo(rest, to, branch, approver, outcomeMarks(outcome));
|
|
9295
9349
|
});
|
|
9296
9350
|
/** Where landing the pending turn would leave the process, or `undefined` when it would land nothing. */
|
|
9297
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)));
|
|
@@ -9328,7 +9382,8 @@ const summaryFor = (run) => gen(function* () {
|
|
|
9328
9382
|
})),
|
|
9329
9383
|
processCost: run.costEntries.reduce((sum, entry) => sum + entry.cost, 0),
|
|
9330
9384
|
processCostByModel: costByModel(run.costEntries),
|
|
9331
|
-
|
|
9385
|
+
marks: run.marks,
|
|
9386
|
+
...settingsFor(resolved, run.pinnedVars, host.env, {}, {})
|
|
9332
9387
|
});
|
|
9333
9388
|
});
|
|
9334
9389
|
/** 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. */
|
|
@@ -10800,6 +10855,12 @@ const FLAGS = [
|
|
|
10800
10855
|
if (/[\r\n]/.test(value)) return left(`gtd: --var ${name} must be a single-line value`);
|
|
10801
10856
|
if (seen.has(name)) return left(`gtd: --var ${name} specified more than once`);
|
|
10802
10857
|
seen.add(name);
|
|
10858
|
+
if (name.startsWith("flag_")) {
|
|
10859
|
+
const pinned = flagSettingValue(value);
|
|
10860
|
+
if (pinned === void 0) return left(`gtd: --var ${name} must be 1, 0, true or false`);
|
|
10861
|
+
vars[name] = pinned;
|
|
10862
|
+
continue;
|
|
10863
|
+
}
|
|
10803
10864
|
vars[name] = value;
|
|
10804
10865
|
}
|
|
10805
10866
|
return right(vars);
|
|
@@ -11624,7 +11685,7 @@ const runCli = (argv, io) => {
|
|
|
11624
11685
|
return flatMap$4(Host, ({ env }) => try_({
|
|
11625
11686
|
try: () => resolveFlags(env, io.flags),
|
|
11626
11687
|
catch: (e) => new GtdError(e.message)
|
|
11627
|
-
})).pipe(flatMap$4(() => runCommand$1(plan.command, plan.json, out)), map$4((code) => {
|
|
11688
|
+
})).pipe(flatMap$4((flags) => runCommand$1(plan.command, plan.json, out).pipe(provideService(StartFlags, flagSettings(flags)))), map$4((code) => {
|
|
11628
11689
|
out.flush();
|
|
11629
11690
|
io.exit(code);
|
|
11630
11691
|
}), provide$2(io.layers(plan.verbose)), sandbox, catchAll$1((cause) => isInterruptedOnly(cause) ? failCause$4(cause) : report(io, plan.json.kind !== "off")(cause)));
|
|
@@ -13376,7 +13437,8 @@ const testRepo = (options) => {
|
|
|
13376
13437
|
}
|
|
13377
13438
|
throw new Error(`the process did not rest within ${maxBeats} beats`);
|
|
13378
13439
|
},
|
|
13379
|
-
subjects: () => repo.commitHistory().map(({ message }) => message.split("\n")[0] ?? "")
|
|
13440
|
+
subjects: () => repo.commitHistory().map(({ message }) => message.split("\n")[0] ?? ""),
|
|
13441
|
+
messages: () => repo.commitHistory().map(({ message }) => message)
|
|
13380
13442
|
};
|
|
13381
13443
|
return self;
|
|
13382
13444
|
};
|
|
@@ -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, numeric, quote, read, refuse, removeScript, requireAnswers, requireProgress, requireReplies, requireRevert, requireThreadsClosed, restoreScript, revertScript, run, scope, 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 { A as architectureAuthorPrompt, B as designGateAnswerMessage, C as fixRisks, D as reviewing, F as buildReviewCollectingPrompt, H as designTriagePrompt, I as buildReviewFixNitsPrompt, J as
|
|
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",
|
|
@@ -345,66 +395,6 @@ const review = (base, escalations = { rounds: 0 }) => {
|
|
|
345
395
|
/** The build tail: fix (when entered red), keep green unless `green`, the quality lap, then human review since `base`. */
|
|
346
396
|
const buildTail = (fixFirst, base, green = false) => tail(fixFirst, (escalations) => review(base, escalations), green);
|
|
347
397
|
//#endregion
|
|
348
|
-
//#region src/workflows/feature/feature.ts
|
|
349
|
-
/** Revert the sketch that started the process out of the working tree; its intent survives in history. */
|
|
350
|
-
const unwind = () => {
|
|
351
|
-
const commit = head();
|
|
352
|
-
return run("unwind", revertScript(commit, FEEDBACK, unwindFailure(commit)), { label: "Unwinding your input" });
|
|
353
|
-
};
|
|
354
|
-
/**
|
|
355
|
-
* Undo the human's review-round code edits. A path changed since the human
|
|
356
|
-
* left it is not overwritten; requireRevert names it instead.
|
|
357
|
-
*/
|
|
358
|
-
const reUnwind = async (feedback) => {
|
|
359
|
-
const { base, restoreFrom, edited } = feedback;
|
|
360
|
-
await run("re-unwind", restoreScript(base, {
|
|
361
|
-
restore: edited.filter((c) => c.status !== "added").map((c) => c.path),
|
|
362
|
-
remove: edited.filter((c) => c.status === "added").map((c) => c.path)
|
|
363
|
-
}, restoreFrom), {
|
|
364
|
-
label: "Re-unwinding your review edit",
|
|
365
|
-
file: REVIEW,
|
|
366
|
-
base
|
|
367
|
-
});
|
|
368
|
-
requireRevert(edited, base);
|
|
369
|
-
};
|
|
370
|
-
/** Plan, build and review until a review round signs off; feedback re-plans from scratch. */
|
|
371
|
-
const planAndBuild = async (firstBase) => {
|
|
372
|
-
let base = firstBase;
|
|
373
|
-
for (;;) {
|
|
374
|
-
await design(base);
|
|
375
|
-
await architecture();
|
|
376
|
-
await packages();
|
|
377
|
-
const outcome = await buildTail(false, base, true);
|
|
378
|
-
if (outcome.verdict === "signoff") return;
|
|
379
|
-
await reUnwind(outcome);
|
|
380
|
-
base = outcome.base;
|
|
381
|
-
}
|
|
382
|
-
};
|
|
383
|
-
/** After a tail that skipped planning: stop on sign-off, re-plan on feedback. */
|
|
384
|
-
const afterTail = async (outcome) => {
|
|
385
|
-
if (outcome.verdict === "signoff") return;
|
|
386
|
-
await reUnwind(outcome);
|
|
387
|
-
await planAndBuild(outcome.base);
|
|
388
|
-
};
|
|
389
|
-
/** An ordinary start: wait at `idle`, unwind the sketch, check the baseline, then plan and build. */
|
|
390
|
-
const ordinaryStart = async () => {
|
|
391
|
-
await human("idle", {
|
|
392
|
-
message: idleMessage(),
|
|
393
|
-
label: "Idle",
|
|
394
|
-
file: ".gtd/TODO.md"
|
|
395
|
-
});
|
|
396
|
-
refusePendingHandOff();
|
|
397
|
-
await unwind();
|
|
398
|
-
if (changes(".gtd/FEEDBACK.md").some((c) => c.status !== "deleted")) await human("unwind-failed", {
|
|
399
|
-
message: unwindFailedMessage(),
|
|
400
|
-
label: "Could not unwind your input",
|
|
401
|
-
file: FEEDBACK
|
|
402
|
-
});
|
|
403
|
-
await gate("start-gate", startGateBlockedMessage());
|
|
404
|
-
await planAndBuild(start());
|
|
405
|
-
};
|
|
406
|
-
const feature = shipped(ordinaryStart);
|
|
407
|
-
//#endregion
|
|
408
398
|
//#region src/workflows/feature/refactor.ts
|
|
409
399
|
const REFACTOR_PACKAGE = ".gtd/REFACTOR.md";
|
|
410
400
|
const isCode = (path) => path !== ".gtd" && !path.startsWith(".gtd/");
|
|
@@ -518,7 +508,144 @@ const refactorFirst = async (shape, options = {}) => {
|
|
|
518
508
|
});
|
|
519
509
|
};
|
|
520
510
|
//#endregion
|
|
511
|
+
//#region src/workflows/feature/seam.ts
|
|
512
|
+
/** Naive-pass retries after a `reject`; the next `reject` rests at `seam.steer`. */
|
|
513
|
+
const NAIVE_RETRIES = 2;
|
|
514
|
+
/** A change to an existing file outside the allowlist is a stray even when the task needs it: the judge decides. */
|
|
515
|
+
const judgeSeam = (tip) => judgeDiffShape("judge", {
|
|
516
|
+
changes: tip.changes,
|
|
517
|
+
allow: (vars.seamAllow ?? defaults.seamAllow).split(",").map((glob) => glob.trim()).filter((glob) => glob !== ""),
|
|
518
|
+
exempt: tip.changes.filter((c) => c.status === "added").map((c) => c.path)
|
|
519
|
+
});
|
|
520
|
+
const naivePass = (rejection) => agentWithSkills("naive", naivePassPrompt(rejection), {
|
|
521
|
+
label: rejection === void 0 ? "Writing the naive pass" : "Rewriting the naive pass",
|
|
522
|
+
keep: true,
|
|
523
|
+
model: coder(),
|
|
524
|
+
system: builderSystem()
|
|
525
|
+
});
|
|
526
|
+
const implement = (reference, tier) => scope("implement", async () => {
|
|
527
|
+
await agentWithSkills("building", implementBuildingPrompt(reference, tier), {
|
|
528
|
+
label: "Implementing",
|
|
529
|
+
model: coder(),
|
|
530
|
+
system: builderSystem()
|
|
531
|
+
});
|
|
532
|
+
await healthy(() => agentWithSkills("fix.suite.fixing", implementFixPrompt(), {
|
|
533
|
+
label: "Fixing the check",
|
|
534
|
+
file: FEEDBACK,
|
|
535
|
+
model: coder(),
|
|
536
|
+
system: builderSystem()
|
|
537
|
+
}));
|
|
538
|
+
await run("closing", removeScript([REQUIREMENTS]), { label: "Closing out the implementation" });
|
|
539
|
+
});
|
|
540
|
+
const decide = () => scope("seam", async () => {
|
|
541
|
+
let rejection;
|
|
542
|
+
let retries = 0;
|
|
543
|
+
for (;;) {
|
|
544
|
+
await naivePass(rejection);
|
|
545
|
+
const tip = kept("naive");
|
|
546
|
+
const shape = await judgeSeam(tip);
|
|
547
|
+
if (shape.choice === "land") return { reference: tip.hash };
|
|
548
|
+
if (shape.choice === "refactor-first") {
|
|
549
|
+
await seamGate("approve", shape);
|
|
550
|
+
return {
|
|
551
|
+
reference: tip.hash,
|
|
552
|
+
shape
|
|
553
|
+
};
|
|
554
|
+
}
|
|
555
|
+
rejection = {
|
|
556
|
+
strays: shape.strays,
|
|
557
|
+
p: shape.judgment?.answers.shape?.p ?? 0
|
|
558
|
+
};
|
|
559
|
+
if (retries < NAIVE_RETRIES) {
|
|
560
|
+
retries++;
|
|
561
|
+
continue;
|
|
562
|
+
}
|
|
563
|
+
await human("steer", {
|
|
564
|
+
message: seamSteerMessage(rejection),
|
|
565
|
+
label: "Steer or overrule the rejected naive pass",
|
|
566
|
+
file: REQUIREMENTS,
|
|
567
|
+
acceptClean: true
|
|
568
|
+
});
|
|
569
|
+
if (changes().length === 0) return { reference: tip.hash };
|
|
570
|
+
rejection = void 0;
|
|
571
|
+
retries = 0;
|
|
572
|
+
}
|
|
573
|
+
});
|
|
574
|
+
const seamBuild = async (tier) => {
|
|
575
|
+
const { reference, shape } = await decide();
|
|
576
|
+
if (shape !== void 0 && !await refactorFirst(shape, { reference })) await scope("seam", () => human("by-hand", {
|
|
577
|
+
message: seamByHandMessage(shape.strays, shape.unreadable, reference),
|
|
578
|
+
label: "Reviewing unreadable files by hand",
|
|
579
|
+
acceptClean: true
|
|
580
|
+
}));
|
|
581
|
+
await implement(reference, tier);
|
|
582
|
+
};
|
|
583
|
+
//#endregion
|
|
584
|
+
//#region src/workflows/feature/feature.ts
|
|
585
|
+
/** Revert the sketch that started the process out of the working tree; its intent survives in history. */
|
|
586
|
+
const unwind = () => {
|
|
587
|
+
const commit = head();
|
|
588
|
+
return run("unwind", revertScript(commit, FEEDBACK, unwindFailure(commit)), { label: "Unwinding your input" });
|
|
589
|
+
};
|
|
590
|
+
/**
|
|
591
|
+
* Undo the human's review-round code edits. A path changed since the human
|
|
592
|
+
* left it is not overwritten; requireRevert names it instead.
|
|
593
|
+
*/
|
|
594
|
+
const reUnwind = async (feedback) => {
|
|
595
|
+
const { base, restoreFrom, edited } = feedback;
|
|
596
|
+
await run("re-unwind", restoreScript(base, {
|
|
597
|
+
restore: edited.filter((c) => c.status !== "added").map((c) => c.path),
|
|
598
|
+
remove: edited.filter((c) => c.status === "added").map((c) => c.path)
|
|
599
|
+
}, restoreFrom), {
|
|
600
|
+
label: "Re-unwinding your review edit",
|
|
601
|
+
file: REVIEW,
|
|
602
|
+
base
|
|
603
|
+
});
|
|
604
|
+
requireRevert(edited, base);
|
|
605
|
+
};
|
|
606
|
+
/** Plan, build and review until a review round signs off; feedback re-plans from scratch. */
|
|
607
|
+
const planAndBuild = async (firstBase) => {
|
|
608
|
+
let base = firstBase;
|
|
609
|
+
for (;;) {
|
|
610
|
+
await design(base);
|
|
611
|
+
const tier = riskTierOn() ? recordRisk() : void 0;
|
|
612
|
+
if (vars.flag_naive_pass === "1") await seamBuild(tier);
|
|
613
|
+
else {
|
|
614
|
+
await architecture();
|
|
615
|
+
await packages();
|
|
616
|
+
}
|
|
617
|
+
const outcome = await buildTail(false, base, true);
|
|
618
|
+
if (outcome.verdict === "signoff") return;
|
|
619
|
+
await reUnwind(outcome);
|
|
620
|
+
base = outcome.base;
|
|
621
|
+
}
|
|
622
|
+
};
|
|
623
|
+
/** After a tail that skipped planning: stop on sign-off, re-plan on feedback. */
|
|
624
|
+
const afterTail = async (outcome) => {
|
|
625
|
+
if (outcome.verdict === "signoff") return;
|
|
626
|
+
await reUnwind(outcome);
|
|
627
|
+
await planAndBuild(outcome.base);
|
|
628
|
+
};
|
|
629
|
+
/** An ordinary start: wait at `idle`, unwind the sketch, check the baseline, then plan and build. */
|
|
630
|
+
const ordinaryStart = async () => {
|
|
631
|
+
await human("idle", {
|
|
632
|
+
message: idleMessage(),
|
|
633
|
+
label: "Idle",
|
|
634
|
+
file: ".gtd/TODO.md"
|
|
635
|
+
});
|
|
636
|
+
refusePendingHandOff();
|
|
637
|
+
await unwind();
|
|
638
|
+
if (changes(".gtd/FEEDBACK.md").some((c) => c.status !== "deleted")) await human("unwind-failed", {
|
|
639
|
+
message: unwindFailedMessage(),
|
|
640
|
+
label: "Could not unwind your input",
|
|
641
|
+
file: FEEDBACK
|
|
642
|
+
});
|
|
643
|
+
await gate("start-gate", startGateBlockedMessage());
|
|
644
|
+
await planAndBuild(start());
|
|
645
|
+
};
|
|
646
|
+
const feature = shipped(ordinaryStart);
|
|
647
|
+
//#endregion
|
|
521
648
|
//#region src/workflows/feature/index.ts
|
|
522
649
|
var feature_default = feature;
|
|
523
650
|
//#endregion
|
|
524
|
-
export { REFACTOR_PACKAGE, access, afterTail, answerProductQuestions, answerReviewQuestions, answerTechnicalQuestions, architecture, author, build, buildTail, collecting, declaredTests, decompose, feature_default as default, defaults, deferNotes, design, envDefaults, feature, fixNits, fixSuite, nextPackage, ordinaryStart, packageItem, packages, planAndBuild, reUnwind, refactorFirst, requireDeclaredTests, requireNoNewFlags, requireWithin, skills, steering, summary, triage, unwind };
|
|
651
|
+
export { REFACTOR_PACKAGE, access, afterTail, answerProductQuestions, answerReviewQuestions, answerTechnicalQuestions, architecture, author, build, buildTail, collecting, declaredTests, decompose, feature_default as default, defaults, deferNotes, design, envDefaults, feature, fixNits, fixSuite, implement, judgeSeam, naivePass, nextPackage, ordinaryStart, packageItem, packages, planAndBuild, reUnwind, refactorFirst, requireDeclaredTests, requireNoNewFlags, requireWithin, seamBuild, skills, steering, summary, triage, unwind };
|
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 {
|
|
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.`;
|