@pmelab/gtd 2.2.0 → 2.4.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.
@@ -367039,6 +367039,7 @@ var Cwd = class _Cwd extends Context_exports.Tag("Cwd")() {
367039
367039
  var stateTier = {
367040
367040
  decompose: "planning",
367041
367041
  grilling: "planning",
367042
+ architecting: "planning",
367042
367043
  building: "execution",
367043
367044
  fixing: "execution",
367044
367045
  "agentic-review": "planning",
@@ -367051,11 +367052,13 @@ var builtinTierDefault = {
367051
367052
  var DEFAULT_TEST_COMMAND = "npm run test";
367052
367053
  var DEFAULT_AGENTIC_REVIEW = true;
367053
367054
  var DEFAULT_SQUASH = true;
367055
+ var DEFAULT_LEARNING = true;
367054
367056
  var DEFAULT_FIX_ATTEMPT_CAP = 3;
367055
367057
  var DEFAULT_REVIEW_THRESHOLD = 3;
367056
367058
  var ModelStatesSchema = Schema_exports.Struct({
367057
367059
  decompose: Schema_exports.optional(Schema_exports.String),
367058
367060
  grilling: Schema_exports.optional(Schema_exports.String),
367061
+ architecting: Schema_exports.optional(Schema_exports.String),
367059
367062
  building: Schema_exports.optional(Schema_exports.String),
367060
367063
  fixing: Schema_exports.optional(Schema_exports.String),
367061
367064
  "agentic-review": Schema_exports.optional(Schema_exports.String),
@@ -367071,6 +367074,7 @@ var ConfigSchema = Schema_exports.Struct({
367071
367074
  models: Schema_exports.optional(ModelsSchema),
367072
367075
  agenticReview: Schema_exports.optional(Schema_exports.Boolean),
367073
367076
  squash: Schema_exports.optional(Schema_exports.Boolean),
367077
+ learning: Schema_exports.optional(Schema_exports.Boolean),
367074
367078
  fixAttemptCap: Schema_exports.optional(Schema_exports.Int.pipe(Schema_exports.greaterThanOrEqualTo(0))),
367075
367079
  reviewThreshold: Schema_exports.optional(Schema_exports.Int.pipe(Schema_exports.greaterThanOrEqualTo(1)))
367076
367080
  });
@@ -367202,6 +367206,7 @@ var toOperations = (decoded) => {
367202
367206
  resolveModel,
367203
367207
  agenticReview: decoded.agenticReview ?? DEFAULT_AGENTIC_REVIEW,
367204
367208
  squash: decoded.squash ?? DEFAULT_SQUASH,
367209
+ learning: decoded.learning ?? DEFAULT_LEARNING,
367205
367210
  fixAttemptCap: decoded.fixAttemptCap ?? DEFAULT_FIX_ATTEMPT_CAP,
367206
367211
  reviewThreshold: decoded.reviewThreshold ?? DEFAULT_REVIEW_THRESHOLD
367207
367212
  };
@@ -387755,6 +387760,7 @@ var formatFile = (path16) => Effect_exports.gen(function* () {
387755
387760
  // src/Subjects.ts
387756
387761
  var turnSubject = (actor, gate) => `gtd(${actor}): ${gate}`;
387757
387762
  var ROUTING_SUBJECT = {
387763
+ architecting: "gtd: architecting",
387758
387764
  grilled: "gtd: grilled",
387759
387765
  planning: "gtd: planning",
387760
387766
  "tests-green": "gtd: tests green",
@@ -387765,12 +387771,17 @@ var ROUTING_SUBJECT = {
387765
387771
  done: "gtd: done",
387766
387772
  "squash-template": "gtd: squash template",
387767
387773
  "health-check": "gtd: health-check",
387768
- "health-fix": "gtd: health-fix"
387774
+ "health-fix": "gtd: health-fix",
387775
+ "learning-template": "gtd: learning template",
387776
+ "learning-drafted": "gtd: learning drafted",
387777
+ "learning-approved": "gtd: learning approved",
387778
+ "learning-applied": "gtd: learning applied"
387769
387779
  };
387770
387780
  var reviewingSubject = (baseHash) => `gtd: reviewing ${baseHash}`;
387771
387781
  var TURN_RE = /^gtd\((human|agent)\): (.+)$/;
387772
387782
  var TURN_GATES = /* @__PURE__ */ new Set([
387773
387783
  "grilling",
387784
+ "architecting",
387774
387785
  "grilled",
387775
387786
  "building",
387776
387787
  "fixing",
@@ -387778,7 +387789,9 @@ var TURN_GATES = /* @__PURE__ */ new Set([
387778
387789
  "review",
387779
387790
  "squashing",
387780
387791
  "health-fixing",
387781
- "escalate"
387792
+ "escalate",
387793
+ "learning",
387794
+ "learning-apply"
387782
387795
  ]);
387783
387796
  var ROUTING_SUBJECT_TO_PHASE = new Map(
387784
387797
  Object.entries(ROUTING_SUBJECT).map(([phase, subject]) => [
@@ -387813,19 +387826,34 @@ var parseSubject = (subject) => {
387813
387826
  // src/Events.ts
387814
387827
  var GTD_DIR = ".gtd";
387815
387828
  var TODO_FILE = `${GTD_DIR}/TODO.md`;
387829
+ var ARCHITECTURE_FILE = `${GTD_DIR}/ARCHITECTURE.md`;
387816
387830
  var REVIEW_FILE = `${GTD_DIR}/REVIEW.md`;
387817
387831
  var FEEDBACK_FILE = `${GTD_DIR}/FEEDBACK.md`;
387818
387832
  var ERRORS_FILE = `${GTD_DIR}/ERRORS.md`;
387819
387833
  var HEALTH_FILE = `${GTD_DIR}/HEALTH.md`;
387820
387834
  var SQUASH_MSG_FILE = `${GTD_DIR}/SQUASH_MSG.md`;
387835
+ var LEARNINGS_FILE = `${GTD_DIR}/LEARNINGS.md`;
387821
387836
  var LEGACY_FEEDBACK_FILE = "FEEDBACK.md";
387822
387837
  var emptyFailureSentinel = (command, exitCode3) => `Test command \`${command}\` failed with exit code ${exitCode3} and produced no output.`;
387823
387838
  var DONE_SUBJECT = "gtd: done";
387824
387839
  var WORKFLOW_FILE_EXCLUDES = [GTD_DIR];
387825
387840
  var GATE_OWN_STEERING_FILE = {
387826
387841
  grilling: TODO_FILE,
387842
+ architecting: ARCHITECTURE_FILE,
387827
387843
  review: REVIEW_FILE
387828
387844
  };
387845
+ var SQUASH_OR_LEARNING_ROUTING_PHASES = /* @__PURE__ */ new Set([
387846
+ "squash-template",
387847
+ "learning-template",
387848
+ "learning-drafted",
387849
+ "learning-approved",
387850
+ "learning-applied"
387851
+ ]);
387852
+ var SQUASH_OR_LEARNING_TURN_GATES = /* @__PURE__ */ new Set([
387853
+ "squashing",
387854
+ "learning",
387855
+ "learning-apply"
387856
+ ]);
387829
387857
  var turnDiffExcludes = (gate) => {
387830
387858
  const ownFile = GATE_OWN_STEERING_FILE[gate];
387831
387859
  return ownFile ? [...WORKFLOW_FILE_EXCLUDES, `!${ownFile}`] : WORKFLOW_FILE_EXCLUDES;
@@ -388015,6 +388043,7 @@ var gatherEvents = (invoker) => (
388015
388043
  const gtdModified = entries2.some((e7) => isPackagePath(e7.path));
388016
388044
  const codeDirty = entries2.some((e7) => !isGtdPath(e7.path));
388017
388045
  const todoExists = yield* fs9.exists(resolve5(TODO_FILE));
388046
+ const architectureExists = yield* fs9.exists(resolve5(ARCHITECTURE_FILE));
388018
388047
  const reviewPresent = yield* fs9.exists(resolve5(REVIEW_FILE));
388019
388048
  const feedbackPresent = yield* fs9.exists(resolve5(FEEDBACK_FILE));
388020
388049
  const errorsPresent = yield* fs9.exists(resolve5(ERRORS_FILE));
@@ -388029,6 +388058,7 @@ var gatherEvents = (invoker) => (
388029
388058
  const reviewCommitted = reviewTrackedAtHead && workingTreeClean;
388030
388059
  const reviewDirty = reviewTrackedAtHead && !workingTreeClean;
388031
388060
  const todoCommitted = todoExists && !isUncommitted(TODO_FILE);
388061
+ const architectureCommitted = architectureExists && !isUncommitted(ARCHITECTURE_FILE);
388032
388062
  const pendingErrorsDeletion = entries2.some(
388033
388063
  (e7) => e7.path === ERRORS_FILE && e7.status.includes("D")
388034
388064
  );
@@ -388063,7 +388093,7 @@ var gatherEvents = (invoker) => (
388063
388093
  }
388064
388094
  const isGrillingTurn = (message) => {
388065
388095
  const parsed = parseSubject(subjectOf(message));
388066
- return parsed.kind === "turn" && parsed.gate === "grilling";
388096
+ return parsed.kind === "turn" && (parsed.gate === "grilling" || parsed.gate === "architecting");
388067
388097
  };
388068
388098
  const firstGrilling = currentCycle.find((c7) => isGrillingTurn(c7.message));
388069
388099
  const lastAwaitingReview = (() => {
@@ -388096,8 +388126,8 @@ var gatherEvents = (invoker) => (
388096
388126
  let squashBase;
388097
388127
  let squashDiff;
388098
388128
  const headParsedForSquash = parseSubject(lastCommitSubject);
388099
- const inSquashChain = lastCommitSubject === DONE_SUBJECT || headParsedForSquash.kind === "routing" && headParsedForSquash.phase === "squash-template" || headParsedForSquash.kind === "turn" && headParsedForSquash.actor === "agent" && headParsedForSquash.gate === "squashing";
388100
- if (hasCommits && inSquashChain && config2.squash) {
388129
+ const inSquashOrLearningChain = lastCommitSubject === DONE_SUBJECT || headParsedForSquash.kind === "routing" && SQUASH_OR_LEARNING_ROUTING_PHASES.has(headParsedForSquash.phase) || headParsedForSquash.kind === "turn" && SQUASH_OR_LEARNING_TURN_GATES.has(headParsedForSquash.gate);
388130
+ if (hasCommits && inSquashOrLearningChain && (config2.squash || config2.learning)) {
388101
388131
  const squashHistory = history;
388102
388132
  let lastDoneIdxForSquash = -1;
388103
388133
  let prevDoneIdx = -1;
@@ -388111,7 +388141,7 @@ var gatherEvents = (invoker) => (
388111
388141
  const squashCycle = squashHistory.slice(prevDoneIdx + 1, lastDoneIdxForSquash + 1);
388112
388142
  const isGrillingTurnSubject = (subject) => {
388113
388143
  const parsed = parseSubject(subject);
388114
- return parsed.kind === "turn" && parsed.gate === "grilling";
388144
+ return parsed.kind === "turn" && (parsed.gate === "grilling" || parsed.gate === "architecting");
388115
388145
  };
388116
388146
  const isReviewingAnchor = (subject) => {
388117
388147
  const parsed = parseSubject(subject);
@@ -388143,11 +388173,13 @@ var gatherEvents = (invoker) => (
388143
388173
  }
388144
388174
  const squashMsgPresent = yield* fs9.exists(resolve5(SQUASH_MSG_FILE));
388145
388175
  const squashMsgIsTemplate = squashMsgPresent && (yield* fs9.readFileString(resolve5(SQUASH_MSG_FILE))).trim() === SQUASH_TEMPLATE.trim();
388176
+ const learningMsgPresent = yield* fs9.exists(resolve5(LEARNINGS_FILE));
388177
+ const learningMsgIsTemplate = learningMsgPresent && (yield* fs9.readFileString(resolve5(LEARNINGS_FILE))).trim() === LEARNING_TEMPLATE.trim();
388146
388178
  const healthPresent = yield* fs9.exists(resolve5(HEALTH_FILE));
388147
388179
  const healthContent = healthPresent ? yield* fs9.readFileString(resolve5(HEALTH_FILE)) : "";
388148
388180
  const healthCommitted = healthPresent && !isUncommitted(HEALTH_FILE);
388149
388181
  let healthFixBase;
388150
- if (config2.squash) {
388182
+ if (config2.squash || config2.learning) {
388151
388183
  let firstHealthCheckHash;
388152
388184
  let healthCheckCount = 0;
388153
388185
  for (const commit3 of commitEvents) {
@@ -388189,6 +388221,8 @@ var gatherEvents = (invoker) => (
388189
388221
  ...headTurnReviewSubstantive !== void 0 ? { headTurnReviewSubstantive } : {},
388190
388222
  todoExists,
388191
388223
  todoCommitted,
388224
+ architectureExists,
388225
+ architectureCommitted,
388192
388226
  packagesPresent: packages.length > 0,
388193
388227
  reviewPresent,
388194
388228
  feedbackPresent,
@@ -388221,7 +388255,10 @@ var gatherEvents = (invoker) => (
388221
388255
  healthPresent,
388222
388256
  healthContent,
388223
388257
  healthCommitted,
388224
- ...healthFixBase !== void 0 ? { healthFixBase } : {}
388258
+ ...healthFixBase !== void 0 ? { healthFixBase } : {},
388259
+ learningEnabled: config2.learning,
388260
+ learningMsgPresent,
388261
+ learningMsgIsTemplate
388225
388262
  };
388226
388263
  const resolveEvent = { type: "RESOLVE", payload };
388227
388264
  return [...commitEvents, resolveEvent];
@@ -388245,6 +388282,16 @@ var SQUASH_TEMPLATE = [
388245
388282
  "Longer description of the change, if needed.",
388246
388283
  ""
388247
388284
  ].join("\n");
388285
+ var ARCHITECTURE_SEED_BANNER = "<!-- gtd: seeded from the converged product plan (.gtd/TODO.md); decide the technical/architectural questions below. -->\n\n";
388286
+ var LEARNING_TEMPLATE = [
388287
+ "<!-- gtd: replace this file's content with the actual distilled learnings for this cycle. -->",
388288
+ "<!-- Keep only durable, generalizable lessons \u2014 delete anything that's a one-off detail. -->",
388289
+ "",
388290
+ "## Learnings",
388291
+ "",
388292
+ "- ...",
388293
+ ""
388294
+ ].join("\n");
388248
388295
  var perform = (action) => (
388249
388296
  // fallow-ignore-next-line complexity
388250
388297
  Effect_exports.gen(function* () {
@@ -388254,22 +388301,38 @@ var perform = (action) => (
388254
388301
  const resolve5 = (p4) => join10(root2, p4);
388255
388302
  const ensureGtdDir = fs9.makeDirectory(resolve5(GTD_DIR), { recursive: true }).pipe(Effect_exports.catchAll(() => Effect_exports.void));
388256
388303
  switch (action.kind) {
388257
- // Capture a human/agent turn: format the pending TODO.md (best-effort),
388258
- // then commit-all under `gtd(<actor>): <gate>` (--allow-empty).
388304
+ // Capture a human/agent turn: format the pending TODO.md/ARCHITECTURE.md
388305
+ // (best-effort), then commit-all under `gtd(<actor>): <gate>` (--allow-empty).
388259
388306
  case "captureTurn": {
388260
388307
  const todoExists = yield* fs9.exists(resolve5(TODO_FILE));
388261
388308
  if (todoExists) {
388262
388309
  yield* formatFile(resolve5(TODO_FILE)).pipe(Effect_exports.catchAll(() => Effect_exports.void));
388263
388310
  }
388311
+ const architectureExists = yield* fs9.exists(resolve5(ARCHITECTURE_FILE));
388312
+ if (architectureExists) {
388313
+ yield* formatFile(resolve5(ARCHITECTURE_FILE)).pipe(Effect_exports.catchAll(() => Effect_exports.void));
388314
+ }
388264
388315
  yield* git.commitAllWithPrefix(turnSubject(action.actor, action.gate));
388265
388316
  return { stop: false };
388266
388317
  }
388267
388318
  // Routing bookkeeping: delete the flagged files FIRST so their removal
388268
388319
  // lands in this same commit, then commit-all under `subject`.
388320
+ // `seedArchitectureFromTodo` instead reads TODO.md, writes it (with a
388321
+ // scaffold banner) as ARCHITECTURE.md, and deletes TODO.md — the
388322
+ // grilling→architecting hand-off, in this same commit.
388269
388323
  case "commitRouting": {
388270
- if (action.removeTodo === true) {
388324
+ if (action.seedArchitectureFromTodo === true) {
388325
+ const todoContent = yield* fs9.readFileString(resolve5(TODO_FILE)).pipe(Effect_exports.catchAll(() => Effect_exports.succeed("")));
388326
+ yield* ensureGtdDir;
388327
+ yield* fs9.writeFileString(
388328
+ resolve5(ARCHITECTURE_FILE),
388329
+ ARCHITECTURE_SEED_BANNER + todoContent
388330
+ );
388271
388331
  yield* fs9.remove(resolve5(TODO_FILE)).pipe(Effect_exports.catchAll(() => Effect_exports.void));
388272
388332
  }
388333
+ if (action.removeArchitecture === true) {
388334
+ yield* fs9.remove(resolve5(ARCHITECTURE_FILE)).pipe(Effect_exports.catchAll(() => Effect_exports.void));
388335
+ }
388273
388336
  if (action.removeReview === true) {
388274
388337
  yield* fs9.remove(resolve5(REVIEW_FILE)).pipe(Effect_exports.catchAll(() => Effect_exports.void));
388275
388338
  }
@@ -388279,6 +388342,9 @@ var perform = (action) => (
388279
388342
  if (action.removeHealth === true) {
388280
388343
  yield* fs9.remove(resolve5(HEALTH_FILE)).pipe(Effect_exports.catchAll(() => Effect_exports.void));
388281
388344
  }
388345
+ if (action.removeLearning === true) {
388346
+ yield* fs9.remove(resolve5(LEARNINGS_FILE)).pipe(Effect_exports.catchAll(() => Effect_exports.void));
388347
+ }
388282
388348
  yield* git.commitAllWithPrefix(action.subject);
388283
388349
  return { stop: false };
388284
388350
  }
@@ -388326,6 +388392,14 @@ var perform = (action) => (
388326
388392
  yield* git.commitAllWithPrefix("gtd: squash template");
388327
388393
  return { stop: false };
388328
388394
  }
388395
+ // Write the LEARNINGS.md template (a durable-lessons skeleton) and
388396
+ // commit routing `gtd: learning template`.
388397
+ case "writeLearningTemplate": {
388398
+ yield* ensureGtdDir;
388399
+ yield* fs9.writeFileString(resolve5(LEARNINGS_FILE), LEARNING_TEMPLATE);
388400
+ yield* git.commitAllWithPrefix("gtd: learning template");
388401
+ return { stop: false };
388402
+ }
388329
388403
  // Squash: read SQUASH_MSG.md content (the real message authored by the
388330
388404
  // squashing turn), rm it, soft-reset to squashBase, commit-all under the
388331
388405
  // file's content as the message.
@@ -388337,10 +388411,11 @@ var perform = (action) => (
388337
388411
  return { stop: false };
388338
388412
  }
388339
388413
  // Health check: run tests on an idle/clean tree.
388340
- // Green, no squash-after chain queued → stop immediately, no commit/write.
388341
- // Green, `squashAfterGreen` → commit routing `gtd: tests green` (the
388414
+ // Green, no learning/squash-after chain queued → stop immediately, no
388415
+ // commit/write.
388416
+ // Green, `chainAfterGreen` → commit routing `gtd: tests green` (the
388342
388417
  // observable green marker) and continue — the resolver chains
388343
- // `writeSquashTemplate` at that HEAD next.
388418
+ // `writeLearningTemplate` or `writeSquashTemplate` at that HEAD next.
388344
388419
  // Red below cap → write HEALTH.md, commit routing `gtd: health-check` (the
388345
388420
  // always-clean invariant: write-and-commit in the same chain).
388346
388421
  // Red at cap → write ERRORS.md, commit routing `gtd: health-check`.
@@ -388348,7 +388423,7 @@ var perform = (action) => (
388348
388423
  const runner = yield* TestRunner;
388349
388424
  const result = yield* runner.run();
388350
388425
  if (result.exitCode === 0) {
388351
- if (action.squashAfterGreen) {
388426
+ if (action.chainAfterGreen) {
388352
388427
  yield* git.commitAllWithPrefix("gtd: tests green");
388353
388428
  return { stop: false };
388354
388429
  }
@@ -388419,6 +388494,8 @@ var DEFAULT_PAYLOAD = {
388419
388494
  headTurnIsEmpty: false,
388420
388495
  todoExists: false,
388421
388496
  todoCommitted: false,
388497
+ architectureExists: false,
388498
+ architectureCommitted: false,
388422
388499
  packagesPresent: false,
388423
388500
  reviewPresent: false,
388424
388501
  feedbackPresent: false,
@@ -388445,7 +388522,10 @@ var DEFAULT_PAYLOAD = {
388445
388522
  squashMsgIsTemplate: false,
388446
388523
  healthPresent: false,
388447
388524
  healthContent: "",
388448
- healthCommitted: false
388525
+ healthCommitted: false,
388526
+ learningEnabled: false,
388527
+ learningMsgPresent: false,
388528
+ learningMsgIsTemplate: false
388449
388529
  };
388450
388530
  var buildContext = (p4, counters) => ({
388451
388531
  testFixCount: counters.testFixCount,
@@ -388476,9 +388556,38 @@ var healthFileConflictRules = [
388476
388556
  message: ".gtd/HEALTH.md + .gtd/ERRORS.md"
388477
388557
  }
388478
388558
  ];
388559
+ var learningFileConflictRules = [
388560
+ {
388561
+ isViolated: (p4) => p4.learningMsgPresent && p4.packagesPresent,
388562
+ message: ".gtd/LEARNINGS.md + packages"
388563
+ },
388564
+ {
388565
+ isViolated: (p4) => p4.learningMsgPresent && p4.reviewPresent,
388566
+ message: ".gtd/LEARNINGS.md + .gtd/REVIEW.md"
388567
+ },
388568
+ {
388569
+ isViolated: (p4) => p4.learningMsgPresent && p4.feedbackPresent,
388570
+ message: ".gtd/LEARNINGS.md + .gtd/FEEDBACK.md"
388571
+ },
388572
+ {
388573
+ isViolated: (p4) => p4.learningMsgPresent && p4.errorsPresent,
388574
+ message: ".gtd/LEARNINGS.md + .gtd/ERRORS.md"
388575
+ },
388576
+ {
388577
+ isViolated: (p4) => p4.learningMsgPresent && p4.healthPresent,
388578
+ message: ".gtd/LEARNINGS.md + .gtd/HEALTH.md"
388579
+ },
388580
+ {
388581
+ isViolated: (p4) => p4.learningMsgPresent && p4.squashMsgPresent,
388582
+ message: ".gtd/LEARNINGS.md + .gtd/SQUASH_MSG.md"
388583
+ }
388584
+ ];
388479
388585
  var isReviewPackagesConflict = (p4) => p4.reviewPresent && p4.packagesPresent;
388480
388586
  var isReviewCommittedTodoConflict = (p4) => p4.reviewPresent && p4.todoCommitted;
388481
388587
  var isUncommittedReviewWithTodoConflict = (p4) => p4.reviewPresent && !(p4.reviewCommitted || p4.reviewDirty) && p4.todoExists;
388588
+ var isReviewCommittedArchitectureConflict = (p4) => p4.reviewPresent && p4.architectureCommitted;
388589
+ var isUncommittedReviewWithArchitectureConflict = (p4) => p4.reviewPresent && !(p4.reviewCommitted || p4.reviewDirty) && p4.architectureExists;
388590
+ var isTodoArchitectureConflict = (p4) => p4.todoExists && p4.architectureExists;
388482
388591
  var isFeedbackReviewConflict = (p4) => p4.feedbackPresent && p4.reviewPresent;
388483
388592
  var isFeedbackWithoutPackages = (p4) => p4.feedbackPresent && !p4.packagesPresent;
388484
388593
  var isErrorsFeedbackConflict = (p4) => p4.errorsPresent && p4.feedbackPresent;
@@ -388494,18 +388603,68 @@ var reviewAndFeedbackRules = [
388494
388603
  isViolated: isUncommittedReviewWithTodoConflict,
388495
388604
  message: "uncommitted .gtd/REVIEW.md + .gtd/TODO.md"
388496
388605
  },
388606
+ {
388607
+ isViolated: isReviewCommittedArchitectureConflict,
388608
+ message: ".gtd/REVIEW.md + committed .gtd/ARCHITECTURE.md"
388609
+ },
388610
+ {
388611
+ isViolated: isUncommittedReviewWithArchitectureConflict,
388612
+ message: "uncommitted .gtd/REVIEW.md + .gtd/ARCHITECTURE.md"
388613
+ },
388614
+ { isViolated: isTodoArchitectureConflict, message: ".gtd/TODO.md + .gtd/ARCHITECTURE.md" },
388497
388615
  { isViolated: isFeedbackReviewConflict, message: ".gtd/FEEDBACK.md + .gtd/REVIEW.md" },
388498
388616
  { isViolated: isFeedbackWithoutPackages, message: ".gtd/FEEDBACK.md without packages" },
388499
388617
  { isViolated: isErrorsFeedbackConflict, message: ".gtd/ERRORS.md + .gtd/FEEDBACK.md" },
388500
388618
  { isViolated: isErrorsWithoutPackages, message: ".gtd/ERRORS.md without packages" }
388501
388619
  ];
388502
388620
  var assertLegal = (p4) => {
388503
- for (const rule of [...healthFileConflictRules, ...reviewAndFeedbackRules]) {
388621
+ for (const rule of [
388622
+ ...healthFileConflictRules,
388623
+ ...learningFileConflictRules,
388624
+ ...reviewAndFeedbackRules
388625
+ ]) {
388504
388626
  if (rule.isViolated(p4)) {
388505
388627
  throw new GtdStateError("illegal-combination", `illegal combination: ${rule.message}`);
388506
388628
  }
388507
388629
  }
388508
388630
  };
388631
+ var nextAfterReviewOrLearning = (flags, state, learningAlreadyRan) => {
388632
+ if (!learningAlreadyRan && flags.learningEnabled && flags.hasSquashBase) {
388633
+ return { kind: "mid-chain", state, actor: "agent", action: { kind: "writeLearningTemplate" } };
388634
+ }
388635
+ return flags.squashEnabled && flags.hasSquashBase ? { kind: "mid-chain", state, actor: "agent", action: { kind: "writeSquashTemplate" } } : { kind: "rest", state: "idle", actor: "human" };
388636
+ };
388637
+ var classifyLearningTurn = (actor, gate, flags) => {
388638
+ if (actor === "agent" && gate === "learning") {
388639
+ return flags.hasSquashBase && !flags.learningMsgIsTemplate ? {
388640
+ kind: "mid-chain",
388641
+ state: "learning",
388642
+ actor: "agent",
388643
+ action: { kind: "commitRouting", subject: "gtd: learning drafted" }
388644
+ } : { kind: "rest", state: "learning", actor: "agent" };
388645
+ }
388646
+ if (actor === "human" && gate === "learning") {
388647
+ return {
388648
+ kind: "mid-chain",
388649
+ state: "learning",
388650
+ actor: "human",
388651
+ action: { kind: "commitRouting", subject: "gtd: learning approved" }
388652
+ };
388653
+ }
388654
+ if (actor === "agent" && gate === "learning-apply") {
388655
+ return {
388656
+ kind: "mid-chain",
388657
+ state: "learning-apply",
388658
+ actor: "agent",
388659
+ action: {
388660
+ kind: "commitRouting",
388661
+ subject: "gtd: learning applied",
388662
+ removeLearning: true
388663
+ }
388664
+ };
388665
+ }
388666
+ return null;
388667
+ };
388509
388668
  var classifyHead = (subject, flags) => {
388510
388669
  const parsed = parseSubject(subject);
388511
388670
  if (parsed.kind === "turn") {
@@ -388518,15 +388677,30 @@ var classifyHead = (subject, flags) => {
388518
388677
  kind: "mid-chain",
388519
388678
  state: "grilling",
388520
388679
  actor: "agent",
388521
- action: { kind: "commitRouting", subject: "gtd: grilled" }
388680
+ action: {
388681
+ kind: "commitRouting",
388682
+ subject: "gtd: architecting",
388683
+ seedArchitectureFromTodo: true
388684
+ }
388522
388685
  } : { kind: "rest", state: "grilling", actor: "agent" };
388523
388686
  }
388687
+ if (gate === "architecting") {
388688
+ if (actor === "agent") {
388689
+ return flags.headTurnIsEmpty ? { kind: "rest", state: "architecting", actor: "agent" } : { kind: "rest", state: "architecting", actor: "human" };
388690
+ }
388691
+ return flags.headTurnIsEmpty ? {
388692
+ kind: "mid-chain",
388693
+ state: "architecting",
388694
+ actor: "agent",
388695
+ action: { kind: "commitRouting", subject: "gtd: grilled" }
388696
+ } : { kind: "rest", state: "architecting", actor: "agent" };
388697
+ }
388524
388698
  if (actor === "agent" && gate === "grilled") {
388525
388699
  return flags.hasPackages ? {
388526
388700
  kind: "mid-chain",
388527
388701
  state: "grilled",
388528
388702
  actor: "agent",
388529
- action: { kind: "commitRouting", subject: "gtd: planning", removeTodo: true }
388703
+ action: { kind: "commitRouting", subject: "gtd: planning", removeArchitecture: true }
388530
388704
  } : { kind: "rest", state: "grilled", actor: "agent" };
388531
388705
  }
388532
388706
  if (actor === "agent" && gate === "building") {
@@ -388578,6 +388752,8 @@ var classifyHead = (subject, flags) => {
388578
388752
  // squashBase filled in by the caller
388579
388753
  } : { kind: "rest", state: "squashing", actor: "agent" };
388580
388754
  }
388755
+ const learningTurn = classifyLearningTurn(actor, gate, flags);
388756
+ if (learningTurn !== null) return learningTurn;
388581
388757
  if (actor === "agent" && gate === "health-fixing") {
388582
388758
  return {
388583
388759
  kind: "mid-chain",
@@ -388598,6 +388774,8 @@ var classifyHead = (subject, flags) => {
388598
388774
  }
388599
388775
  if (parsed.kind === "routing") {
388600
388776
  switch (parsed.phase) {
388777
+ case "architecting":
388778
+ return { kind: "rest", state: "architecting", actor: "agent" };
388601
388779
  case "grilled":
388602
388780
  return { kind: "rest", state: "grilled", actor: "agent" };
388603
388781
  case "planning":
@@ -388611,12 +388789,7 @@ var classifyHead = (subject, flags) => {
388611
388789
  action: { kind: "closePackage" }
388612
388790
  } : { kind: "rest", state: "agentic-review", actor: "agent" };
388613
388791
  }
388614
- return flags.squashEnabled && flags.squashAfterGreen ? {
388615
- kind: "mid-chain",
388616
- state: "testing",
388617
- actor: "agent",
388618
- action: { kind: "writeSquashTemplate" }
388619
- } : { kind: "rest", state: "idle", actor: "human" };
388792
+ return nextAfterReviewOrLearning(flags, "testing", false);
388620
388793
  case "errors":
388621
388794
  return flags.errorsPresent ? { kind: "rest", state: "escalate", actor: "human" } : { kind: "rest", state: "fixing", actor: "agent" };
388622
388795
  case "package-done":
@@ -388626,16 +388799,19 @@ var classifyHead = (subject, flags) => {
388626
388799
  case "review-feedback":
388627
388800
  return { kind: "rest", state: "grilling", actor: "agent" };
388628
388801
  case "done":
388629
- return flags.squashEnabled && flags.hasSquashBase ? {
388630
- kind: "mid-chain",
388631
- state: "done",
388632
- actor: "agent",
388633
- action: { kind: "writeSquashTemplate" }
388634
- } : { kind: "rest", state: "idle", actor: "human" };
388802
+ return nextAfterReviewOrLearning(flags, "done", false);
388635
388803
  case "squash-template":
388636
388804
  return { kind: "rest", state: "squashing", actor: "agent" };
388637
388805
  case "reviewing":
388638
388806
  return { kind: "rest", state: "review", actor: "agent" };
388807
+ case "learning-template":
388808
+ return { kind: "rest", state: "learning", actor: "agent" };
388809
+ case "learning-drafted":
388810
+ return { kind: "rest", state: "await-learning-review", actor: "human" };
388811
+ case "learning-approved":
388812
+ return { kind: "rest", state: "learning-apply", actor: "agent" };
388813
+ case "learning-applied":
388814
+ return nextAfterReviewOrLearning(flags, "learning-applied", true);
388639
388815
  case "health-check":
388640
388816
  return flags.errorsPresent ? { kind: "rest", state: "escalate", actor: "human" } : { kind: "rest", state: "health-fixing", actor: "agent" };
388641
388817
  case "health-fix":
@@ -388646,6 +388822,7 @@ var classifyHead = (subject, flags) => {
388646
388822
  };
388647
388823
  var STATE_IS_OWN_GATE = /* @__PURE__ */ new Set([
388648
388824
  "grilling",
388825
+ "architecting",
388649
388826
  "grilled",
388650
388827
  "building",
388651
388828
  "fixing",
@@ -388653,18 +388830,25 @@ var STATE_IS_OWN_GATE = /* @__PURE__ */ new Set([
388653
388830
  "review",
388654
388831
  "squashing",
388655
388832
  "health-fixing",
388656
- "escalate"
388833
+ "escalate",
388834
+ "learning",
388835
+ "learning-apply"
388657
388836
  ]);
388658
- var gateForState = (state) => STATE_IS_OWN_GATE.has(state) ? state : "review";
388837
+ var GATE_OVERRIDES = {
388838
+ "await-learning-review": "learning"
388839
+ };
388840
+ var gateForState = (state) => STATE_IS_OWN_GATE.has(state) ? state : GATE_OVERRIDES[state] ?? "review";
388659
388841
  var INERT_EMPTY_AGENT_GATES = /* @__PURE__ */ new Set([
388660
388842
  "grilling",
388843
+ "architecting",
388661
388844
  "grilled",
388662
388845
  "building",
388663
388846
  "fixing",
388664
388847
  "agentic-review",
388665
- "review"
388848
+ "review",
388849
+ "learning-apply"
388666
388850
  ]);
388667
- var isInertEmptyAgentRest = (state, p4) => p4.workingTreeClean && (INERT_EMPTY_AGENT_GATES.has(state) || state === "squashing" && p4.squashMsgIsTemplate);
388851
+ var isInertEmptyAgentRest = (state, p4) => p4.workingTreeClean && (INERT_EMPTY_AGENT_GATES.has(state) || state === "squashing" && p4.squashMsgIsTemplate || state === "learning" && p4.learningMsgIsTemplate);
388668
388852
  var resolveBaseline = (p4, counters, head7, corrupt, lastTurn) => {
388669
388853
  const headIsFixerTurn = (() => {
388670
388854
  const parsed = parseSubject(head7);
@@ -388693,7 +388877,6 @@ var resolveBaseline = (p4, counters, head7, corrupt, lastTurn) => {
388693
388877
  action: { kind: "closePackage" }
388694
388878
  } : { kind: "rest", state: "fixing", actor: "agent" };
388695
388879
  }
388696
- const squashAfterGreen = p4.squashEnabled && counters.healthFixCount > 0 && p4.healthFixBase !== void 0;
388697
388880
  const reviewSubstantive = p4.headTurnReviewSubstantive !== void 0 ? p4.headTurnReviewSubstantive : p4.reviewDirty && !p4.reviewCheckboxOnly;
388698
388881
  const flags = {
388699
388882
  headTurnIsEmpty: p4.headTurnIsEmpty,
@@ -388701,7 +388884,8 @@ var resolveBaseline = (p4, counters, head7, corrupt, lastTurn) => {
388701
388884
  agenticReviewForceApproved: forceApprove,
388702
388885
  squashEnabled: p4.squashEnabled,
388703
388886
  hasSquashBase: p4.squashBase !== void 0,
388704
- squashAfterGreen,
388887
+ learningEnabled: p4.learningEnabled,
388888
+ learningMsgIsTemplate: p4.learningMsgIsTemplate,
388705
388889
  reviewSubstantive,
388706
388890
  // A pending (uncommitted) deletion of ERRORS.md still counts as "ERRORS.md
388707
388891
  // was committed at this HEAD" for classification purposes: `fs.exists`
@@ -388739,7 +388923,7 @@ var resolveBaseline = (p4, counters, head7, corrupt, lastTurn) => {
388739
388923
  kind: "runHealthCheck",
388740
388924
  errorCount: counters.healthFixCount,
388741
388925
  capReached: counters.healthFixCount >= p4.fixAttemptCap,
388742
- squashAfterGreen: classified.action.squashAfterGreen
388926
+ chainAfterGreen: classified.action.chainAfterGreen
388743
388927
  }
388744
388928
  };
388745
388929
  }
@@ -388759,6 +388943,9 @@ var resolveBaseline = (p4, counters, head7, corrupt, lastTurn) => {
388759
388943
  if (p4.todoExists) {
388760
388944
  return { kind: "rest", state: "grilling", actor: "agent" };
388761
388945
  }
388946
+ if (p4.architectureExists) {
388947
+ return { kind: "rest", state: "architecting", actor: "agent" };
388948
+ }
388762
388949
  if (p4.packagesPresent && p4.packages.length > 0 && lastTurn?.actor === "agent" && lastTurn.gate === "building") {
388763
388950
  return {
388764
388951
  kind: "mid-chain",
@@ -388767,7 +388954,7 @@ var resolveBaseline = (p4, counters, head7, corrupt, lastTurn) => {
388767
388954
  action: { kind: "runTest", errorCount: 0, capReached: false }
388768
388955
  };
388769
388956
  }
388770
- if (!p4.packagesPresent && !p4.reviewPresent && !p4.feedbackPresent && !p4.errorsPresent && !p4.healthPresent && !p4.todoExists) {
388957
+ if (!p4.packagesPresent && !p4.reviewPresent && !p4.feedbackPresent && !p4.errorsPresent && !p4.healthPresent && !p4.todoExists && !p4.architectureExists) {
388771
388958
  return resolveIdleOrHealth(p4);
388772
388959
  }
388773
388960
  return corrupt();
@@ -388780,13 +388967,14 @@ var resolveIdleOrHealth = (p4) => {
388780
388967
  var applyTurnTaking = (p4, counters, head7, baseline) => {
388781
388968
  const context10 = buildContext(p4, counters);
388782
388969
  const invoker = p4.invoker;
388783
- const isDirtyBoundaryEntry = invoker === "human" && !p4.workingTreeClean && !p4.todoCommitted && !p4.packagesPresent && !p4.reviewPresent && !p4.feedbackPresent && !p4.errorsPresent && !p4.healthPresent && (parseSubject(head7).kind === "boundary" || head7 === "gtd: done");
388970
+ const isDirtyBoundaryEntry = invoker === "human" && !p4.workingTreeClean && !p4.todoCommitted && !p4.architectureCommitted && !p4.packagesPresent && !p4.reviewPresent && !p4.feedbackPresent && !p4.errorsPresent && !p4.healthPresent && (parseSubject(head7).kind === "boundary" || head7 === "gtd: done");
388784
388971
  if (isDirtyBoundaryEntry) {
388972
+ const entryGate = p4.architectureExists ? "architecting" : "grilling";
388785
388973
  return {
388786
- state: "grilling",
388974
+ state: entryGate,
388787
388975
  actor: "human",
388788
388976
  pending: false,
388789
- edgeAction: { kind: "captureTurn", actor: "human", gate: "grilling" },
388977
+ edgeAction: { kind: "captureTurn", actor: "human", gate: entryGate },
388790
388978
  context: context10
388791
388979
  };
388792
388980
  }
@@ -388808,7 +388996,7 @@ var applyTurnTaking = (p4, counters, head7, baseline) => {
388808
388996
  }
388809
388997
  const healthCheckCapReached = head7 === "gtd: health-check" && counters.healthFixCount >= p4.fixAttemptCap;
388810
388998
  if (head7 === "gtd: health-fix" && baseline.state === "idle" || healthCheckCapReached && baseline.state === "health-fixing") {
388811
- const squashAfterGreenAtHealthFix = p4.squashEnabled && counters.healthFixCount > 0 && p4.healthFixBase !== void 0;
388999
+ const chainAfterGreenAtHealthFix = (p4.squashEnabled || p4.learningEnabled) && counters.healthFixCount > 0 && p4.healthFixBase !== void 0;
388812
389000
  return {
388813
389001
  state: "idle",
388814
389002
  actor: "agent",
@@ -388817,7 +389005,7 @@ var applyTurnTaking = (p4, counters, head7, baseline) => {
388817
389005
  kind: "runHealthCheck",
388818
389006
  errorCount: counters.healthFixCount,
388819
389007
  capReached: counters.healthFixCount >= p4.fixAttemptCap,
388820
- squashAfterGreen: squashAfterGreenAtHealthFix
389008
+ chainAfterGreen: chainAfterGreenAtHealthFix
388821
389009
  },
388822
389010
  context: context10
388823
389011
  };
@@ -388832,7 +389020,7 @@ var applyTurnTaking = (p4, counters, head7, baseline) => {
388832
389020
  };
388833
389021
  }
388834
389022
  if (baseline.state === "idle" && invoker === "human") {
388835
- const squashAfterGreen = p4.squashEnabled && counters.healthFixCount > 0 && p4.healthFixBase !== void 0;
389023
+ const chainAfterGreen = (p4.squashEnabled || p4.learningEnabled) && counters.healthFixCount > 0 && p4.healthFixBase !== void 0;
388836
389024
  return {
388837
389025
  state: "idle",
388838
389026
  actor: "human",
@@ -388841,7 +389029,7 @@ var applyTurnTaking = (p4, counters, head7, baseline) => {
388841
389029
  kind: "runHealthCheck",
388842
389030
  errorCount: counters.healthFixCount,
388843
389031
  capReached: counters.healthFixCount >= p4.fixAttemptCap,
388844
- squashAfterGreen
389032
+ chainAfterGreen
388845
389033
  },
388846
389034
  context: context10
388847
389035
  };
@@ -388896,9 +389084,14 @@ var predictTurn = (events) => {
388896
389084
  const subject = baseline.action.kind === "commitRouting" ? baseline.action.subject : baseline.action.kind === "captureTurn" ? `gtd(${baseline.action.actor}): ${baseline.action.gate}` : null;
388897
389085
  return { actor: baseline.actor, subject, state: baseline.state };
388898
389086
  }
388899
- const isDirtyBoundaryEntry = !payload.workingTreeClean && !payload.todoCommitted && !payload.packagesPresent && !payload.reviewPresent && !payload.feedbackPresent && !payload.errorsPresent && !payload.healthPresent && (parseSubject(head7).kind === "boundary" || head7 === "gtd: done");
389087
+ const isDirtyBoundaryEntry = !payload.workingTreeClean && !payload.todoCommitted && !payload.architectureCommitted && !payload.packagesPresent && !payload.reviewPresent && !payload.feedbackPresent && !payload.errorsPresent && !payload.healthPresent && (parseSubject(head7).kind === "boundary" || head7 === "gtd: done");
388900
389088
  if (isDirtyBoundaryEntry) {
388901
- return { actor: "human", subject: "gtd(human): grilling", state: "grilling" };
389089
+ const entryGate = payload.architectureExists ? "architecting" : "grilling";
389090
+ return {
389091
+ actor: "human",
389092
+ subject: `gtd(human): ${entryGate}`,
389093
+ state: entryGate
389094
+ };
388902
389095
  }
388903
389096
  if (baseline.state === "idle") {
388904
389097
  return { actor: "human", subject: null, state: "idle" };
@@ -389387,13 +389580,19 @@ var package_default = '<%\n const lines = ["", "### Package: `" + it.pkg.name +
389387
389580
  var agent_turn_default = "Finish your turn by running `gtd step-agent`. Then run `gtd next` and follow\nits output \u2014 repeat this cycle as long as the output is addressed to you (the\nagent); when it awaits the human, stop and hand off.\n";
389388
389581
 
389389
389582
  // src/prompts/grilling-agent.md
389390
- var grilling_agent_default = '<%~ include("@header") %>\n\n`.gtd/TODO.md` holds the plan under development. Develop it into a concrete,\nimplementation-ready plan **in this one turn** \u2014 use subagents / internal\niteration to go as deep as needed; there is no further agent-only round after\nthis one.\n\n### Develop the plan\n\nSpawn a **planning-model subagent** using model `<%= it.model %>` to develop the plan.\nThe subagent works entirely by editing `.gtd/TODO.md`:\n\n1. **Explore the codebase before asking anything** \u2014 read the relevant files,\n tests, and docs so every question below is one the codebase genuinely\n cannot answer.\n2. **Replace the captured input / seed template with a real implementation\n plan** \u2014 the files to change, exactly what changes, and why, grounded in\n the codebase.\n3. **Iterate internally** (spawn further subagents, reconsider, cross-check)\n until the plan is as complete as it can get without human input. Do not\n leave this to a future round \u2014 there isn\'t one before the human is asked.\n4. For every remaining open question, write it near the top of the file with\n a **suggested default** \u2014 your best-guess answer, stated plainly, that the\n human can accept as-is. A question with no suggested default is incomplete.\n5. Leave `.gtd/TODO.md` **uncommitted** \u2014 the human\'s turn (`gtd step`) reads it\n next.\n<% if (it.context.turnDiff && it.context.turnDiff.trim()) { %>\n<%~ include("@diff", { heading: "Latest human input (answers / sketch / review feedback)", diff: it.context.turnDiff, fenceFor: it.fenceFor }) %>\n\nRead the diff above as **feedback, not finished work**:\n\n- Code changes are suggestions \u2014 fold them into the plan and re-implement them\n properly, including test coverage, rather than restoring them verbatim.\n- Code comments are positional feedback about the code at that location.\n- Plan-text changes are global feedback on the plan (or the reviewed work) as\n a whole.\n<% } %>\n\n<% if (it.tail) { %>\n<%~ include(it.tail) %>\n<% } %>\n';
389583
+ var grilling_agent_default = '<%~ include("@header") %>\n\n`.gtd/TODO.md` holds the plan under development. Develop it into a concrete,\nproduct-level plan **in this one turn** \u2014 use subagents / internal iteration\nto go as deep as needed; there is no further agent-only round after this one.\n\n**Scope: product and user-facing decisions only.** Do not decide\nimplementation details in this file \u2014 file/module structure, data models,\nlibrary or tech-stack choices, and other architecture questions belong to the\nnext phase (`.gtd/ARCHITECTURE.md`) and must not be treated as open questions\nhere.\n\n### Develop the plan\n\nSpawn a **planning-model subagent** using model `<%= it.model %>` to develop the plan.\nThe subagent works entirely by editing `.gtd/TODO.md`:\n\n1. **Explore the codebase before asking anything** \u2014 read the relevant files,\n tests, and docs so every question below is one the codebase genuinely\n cannot answer.\n2. **Replace the captured input / seed template with a real product plan** \u2014\n what to build and why, and the user-facing behavior it should have,\n grounded in the codebase. Leave *how* to build it for the next phase.\n3. **Iterate internally** (spawn further subagents, reconsider, cross-check)\n until the plan is as complete as it can get without human input. Do not\n leave this to a future round \u2014 there isn\'t one before the human is asked.\n4. For every remaining open question, write it near the top of the file with\n a **suggested default** \u2014 your best-guess answer, stated plainly, that the\n human can accept as-is. A question with no suggested default is incomplete.\n5. Leave `.gtd/TODO.md` **uncommitted** \u2014 the human\'s turn (`gtd step`) reads it\n next.\n<% if (it.context.turnDiff && it.context.turnDiff.trim()) { %>\n<%~ include("@diff", { heading: "Latest human input (answers / sketch / review feedback)", diff: it.context.turnDiff, fenceFor: it.fenceFor }) %>\n\nRead the diff above as **feedback, not finished work**:\n\n- Code changes are suggestions \u2014 fold them into the plan and re-implement them\n properly, including test coverage, rather than restoring them verbatim.\n- Code comments are positional feedback about the code at that location.\n- Plan-text changes are global feedback on the plan (or the reviewed work) as\n a whole.\n<% } %>\n\n<% if (it.tail) { %>\n<%~ include(it.tail) %>\n<% } %>\n';
389391
389584
 
389392
389585
  // src/prompts/grilling-answers.md
389393
- var grilling_answers_default = '<%~ include("@header") %>\n\n`.gtd/TODO.md` holds the plan under development, with open questions written near\nthe top \u2014 each one carrying a suggested default.\n\nThis is a human gate; there is nothing for the agent to do.\n\nTell the user:\n\n- To answer a question, edit `.gtd/TODO.md` in place (replace the question\'s text\n with your answer, or annotate it), then run `gtd step`.\n- To accept **all** suggested defaults as-is, run `gtd step` with **no\n edits** \u2014 this converges the plan and moves on to decomposition.\n\n<% if (it.tail) { %>\n<%~ include(it.tail) %>\n<% } %>\n';
389586
+ var grilling_answers_default = '<%~ include("@header") %>\n\n`.gtd/TODO.md` holds the plan under development, with open questions written near\nthe top \u2014 each one carrying a suggested default.\n\nThis is a human gate; there is nothing for the agent to do.\n\nTell the user:\n\n- To answer a question, edit `.gtd/TODO.md` in place (replace the question\'s text\n with your answer, or annotate it), then run `gtd step`.\n- To accept **all** suggested defaults as-is, run `gtd step` with **no\n edits** \u2014 this converges the plan and moves on to technical architecting.\n\n<% if (it.tail) { %>\n<%~ include(it.tail) %>\n<% } %>\n';
389587
+
389588
+ // src/prompts/architecting-agent.md
389589
+ var architecting_agent_default = '<%~ include("@header") %>\n\n`.gtd/ARCHITECTURE.md` holds the technical plan under development \u2014 seeded\nfrom the converged product plan, or, if this is the very first turn on\nalready-technical input, written directly by the human. Develop it into a concrete,\nimplementation-ready technical plan **in this one turn** \u2014 use subagents /\ninternal iteration to go as deep as needed; there is no further agent-only\nround after this one.\n\n**Scope: technical/architectural decisions only.** Decide file/module\nstructure, data models, module boundaries, library or tech-stack choices, and\nerror-handling/concurrency strategy \u2014 the *how*, building on the *what*\nalready settled in this file\'s product content. Do not re-open or re-litigate\nproduct/user-facing decisions from the prior phase; treat them as settled\ncontext.\n\n### Develop the architecture\n\nSpawn a **planning-model subagent** using model `<%= it.model %>` to develop the\narchitecture. The subagent works entirely by editing `.gtd/ARCHITECTURE.md`:\n\n1. **Explore the codebase before asking anything** \u2014 read the relevant files,\n tests, and docs so every question below is one the codebase genuinely\n cannot answer.\n2. **Replace the seeded/captured content with a real architecture** \u2014 the\n files to change, module/data-model structure, and why, grounded in the\n codebase.\n3. **Iterate internally** (spawn further subagents, reconsider, cross-check)\n until the architecture is as complete as it can get without human input.\n Do not leave this to a future round \u2014 there isn\'t one before the human is\n asked.\n4. For every remaining open question, write it near the top of the file with\n a **suggested default** \u2014 your best-guess answer, stated plainly, that the\n human can accept as-is. A question with no suggested default is incomplete.\n5. Leave `.gtd/ARCHITECTURE.md` **uncommitted** \u2014 the human\'s turn (`gtd step`)\n reads it next.\n<% if (it.context.turnDiff && it.context.turnDiff.trim()) { %>\n<%~ include("@diff", { heading: "Latest human input (answers / sketch / review feedback)", diff: it.context.turnDiff, fenceFor: it.fenceFor }) %>\n\nRead the diff above as **feedback, not finished work**:\n\n- Code changes are suggestions \u2014 fold them into the architecture and\n re-implement them properly, including test coverage, rather than restoring\n them verbatim.\n- Code comments are positional feedback about the code at that location.\n- Plan-text changes are global feedback on the architecture (or the reviewed\n work) as a whole.\n<% } %>\n\n<% if (it.tail) { %>\n<%~ include(it.tail) %>\n<% } %>\n';
389590
+
389591
+ // src/prompts/architecting-answers.md
389592
+ var architecting_answers_default = '<%~ include("@header") %>\n\n`.gtd/ARCHITECTURE.md` holds the technical plan under development, with open\nquestions written near the top \u2014 each one carrying a suggested default.\n\nThis is a human gate; there is nothing for the agent to do.\n\nTell the user:\n\n- To answer a question, edit `.gtd/ARCHITECTURE.md` in place (replace the\n question\'s text with your answer, or annotate it), then run `gtd step`.\n- To accept **all** suggested defaults as-is, run `gtd step` with **no\n edits** \u2014 this converges the architecture and moves on to decomposition.\n\n<% if (it.tail) { %>\n<%~ include(it.tail) %>\n<% } %>\n';
389394
389593
 
389395
389594
  // src/prompts/decompose.md
389396
- var decompose_default = '<%~ include("@header") %>\n\n`.gtd/TODO.md` contains an implementation plan. Decompose it into an ordered set of\nexecutable work packages stored in the `.gtd/` directory. If `.gtd/` already\nholds packages from an earlier turn, immediately abort and raise an error.\n\nSpawn a **planning-model subagent** using model `<%= it.model %>` to perform the\ndecomposition. It creates numbered package directories, each holding numbered\ntask files:\n\n```\n.gtd/\n 01-<package-name>/\n 01-<task-name>.md\n 02-<task-name>.md\n 02-<package-name>/\n ...\n```\n\n**Rules for the subagent:**\n\n1. **Packages are sequential, in dependency order** \u2014 ordinal-prefixed\n directories (`01-`, `02-`, \u2026) execute in that order and the set is frozen\n once written. Order them so each package depends only on lower-numbered ones.\n\n2. **Each package is green on its own** \u2014 the test suite runs after every\n package. Never split a feature so the tree stays red until a later package\n lands.\n\n3. **Tasks within a package are parallel and file-disjoint** \u2014 all tasks in a\n package run simultaneously, each via one subagent, against the same working\n tree with no isolation. Two tasks that would touch the same file must be\n **merged** into one. If task B depends on task A, put them in separate\n packages.\n\n4. **Vertical slices, not horizontal** \u2014 each package is a thin, end-to-end\n slice that is demoable or verifiable on its own. Prefer many thin packages\n over few thick ones; never a "set up infrastructure" package.\n\n5. **Task files are self-contained** \u2014 each task `.md` includes a clear\n description of what to build, acceptance criteria as `- [ ] Criterion`\n checkboxes, the relevant file paths to examine, and any constraints or edge\n cases. It is the only context building agents will receive to work on the\n task.\n\n6. **Specs must never reference `.gtd/` files** \u2014 `.gtd/TODO.md` is consumed\n and **deleted** the moment decomposition completes, and every other file\n under `.gtd/` (including these task specs) is machine-managed workflow\n state. No task description, acceptance criterion, or constraint may require\n creating, preserving, updating, or checking any `.gtd/` file \u2014 such a\n criterion can never be satisfied and would derail the workflow.\n\n**DO NOT** commit any changes. This process runs within a larger orchestration\nthat depends on uncommitted changes.\n\n<% if (it.tail) { %>\n<%~ include(it.tail) %>\n<% } %>\n';
389595
+ var decompose_default = '<%~ include("@header") %>\n\n`.gtd/ARCHITECTURE.md` contains a converged architecture/technical plan.\nDecompose it into an ordered set of executable work packages stored in the\n`.gtd/` directory. If `.gtd/` already holds packages from an earlier turn,\nimmediately abort and raise an error.\n\nSpawn a **planning-model subagent** using model `<%= it.model %>` to perform the\ndecomposition. It creates numbered package directories, each holding numbered\ntask files:\n\n```\n.gtd/\n 01-<package-name>/\n 01-<task-name>.md\n 02-<task-name>.md\n 02-<package-name>/\n ...\n```\n\n**Rules for the subagent:**\n\n1. **Packages are sequential, in dependency order** \u2014 ordinal-prefixed\n directories (`01-`, `02-`, \u2026) execute in that order and the set is frozen\n once written. Order them so each package depends only on lower-numbered ones.\n\n2. **Each package is green on its own** \u2014 the test suite runs after every\n package. Never split a feature so the tree stays red until a later package\n lands.\n\n3. **Tasks within a package are parallel and file-disjoint** \u2014 all tasks in a\n package run simultaneously, each via one subagent, against the same working\n tree with no isolation. Two tasks that would touch the same file must be\n **merged** into one. If task B depends on task A, put them in separate\n packages.\n\n4. **Vertical slices, not horizontal** \u2014 each package is a thin, end-to-end\n slice that is demoable or verifiable on its own. Prefer many thin packages\n over few thick ones; never a "set up infrastructure" package.\n\n5. **Task files are self-contained** \u2014 each task `.md` includes a clear\n description of what to build, acceptance criteria as `- [ ] Criterion`\n checkboxes, the relevant file paths to examine, and any constraints or edge\n cases. It is the only context building agents will receive to work on the\n task.\n\n6. **Specs must never reference `.gtd/` files** \u2014 `.gtd/ARCHITECTURE.md` is\n consumed and **deleted** the moment decomposition completes, and every other file\n under `.gtd/` (including these task specs) is machine-managed workflow\n state. No task description, acceptance criterion, or constraint may require\n creating, preserving, updating, or checking any `.gtd/` file \u2014 such a\n criterion can never be satisfied and would derail the workflow.\n\n**DO NOT** commit any changes. This process runs within a larger orchestration\nthat depends on uncommitted changes.\n\n<% if (it.tail) { %>\n<%~ include(it.tail) %>\n<% } %>\n';
389397
389596
 
389398
389597
  // src/prompts/building.md
389399
389598
  var building_default = '<%~ include("@header") %>\n\nBuild the package described below by orchestrating the execution \u2014 you do not\nimplement the tasks yourself. Spawn **one subagent per task**, all in\n**parallel**, each using model `<%= it.model %>`:\n\n- **Context**: the task content only (it is self-contained).\n- **Fresh context**: each worker starts cold, with no shared history.\n- **Hands off `.gtd/`** (inline rule for workers): never create, edit, or\n delete anything under `.gtd/` \u2014 it is workflow state owned by the machine.\n If the task spec mentions a `.gtd/` file, skip that part.\n- **TDD discipline** (inline rules for workers):\n - Write ONE test \u2192 implement \u2192 pass \u2192 repeat (vertical slices).\n - **Do NOT** write all tests first then implement (horizontal slicing).\n - Tests verify behavior through public interfaces, not implementation details\n \u2014 a good test survives a refactor.\n\nWait for all workers to complete. **If any worker fails** (crash, timeout, error\n\u2014 not a test failure): report which tasks failed and ask the user whether to\nretry the failed tasks, skip and continue, or abort.\n\nLeave all changes uncommitted. An outside process handles that.\n<% if (it.context.packages[0]) { %>\n<%~ include("@package", { pkg: it.context.packages[0], fenceFor: it.fenceFor }) %>\n<% } %>\n\n<% if (it.tail) { %>\n<%~ include(it.tail) %>\n<% } %>\n';
@@ -389414,7 +389613,16 @@ var review_default = '<%~ include("@header") %>\n\nSpawn a **planning-model suba
389414
389613
  var await_review_default = '<%~ include("@header") %>\n\n`.gtd/REVIEW.md` holds the review record for the completed work. This is a human\ngate; there is nothing for the agent to do.\n\nTell the user:\n\n- Read `.gtd/REVIEW.md` (and the underlying work it points at).\n- To **approve**: run `gtd step` with no edits, or only tick checkboxes in\n `.gtd/REVIEW.md` \u2014 either way ends the review with an approval.\n- To **request changes**: write substantive edits or annotations into\n `.gtd/REVIEW.md` (or the code) describing what\'s wrong, then run `gtd step` \u2014\n this sends the feedback back for another round.\n\n<% if (it.tail) { %>\n<%~ include(it.tail) %>\n<% } %>\n';
389415
389614
 
389416
389615
  // src/prompts/squashing.md
389417
- var squashing_default = '<%~ include("@header") %>\n\nThe process is **approved and done**. `.gtd/SQUASH_MSG.md` has already been\ncommitted with a template \u2014 your job is to overwrite it with the real\nconventional-commits squash message and finish your turn.\n\n### Step 1 \u2014 Extract decisions from grilling rounds\n\nScan the git history of recent `gtd: ...` / `gtd(agent): ...` /\n`gtd(human): ...` commits. Look for changes to `.gtd/TODO.md` \u2014 specifically the\n`## Captured input` sections and any edits to plan text. Extract **key\ndecisions, trade-offs, and design choices** made during grilling rounds. These\nwill appear in the commit body so the history is self-documenting.\n\n### Step 2 \u2014 Draft the commit message\n\nDraft ONE conventional-commits message:\n\n```\ntype(scope): subject\n\nbody (explain the why \u2014 motivation, trade-offs, key decisions from grilling)\n```\n\n- **type**: `feat` / `fix` / `refactor` / `chore` / `docs` / `test`\n- **subject**: imperative mood, \u2264 72 characters, lowercase after the colon\n- **body**: include the important decisions / trade-offs from grilling\n sessions. Omit if there were no meaningful decisions to capture.\n\n### Step 3 \u2014 Overwrite .gtd/SQUASH_MSG.md\n\nReplace the **entire content** of `.gtd/SQUASH_MSG.md` (plain text, no markdown\nwrapper, no leftover template scaffolding) with the message from Step 2, then\nleave it uncommitted and finish your turn \u2014 `gtd step-agent` performs the\nsquash using this file\'s content.\n<% if (it.context.squashDiff && it.context.squashDiff.trim()) { %>\n<%= it.context.squashBase !== undefined ? "\\nSquash base: " + it.context.squashBase + "\\n" : "" %>\n<%~ include("@diff", { heading: "Full-process diff (`git diff " + (it.context.squashBase !== undefined ? it.context.squashBase : "<squashBase>") + " HEAD`)", diff: it.context.squashDiff, fenceFor: it.fenceFor }) %>\n<% } %>\n\n<% if (it.tail) { %>\n<%~ include(it.tail) %>\n<% } %>\n';
389616
+ var squashing_default = '<%~ include("@header") %>\n\nThe process is **approved and done**. `.gtd/SQUASH_MSG.md` has already been\ncommitted with a template \u2014 your job is to overwrite it with the real\nconventional-commits squash message and finish your turn.\n\n### Step 1 \u2014 Extract decisions from grilling rounds\n\nScan the git history of recent `gtd: ...` / `gtd(agent): ...` /\n`gtd(human): ...` commits. Look for changes to `.gtd/TODO.md` and\n`.gtd/ARCHITECTURE.md` \u2014 specifically the `## Captured input` sections and any\nedits to plan/architecture text. Extract **key decisions, trade-offs, and\ndesign choices** made during grilling and architecting rounds. These will\nappear in the commit body so the history is self-documenting.\n\n### Step 2 \u2014 Draft the commit message\n\nDraft ONE conventional-commits message:\n\n```\ntype(scope): subject\n\nbody (explain the why \u2014 motivation, trade-offs, key decisions from grilling)\n```\n\n- **type**: `feat` / `fix` / `refactor` / `chore` / `docs` / `test`\n- **subject**: imperative mood, \u2264 72 characters, lowercase after the colon\n- **body**: include the important decisions / trade-offs from grilling\n sessions. Omit if there were no meaningful decisions to capture.\n\n### Step 3 \u2014 Overwrite .gtd/SQUASH_MSG.md\n\nReplace the **entire content** of `.gtd/SQUASH_MSG.md` (plain text, no markdown\nwrapper, no leftover template scaffolding) with the message from Step 2, then\nleave it uncommitted and finish your turn \u2014 `gtd step-agent` performs the\nsquash using this file\'s content.\n<% if (it.context.squashDiff && it.context.squashDiff.trim()) { %>\n<%= it.context.squashBase !== undefined ? "\\nSquash base: " + it.context.squashBase + "\\n" : "" %>\n<%~ include("@diff", { heading: "Full-process diff (`git diff " + (it.context.squashBase !== undefined ? it.context.squashBase : "<squashBase>") + " HEAD`)", diff: it.context.squashDiff, fenceFor: it.fenceFor }) %>\n<% } %>\n\n<% if (it.tail) { %>\n<%~ include(it.tail) %>\n<% } %>\n';
389617
+
389618
+ // src/prompts/learning.md
389619
+ var learning_default = '<%~ include("@header") %>\n\nThe process is **approved and done**, and headed for a squash. Before that\nhappens, `.gtd/LEARNINGS.md` has been committed with a template \u2014 your job is\nto overwrite it with the real, distilled learnings from this cycle so they\nsurvive the squash even though the granular history won\'t.\n\n### Step 1 \u2014 Walk the cycle\'s history\n\nScan the git history back to the cycle\'s start (the full-process diff is\ninlined below, and `git log` over the same range shows every `gtd: ...` /\n`gtd(agent): ...` / `gtd(human): ...` commit). Look specifically for:\n\n- **Test failures and fixes** \u2014 each `gtd: errors` round and the fix that\n followed it. What broke, and what actually made it pass?\n- **Review feedback** \u2014 human `.gtd/REVIEW.md` notes and `gtd: review\n feedback` detours. What did the human have to correct that the agent got\n wrong on its own?\n- **Health-check rounds** \u2014 any `gtd: health-check` / `gtd: health-fix`\n cycles. Environmental or systemic issues surfaced outside normal building?\n- **Grilling decisions** \u2014 `.gtd/TODO.md`\'s "## Captured input" sections and\n plan edits. Trade-offs or conventions the human specified that a future\n agent should already know.\n\n### Step 2 \u2014 Distill, don\'t transcribe\n\nFor each thing you found, ask: is this a **durable, generalizable lesson**\n(a convention, a gotcha, a pattern this project wants followed next time), or\na **one-off detail** specific to this change? Keep only the former. A future\nagent reading this should learn something that changes how it works on this\nproject, not a recap of what happened.\n\n### Step 3 \u2014 Overwrite .gtd/LEARNINGS.md\n\nReplace the **entire content** of `.gtd/LEARNINGS.md` with your distilled\nlearnings (plain markdown, no leftover template scaffolding). If nothing\ndurable surfaced this cycle, write a short explicit note saying so \u2014 don\'t\nleave the placeholder text in place. Leave the file uncommitted and finish\nyour turn; a human reviews it next before it\'s integrated into the project\'s\nown memory (CLAUDE.md/AGENTS.md/docs).\n<% if (it.context.squashDiff && it.context.squashDiff.trim()) { %>\n<%= it.context.squashBase !== undefined ? "\\nCycle base: " + it.context.squashBase + "\\n" : "" %>\n<%~ include("@diff", { heading: "Full-process diff (`git diff " + (it.context.squashBase !== undefined ? it.context.squashBase : "<squashBase>") + " HEAD`)", diff: it.context.squashDiff, fenceFor: it.fenceFor }) %>\n<% } %>\n\n<% if (it.tail) { %>\n<%~ include(it.tail) %>\n<% } %>\n';
389620
+
389621
+ // src/prompts/await-learning-review.md
389622
+ var await_learning_review_default = "<%~ include(\"@header\") %>\n\n`.gtd/LEARNINGS.md` holds the distilled learnings from this cycle, drafted by\nthe agent. This is a human gate; there is nothing for the agent to do.\n\nTell the user:\n\n- Read `.gtd/LEARNINGS.md`.\n- Delete anything not worth keeping, or edit/add anything the draft missed.\n- Run `gtd step` \u2014 with or without edits, either way proceeds: the agent\n integrates whatever remains into the project's own memory\n (CLAUDE.md/AGENTS.md/docs) next.\n\nThere is no reject path \u2014 this gate only refines what gets kept, it doesn't\nsend the cycle back for rework.\n\n<% if (it.tail) { %>\n<%~ include(it.tail) %>\n<% } %>\n";
389623
+
389624
+ // src/prompts/learning-apply.md
389625
+ var learning_apply_default = "<%~ include(\"@header\") %>\n\nThe human has approved (and possibly amended) `.gtd/LEARNINGS.md`. Your job is\nto integrate those learnings into the project's own memory, not to touch\n`.gtd/LEARNINGS.md` itself.\n\n1. **Read `.gtd/LEARNINGS.md`.**\n2. **Pick the right home for each point**, using your judgment and this\n project's existing convention:\n - An existing `CLAUDE.md` or `AGENTS.md` at the repo root (or a directory\n closer to what the learning concerns) is usually the right place.\n - If neither exists and the learnings warrant one, create `AGENTS.md` at\n the repo root.\n - A learning about a specific subsystem may fit better in that\n subsystem's own docs than a root-level file \u2014 use your judgment.\n3. **Integrate, don't just append** \u2014 merge each point into the relevant\n section of the target file(s); rephrase for clarity, dedupe against what's\n already documented, and keep the existing document's structure and tone.\n4. **Never delete or edit `.gtd/LEARNINGS.md`** \u2014 the machine removes it once\n your turn is captured.\n5. **Leave every change uncommitted and finish your turn.**\n\n<% if (it.tail) { %>\n<%~ include(it.tail) %>\n<% } %>\n";
389418
389626
 
389419
389627
  // src/prompts/escalate.md
389420
389628
  var escalate_default = '<%~ include("@header") %>\n\nThe agent was not able to fix all errors on its own. The last error report is\nstored in `.gtd/ERRORS.md` for a human to investigate.\n\nNext steps for the human developer:\n\n1. Investigate and fix errors reported in `.gtd/ERRORS.md`\n2. Delete `.gtd/ERRORS.md`\n3. Run `gtd step` when you are ready to continue\n\n<% if (it.tail) { %>\n<%~ include(it.tail) %>\n<% } %>\n';
@@ -389425,6 +389633,7 @@ var idle_default = "The repository is idle \u2014 no work cycle is in progress a
389425
389633
  // src/Prompt.ts
389426
389634
  var PROMPT_STATES = /* @__PURE__ */ new Set([
389427
389635
  "grilling",
389636
+ "architecting",
389428
389637
  "grilled",
389429
389638
  "building",
389430
389639
  "fixing",
@@ -389432,6 +389641,9 @@ var PROMPT_STATES = /* @__PURE__ */ new Set([
389432
389641
  "review",
389433
389642
  "await-review",
389434
389643
  "squashing",
389644
+ "learning",
389645
+ "await-learning-review",
389646
+ "learning-apply",
389435
389647
  "escalate",
389436
389648
  "idle",
389437
389649
  "health-fixing"
@@ -389439,13 +389651,16 @@ var PROMPT_STATES = /* @__PURE__ */ new Set([
389439
389651
  var isPromptState = (state) => PROMPT_STATES.has(state);
389440
389652
  var MODEL_STATE = {
389441
389653
  grilling: "grilling",
389654
+ architecting: "architecting",
389442
389655
  grilled: "decompose",
389443
389656
  building: "building",
389444
389657
  fixing: "fixing",
389445
389658
  "health-fixing": "fixing",
389446
389659
  "agentic-review": "agentic-review",
389447
389660
  review: "clean",
389448
- squashing: "clean"
389661
+ squashing: "clean",
389662
+ learning: "clean",
389663
+ "learning-apply": "clean"
389449
389664
  };
389450
389665
  var builtinResolveModel = (state) => builtinTierDefault[stateTier[state]];
389451
389666
  var fenceFor = (content) => {
@@ -389461,6 +389676,8 @@ eta.loadTemplate("@package", package_default);
389461
389676
  eta.loadTemplate("@agent-turn", agent_turn_default);
389462
389677
  eta.loadTemplate("@grilling-agent", grilling_agent_default);
389463
389678
  eta.loadTemplate("@grilling-answers", grilling_answers_default);
389679
+ eta.loadTemplate("@architecting-agent", architecting_agent_default);
389680
+ eta.loadTemplate("@architecting-answers", architecting_answers_default);
389464
389681
  eta.loadTemplate("@decompose", decompose_default);
389465
389682
  eta.loadTemplate("@building", building_default);
389466
389683
  eta.loadTemplate("@fixing", fixing_default);
@@ -389469,6 +389686,9 @@ eta.loadTemplate("@agentic-review", agentic_review_default);
389469
389686
  eta.loadTemplate("@review", review_default);
389470
389687
  eta.loadTemplate("@await-review", await_review_default);
389471
389688
  eta.loadTemplate("@squashing", squashing_default);
389689
+ eta.loadTemplate("@learning", learning_default);
389690
+ eta.loadTemplate("@await-learning-review", await_learning_review_default);
389691
+ eta.loadTemplate("@learning-apply", learning_apply_default);
389472
389692
  eta.loadTemplate("@escalate", escalate_default);
389473
389693
  eta.loadTemplate("@idle", idle_default);
389474
389694
  eta.readFile = null;
@@ -389481,6 +389701,9 @@ var STATE_TEMPLATE = {
389481
389701
  review: "@review",
389482
389702
  "await-review": "@await-review",
389483
389703
  squashing: "@squashing",
389704
+ learning: "@learning",
389705
+ "await-learning-review": "@await-learning-review",
389706
+ "learning-apply": "@learning-apply",
389484
389707
  escalate: "@escalate",
389485
389708
  idle: "@idle",
389486
389709
  "health-fixing": "@health-fixing"
@@ -389497,6 +389720,8 @@ var buildPrompt = (result, resolveModel = builtinResolveModel, output = "plain")
389497
389720
  let templateName;
389498
389721
  if (promptState === "grilling") {
389499
389722
  templateName = result.actor === "human" ? "@grilling-answers" : "@grilling-agent";
389723
+ } else if (promptState === "architecting") {
389724
+ templateName = result.actor === "human" ? "@architecting-answers" : "@architecting-agent";
389500
389725
  } else {
389501
389726
  templateName = STATE_TEMPLATE[promptState];
389502
389727
  }
@@ -389587,11 +389812,15 @@ var edgeActionHandlers = {
389587
389812
  captureTurn: (a5) => `capture the ${a5.actor} turn as "gtd(${a5.actor}): ${a5.gate}"`,
389588
389813
  commitRouting: (a5) => {
389589
389814
  let msg = `commit routing as "${a5.subject}"`;
389815
+ if (a5.seedArchitectureFromTodo) {
389816
+ msg += " (seeding .gtd/ARCHITECTURE.md from .gtd/TODO.md)";
389817
+ }
389590
389818
  const removed = [];
389591
- if (a5.removeTodo) removed.push(".gtd/TODO.md");
389819
+ if (a5.removeArchitecture) removed.push(".gtd/ARCHITECTURE.md");
389592
389820
  if (a5.removeReview) removed.push(".gtd/REVIEW.md");
389593
389821
  if (a5.removeFeedback) removed.push(".gtd/FEEDBACK.md");
389594
389822
  if (a5.removeHealth) removed.push(".gtd/HEALTH.md");
389823
+ if (a5.removeLearning) removed.push(".gtd/LEARNINGS.md");
389595
389824
  if (removed.length > 0) msg += ` (removing ${removed.join(", ")})`;
389596
389825
  return msg;
389597
389826
  },
@@ -389599,7 +389828,8 @@ var edgeActionHandlers = {
389599
389828
  closePackage: () => "close the active package",
389600
389829
  writeSquashTemplate: () => "write the squash message template",
389601
389830
  squashCommit: (a5) => `squash the cycle onto ${a5.squashBase}`,
389602
- runHealthCheck: (a5) => `run the health check (attempt ${a5.errorCount + 1}${a5.capReached ? ", cap reached" : ""}${a5.squashAfterGreen ? ", squash after green" : ""})`
389831
+ writeLearningTemplate: () => "write the learnings template",
389832
+ runHealthCheck: (a5) => `run the health check (attempt ${a5.errorCount + 1}${a5.capReached ? ", cap reached" : ""}${a5.chainAfterGreen ? ", chain after green" : ""})`
389603
389833
  };
389604
389834
  var describeEdgeAction = (a5) => edgeActionHandlers[a5.kind](a5);
389605
389835
  var describeStatus = (prediction) => ({