@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.
Files changed (54) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/README.md +17 -0
  3. package/dist/chunks/{Schema-CyImoHTj.mjs → Schema-DO5ttJaI.mjs} +1 -1
  4. package/dist/chunks/{reserved-BPdVZkgv.mjs → reserved-Df8GeSHw.mjs} +97 -8
  5. package/dist/chunks/{tail-iLAMoigl.mjs → tail-DmIfEAI4.mjs} +1 -1
  6. package/dist/flows.mjs +20 -2
  7. package/dist/gtd.bundle.mjs +97 -17
  8. package/dist/testkit.mjs +80 -18
  9. package/dist/workflows/cleanup.mjs +2 -2
  10. package/dist/workflows/feature.mjs +193 -66
  11. package/dist/workflows/fix.mjs +2 -2
  12. package/dist/workflows/review.mjs +2 -2
  13. package/dist/workflows/rollout.mjs +1 -1
  14. package/dist/workflows.mjs +447 -114
  15. package/package.json +1 -1
  16. package/src/cli/Cli.test.ts +49 -1
  17. package/src/cli/Cli.ts +20 -3
  18. package/src/edge/Edge.test.ts +156 -1
  19. package/src/edge/Edge.ts +30 -7
  20. package/src/flags/index.test.ts +27 -1
  21. package/src/flags/index.ts +15 -3
  22. package/src/flows/helpers.ts +7 -3
  23. package/src/flows/runtime.test.ts +42 -0
  24. package/src/flows/runtime.ts +36 -0
  25. package/src/replay/Replay.test.ts +43 -0
  26. package/src/replay/Replay.ts +10 -2
  27. package/src/replay/Trailers.test.ts +54 -0
  28. package/src/replay/Trailers.ts +15 -1
  29. package/src/replay/judgeDiffShape.test.ts +43 -0
  30. package/src/step/planStart.ts +3 -2
  31. package/src/testkit/TestRepo.ts +3 -0
  32. package/src/testkit/steps.ts +18 -0
  33. package/src/workflow/index.ts +8 -1
  34. package/src/workflow/vars.test.ts +23 -1
  35. package/src/workflow/vars.ts +24 -0
  36. package/src/workflows/feature/feature.ts +11 -2
  37. package/src/workflows/feature/index.ts +1 -0
  38. package/src/workflows/feature/risk-tier.feature +301 -0
  39. package/src/workflows/feature/seam-pin.feature +52 -0
  40. package/src/workflows/feature/seam.feature +387 -0
  41. package/src/workflows/feature/seam.test.ts +88 -0
  42. package/src/workflows/feature/seam.ts +111 -0
  43. package/src/workflows/feature/steps.ts +3 -2
  44. package/src/workflows/feature/tier.test.ts +164 -0
  45. package/src/workflows/feature/tier.ts +74 -0
  46. package/src/workflows/index.ts +1 -0
  47. package/src/workflows/shared/options.test.ts +17 -1
  48. package/src/workflows/shared/options.ts +1 -0
  49. package/src/workflows/shared/skills.test.ts +20 -2
  50. package/src/workflows/shared/skills.ts +6 -0
  51. package/src/workflows/shared/text.test.ts +188 -0
  52. package/src/workflows/shared/text.ts +139 -5
  53. package/src/workflows/shared/vars.ts +6 -0
  54. 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 format, $n as map$5, $r as make$19, $t as provide$1, A as catchIf, Ai as get$5, An as scopeMake, Ar as flatMap$3, At as suspend$5, B as flatMap$4, Bi as isNone, 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 splitAt, Fn as join$3, Fr as map$3, Ft as tryMap, G as forkIn, Gi as some, Gn as TypeId$10, Gr as whenEffect, Gt as zipLeft, H as fnUntraced, Hi as map$8, Hn as modify$1, Hr as uninterruptible, Ht as when, I as exit, Ii as filter$1, In as sequential, Ir as onInterrupt, It as tryPromise, J as ignore, Ji as match$2, Jn as all, Jr as DeferredTypeId$1, Jt as spaced$1, K as forkScoped, Ki as isLeft, Kn as Class, Kr as withFiberRuntime, Kt as zipRight$3, L as fail$5, Li as flatMap$5, Ln as bind, Lr as scopeClose, Lt as try_, M as die$2, Mi as omit, Mn as _await$2, Mr as interruptAsFiber, Mt as tap, N as dieMessage, Ni as empty$5, Nn as inheritAll$1, Nr as interruptFiber, Nt as tapErrorCause, O as catchAll$1, Oi as Tag, On as ensuring$3, Or as fiberId, Ot as serviceOption, P as either, Pi as fromIterable, Pn as interruptAllAs$1, Pr as interruptible$1, Pt as timeout, Q as isEffect, Qi as NodeInspectSymbol, Qn as isFailure, Qr as ids, Qt as mergeAll$2, R as failCause$4, Ri as fromNullable, 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 match$4, Un as set$1, Ur as uninterruptibleMask$1, Ut as withFiberRuntime$1, V as flatten$4, Vi as isSome, Vn as make$16, Vr as tap$1, Vt as unsafeMakeSemaphore$1, W as forkDaemon, Wi as none, Wn as update$1, Wr as void_$2, Wt as zip$1, X as interruptible, Xi as pipeArguments, Xn as fail$4, Xr as combine, Xt as tagged, Y as interrupt$4, Yi as right, Yn as die, Yr as empty$6, Yt as TaggedError$1, Z as intoDeferred, Zi as BaseProto, 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 constUndefined, 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 identity, 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 toJSON, 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 constTrue, ii as has, in as unsafeRunPromiseExit, ir as zip$2, it as match$5, j as catchTag, ji as getOption, jn as scopeTag, jr as interrupt$2, jt as sync$2, k as catchAllCause, ki as add, kn as forEachConcurrentDiscard, kr as fiberRefLocally, kt as succeed$6, l as Struct, la as pipe, 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 hasProperty, ni as make$17, nn as succeed$3, nr as succeed$5, nt as mapBoth$1, o as Never, oa as constVoid, 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 left, qn as spanTag, qr as zipRight$1, qt as zipWith, r as Cause, ra as constFalse, ri as set$2, rn as unsafeFork, rr as void_$3, rt as mapError, s as Number$, sa as dual, si as isFinite, sn as unsafeMakeSemaphore, sr as asVoid, st as matchEffect, t as Array$, ta as equals, ti as get$4, tn as scoped$3, tr as match$3, tt as map$4, u as TaggedError, 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 getOrElse, zn as sleep, zr as suspend$4, zt as uninterruptibleMask } from "./chunks/Schema-CyImoHTj.mjs";
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 = resolveVars(workflow.vars, varOverrides, (yield* Host).env);
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
- ...resolveVars(workflow.vars, startOverrides, hostEnv),
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 { vars, env } = settingsFor(resolved, run.pinnedVars, host.env, startOverrides);
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
- ...settingsFor(resolved, run.pinnedVars, host.env)
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-BPdVZkgv.mjs";
3
- import { i as refusePendingHandOff, r as handOffTail } from "../chunks/tail-iLAMoigl.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-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 packagesItemFixSuitePrompt, K as idleMessage, L as buildReviewTriageMessage, M as architectureGateAnswerMessage, N as buildReviewAnswerQuestionsPrompt, O as agentWithSkills, P as buildReviewAwaitAfterDeferMessage, Q as reviewEditsCapture, R as builderSystem, T as reviewMissing, V as designSystem, X as refactorFixPrompt, Y as refactorBuildingPrompt, Z as reviewEditNotesCapture, _ as REQUIREMENTS, b as coder, c as envDefaults, d as gate, et as reviewNotesCapture, h as FEEDBACK, i as skills, it as unwindFailure, j as architectureDecomposePrompt, k as architectSystem, m as ARCHITECTURE, n as summary, nt as startGateBlockedMessage, o as shipped, p as healthy, q as packagesItemBuildingPrompt, r as access, rt as unwindFailedMessage, s as defaults, t as steering, tt as reviewerSystem, v as REVIEW, w as planner, x as fixCheck, y as awaitReview, z as deferNotesScript } from "../chunks/reserved-BPdVZkgv.mjs";
4
- import { a as tail, i as refusePendingHandOff, n as closeReview } from "../chunks/tail-iLAMoigl.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-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 };
@@ -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-BPdVZkgv.mjs";
4
- import { r as handOffTail, t as REVIEW_ORIG } from "../chunks/tail-iLAMoigl.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-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 { $ as reviewGateBlockedMessage, c as envDefaults, d as gate, i as skills, n as summary, o as shipped, r as access, s as defaults, t as steering } from "../chunks/reserved-BPdVZkgv.mjs";
3
- import { i as refusePendingHandOff, r as handOffTail } from "../chunks/tail-iLAMoigl.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-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-BPdVZkgv.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-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.`;