gsd-pi 2.41.0-dev.0acbce9 → 2.41.0-dev.3557dc4

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 (213) hide show
  1. package/dist/cli-web-branch.d.ts +6 -0
  2. package/dist/cli-web-branch.js +17 -0
  3. package/dist/onboarding.js +2 -1
  4. package/dist/resources/extensions/gsd/auto/loop.js +9 -1
  5. package/dist/resources/extensions/gsd/auto/phases.js +26 -8
  6. package/dist/resources/extensions/gsd/auto-dashboard.js +6 -2
  7. package/dist/resources/extensions/gsd/auto-dispatch.js +19 -2
  8. package/dist/resources/extensions/gsd/auto-post-unit.js +7 -0
  9. package/dist/resources/extensions/gsd/auto-recovery.js +12 -4
  10. package/dist/resources/extensions/gsd/auto-start.js +8 -3
  11. package/dist/resources/extensions/gsd/auto-worktree.js +147 -13
  12. package/dist/resources/extensions/gsd/auto.js +36 -1
  13. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +199 -164
  14. package/dist/resources/extensions/gsd/bootstrap/journal-tools.js +62 -0
  15. package/dist/resources/extensions/gsd/bootstrap/register-extension.js +2 -0
  16. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +16 -0
  17. package/dist/resources/extensions/gsd/commands/catalog.js +8 -1
  18. package/dist/resources/extensions/gsd/commands/handlers/core.js +1 -0
  19. package/dist/resources/extensions/gsd/commands/handlers/ops.js +5 -0
  20. package/dist/resources/extensions/gsd/context-store.js +4 -3
  21. package/dist/resources/extensions/gsd/db-writer.js +5 -2
  22. package/dist/resources/extensions/gsd/detection.js +1 -1
  23. package/dist/resources/extensions/gsd/doctor.js +11 -1
  24. package/dist/resources/extensions/gsd/exit-command.js +12 -2
  25. package/dist/resources/extensions/gsd/export.js +9 -13
  26. package/dist/resources/extensions/gsd/extension-manifest.json +2 -2
  27. package/dist/resources/extensions/gsd/files.js +28 -11
  28. package/dist/resources/extensions/gsd/forensics.js +10 -3
  29. package/dist/resources/extensions/gsd/git-service.js +5 -1
  30. package/dist/resources/extensions/gsd/gsd-db.js +25 -8
  31. package/dist/resources/extensions/gsd/guided-flow-queue.js +1 -1
  32. package/dist/resources/extensions/gsd/guided-flow.js +7 -3
  33. package/dist/resources/extensions/gsd/journal.js +85 -0
  34. package/dist/resources/extensions/gsd/md-importer.js +5 -0
  35. package/dist/resources/extensions/gsd/milestone-ids.js +1 -1
  36. package/dist/resources/extensions/gsd/native-git-bridge.js +2 -2
  37. package/dist/resources/extensions/gsd/post-unit-hooks.js +24 -412
  38. package/dist/resources/extensions/gsd/preferences-types.js +1 -0
  39. package/dist/resources/extensions/gsd/preferences.js +1 -0
  40. package/dist/resources/extensions/gsd/prompt-loader.js +34 -4
  41. package/dist/resources/extensions/gsd/prompts/complete-milestone.md +11 -10
  42. package/dist/resources/extensions/gsd/prompts/discuss-headless.md +2 -2
  43. package/dist/resources/extensions/gsd/prompts/discuss.md +1 -1
  44. package/dist/resources/extensions/gsd/prompts/queue.md +1 -1
  45. package/dist/resources/extensions/gsd/repo-identity.js +46 -2
  46. package/dist/resources/extensions/gsd/rule-registry.js +489 -0
  47. package/dist/resources/extensions/gsd/rule-types.js +6 -0
  48. package/dist/resources/extensions/gsd/service-tier.js +138 -0
  49. package/dist/resources/extensions/gsd/structured-data-formatter.js +2 -1
  50. package/dist/resources/extensions/gsd/templates/decisions.md +2 -2
  51. package/dist/resources/extensions/gsd/workflow-templates.js +13 -1
  52. package/dist/resources/extensions/gsd/worktree-manager.js +20 -6
  53. package/dist/resources/extensions/gsd/worktree-resolver.js +19 -2
  54. package/dist/resources/extensions/subagent/index.js +7 -3
  55. package/dist/resources/extensions/voice/index.js +4 -4
  56. package/dist/web/standalone/.next/BUILD_ID +1 -1
  57. package/dist/web/standalone/.next/app-path-routes-manifest.json +13 -13
  58. package/dist/web/standalone/.next/build-manifest.json +3 -3
  59. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  60. package/dist/web/standalone/.next/react-loadable-manifest.json +1 -1
  61. package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
  62. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  63. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  64. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  65. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  66. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  67. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  68. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  69. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  70. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  71. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  72. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  73. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  74. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  75. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  76. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  77. package/dist/web/standalone/.next/server/app/api/boot/route.js +1 -1
  78. package/dist/web/standalone/.next/server/app/api/captures/route.js +1 -1
  79. package/dist/web/standalone/.next/server/app/api/cleanup/route.js +1 -1
  80. package/dist/web/standalone/.next/server/app/api/doctor/route.js +1 -1
  81. package/dist/web/standalone/.next/server/app/api/export-data/route.js +1 -1
  82. package/dist/web/standalone/.next/server/app/api/forensics/route.js +1 -1
  83. package/dist/web/standalone/.next/server/app/api/history/route.js +1 -1
  84. package/dist/web/standalone/.next/server/app/api/hooks/route.js +1 -1
  85. package/dist/web/standalone/.next/server/app/api/recovery/route.js +1 -1
  86. package/dist/web/standalone/.next/server/app/api/settings-data/route.js +1 -1
  87. package/dist/web/standalone/.next/server/app/api/skill-health/route.js +1 -1
  88. package/dist/web/standalone/.next/server/app/api/terminal/input/route.js +1 -1
  89. package/dist/web/standalone/.next/server/app/api/terminal/resize/route.js +1 -1
  90. package/dist/web/standalone/.next/server/app/api/undo/route.js +1 -1
  91. package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
  92. package/dist/web/standalone/.next/server/app/index.html +1 -1
  93. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  94. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  95. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  96. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  97. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  98. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  99. package/dist/web/standalone/.next/server/app-paths-manifest.json +13 -13
  100. package/dist/web/standalone/.next/server/chunks/229.js +3 -3
  101. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  102. package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
  103. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  104. package/dist/web/standalone/.next/server/pages/500.html +2 -2
  105. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  106. package/dist/web/standalone/.next/static/chunks/4024.c195dc1fdd2adbea.js +9 -0
  107. package/dist/web/standalone/.next/static/chunks/{webpack-9afaaebf6042a1d7.js → webpack-fa307370fcf9fb2c.js} +1 -1
  108. package/dist/web-mode.d.ts +2 -0
  109. package/dist/web-mode.js +29 -7
  110. package/package.json +1 -1
  111. package/packages/native/src/__tests__/text.test.mjs +33 -0
  112. package/packages/pi-coding-agent/dist/core/discovery-cache.test.js +3 -1
  113. package/packages/pi-coding-agent/dist/core/discovery-cache.test.js.map +1 -1
  114. package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
  115. package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.js +10 -7
  116. package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.js.map +1 -1
  117. package/packages/pi-coding-agent/src/core/discovery-cache.test.ts +4 -2
  118. package/packages/pi-coding-agent/src/modes/interactive/components/login-dialog.ts +11 -7
  119. package/src/resources/extensions/gsd/auto/loop-deps.ts +5 -1
  120. package/src/resources/extensions/gsd/auto/loop.ts +10 -1
  121. package/src/resources/extensions/gsd/auto/phases.ts +28 -8
  122. package/src/resources/extensions/gsd/auto/types.ts +4 -0
  123. package/src/resources/extensions/gsd/auto-dashboard.ts +7 -2
  124. package/src/resources/extensions/gsd/auto-dispatch.ts +25 -5
  125. package/src/resources/extensions/gsd/auto-post-unit.ts +8 -0
  126. package/src/resources/extensions/gsd/auto-recovery.ts +12 -4
  127. package/src/resources/extensions/gsd/auto-start.ts +8 -3
  128. package/src/resources/extensions/gsd/auto-worktree.ts +162 -18
  129. package/src/resources/extensions/gsd/auto.ts +40 -1
  130. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +209 -162
  131. package/src/resources/extensions/gsd/bootstrap/journal-tools.ts +62 -0
  132. package/src/resources/extensions/gsd/bootstrap/register-extension.ts +2 -0
  133. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +13 -0
  134. package/src/resources/extensions/gsd/commands/catalog.ts +8 -1
  135. package/src/resources/extensions/gsd/commands/handlers/core.ts +1 -0
  136. package/src/resources/extensions/gsd/commands/handlers/ops.ts +5 -0
  137. package/src/resources/extensions/gsd/context-store.ts +4 -3
  138. package/src/resources/extensions/gsd/db-writer.ts +6 -2
  139. package/src/resources/extensions/gsd/detection.ts +1 -1
  140. package/src/resources/extensions/gsd/doctor.ts +12 -1
  141. package/src/resources/extensions/gsd/exit-command.ts +14 -2
  142. package/src/resources/extensions/gsd/export.ts +8 -15
  143. package/src/resources/extensions/gsd/extension-manifest.json +2 -2
  144. package/src/resources/extensions/gsd/files.ts +29 -12
  145. package/src/resources/extensions/gsd/forensics.ts +9 -3
  146. package/src/resources/extensions/gsd/git-service.ts +5 -4
  147. package/src/resources/extensions/gsd/gsd-db.ts +37 -8
  148. package/src/resources/extensions/gsd/guided-flow-queue.ts +1 -1
  149. package/src/resources/extensions/gsd/guided-flow.ts +7 -3
  150. package/src/resources/extensions/gsd/journal.ts +134 -0
  151. package/src/resources/extensions/gsd/md-importer.ts +6 -0
  152. package/src/resources/extensions/gsd/milestone-ids.ts +1 -1
  153. package/src/resources/extensions/gsd/native-git-bridge.ts +2 -2
  154. package/src/resources/extensions/gsd/post-unit-hooks.ts +24 -462
  155. package/src/resources/extensions/gsd/preferences-types.ts +3 -0
  156. package/src/resources/extensions/gsd/preferences.ts +1 -0
  157. package/src/resources/extensions/gsd/prompt-loader.ts +35 -4
  158. package/src/resources/extensions/gsd/prompts/complete-milestone.md +11 -10
  159. package/src/resources/extensions/gsd/prompts/discuss-headless.md +2 -2
  160. package/src/resources/extensions/gsd/prompts/discuss.md +1 -1
  161. package/src/resources/extensions/gsd/prompts/queue.md +1 -1
  162. package/src/resources/extensions/gsd/repo-identity.ts +47 -2
  163. package/src/resources/extensions/gsd/rule-registry.ts +599 -0
  164. package/src/resources/extensions/gsd/rule-types.ts +68 -0
  165. package/src/resources/extensions/gsd/service-tier.ts +171 -0
  166. package/src/resources/extensions/gsd/structured-data-formatter.ts +3 -1
  167. package/src/resources/extensions/gsd/templates/decisions.md +2 -2
  168. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +3 -2
  169. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +85 -0
  170. package/src/resources/extensions/gsd/tests/auto-worktree-milestone-merge.test.ts +202 -0
  171. package/src/resources/extensions/gsd/tests/context-store.test.ts +10 -5
  172. package/src/resources/extensions/gsd/tests/db-writer.test.ts +10 -0
  173. package/src/resources/extensions/gsd/tests/doctor-completion-deferral.test.ts +15 -10
  174. package/src/resources/extensions/gsd/tests/doctor-fixlevel.test.ts +5 -4
  175. package/src/resources/extensions/gsd/tests/doctor-roadmap-summary-atomicity.test.ts +167 -0
  176. package/src/resources/extensions/gsd/tests/doctor-task-done-missing-summary-slice-loop.test.ts +174 -0
  177. package/src/resources/extensions/gsd/tests/exit-command.test.ts +55 -0
  178. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +8 -1
  179. package/src/resources/extensions/gsd/tests/gsd-tools.test.ts +7 -7
  180. package/src/resources/extensions/gsd/tests/journal-integration.test.ts +513 -0
  181. package/src/resources/extensions/gsd/tests/journal-query-tool.test.ts +147 -0
  182. package/src/resources/extensions/gsd/tests/journal.test.ts +386 -0
  183. package/src/resources/extensions/gsd/tests/md-importer.test.ts +31 -1
  184. package/src/resources/extensions/gsd/tests/memory-store.test.ts +2 -2
  185. package/src/resources/extensions/gsd/tests/milestone-id-reservation.test.ts +1 -1
  186. package/src/resources/extensions/gsd/tests/parsers.test.ts +110 -0
  187. package/src/resources/extensions/gsd/tests/preferences.test.ts +47 -25
  188. package/src/resources/extensions/gsd/tests/prompt-db.test.ts +3 -1
  189. package/src/resources/extensions/gsd/tests/repo-identity-worktree.test.ts +61 -1
  190. package/src/resources/extensions/gsd/tests/routing-history.test.ts +11 -22
  191. package/src/resources/extensions/gsd/tests/rule-registry.test.ts +413 -0
  192. package/src/resources/extensions/gsd/tests/service-tier.test.ts +98 -0
  193. package/src/resources/extensions/gsd/tests/skill-lifecycle.test.ts +2 -2
  194. package/src/resources/extensions/gsd/tests/stalled-tool-recovery.test.ts +102 -0
  195. package/src/resources/extensions/gsd/tests/structured-data-formatter.test.ts +4 -3
  196. package/src/resources/extensions/gsd/tests/tool-naming.test.ts +117 -0
  197. package/src/resources/extensions/gsd/tests/triage-dispatch.test.ts +6 -1
  198. package/src/resources/extensions/gsd/tests/windows-path-normalization.test.ts +99 -0
  199. package/src/resources/extensions/gsd/tests/worktree-db-integration.test.ts +1 -0
  200. package/src/resources/extensions/gsd/tests/worktree-db.test.ts +4 -0
  201. package/src/resources/extensions/gsd/tests/worktree-health-dispatch.test.ts +178 -0
  202. package/src/resources/extensions/gsd/tests/worktree-resolver.test.ts +78 -3
  203. package/src/resources/extensions/gsd/tests/worktree-symlink-removal.test.ts +140 -0
  204. package/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts +74 -0
  205. package/src/resources/extensions/gsd/types.ts +3 -0
  206. package/src/resources/extensions/gsd/workflow-templates.ts +12 -1
  207. package/src/resources/extensions/gsd/worktree-manager.ts +21 -6
  208. package/src/resources/extensions/gsd/worktree-resolver.ts +30 -9
  209. package/src/resources/extensions/subagent/index.ts +7 -3
  210. package/src/resources/extensions/voice/index.ts +4 -4
  211. package/dist/web/standalone/.next/static/chunks/4024.279c423e4661ece1.js +0 -9
  212. /package/dist/web/standalone/.next/static/{SwbKZ7JPNFlEmU4f8pKEv → JBSIr4fSfHXs5g5x2ZBSC}/_buildManifest.js +0 -0
  213. /package/dist/web/standalone/.next/static/{SwbKZ7JPNFlEmU4f8pKEv → JBSIr4fSfHXs5g5x2ZBSC}/_ssgManifest.js +0 -0
@@ -0,0 +1,171 @@
1
+ /**
2
+ * Service Tier — gating, status formatting, icon resolution, and
3
+ * the /gsd fast command handler.
4
+ *
5
+ * Service tiers (priority/flex) are an OpenAI feature that only applies
6
+ * to gpt-5.4 variants. This module centralizes the model-gating logic
7
+ * so that icons, preferences, and the before_provider_request hook all
8
+ * use a single source of truth.
9
+ */
10
+
11
+ import type { ExtensionCommandContext } from "@gsd/pi-coding-agent";
12
+
13
+ import { existsSync, readFileSync } from "node:fs";
14
+ import { saveFile } from "./files.js";
15
+ import {
16
+ getGlobalGSDPreferencesPath,
17
+ loadEffectiveGSDPreferences,
18
+ loadGlobalGSDPreferences,
19
+ } from "./preferences.js";
20
+ import { ensurePreferencesFile, serializePreferencesToFrontmatter } from "./commands-prefs-wizard.js";
21
+
22
+ // ─── Types ───────────────────────────────────────────────────────────────────
23
+
24
+ export type ServiceTierSetting = "priority" | "flex" | undefined;
25
+
26
+ // ─── Gating ──────────────────────────────────────────────────────────────────
27
+
28
+ /**
29
+ * Returns true when the given model ID supports OpenAI service tiers.
30
+ * Currently only gpt-5.4 variants qualify.
31
+ */
32
+ export function supportsServiceTier(modelId: string): boolean {
33
+ if (!modelId) return false;
34
+ // Strip provider prefix if present (e.g. "openai/gpt-5.4" → "gpt-5.4")
35
+ const bare = modelId.includes("/") ? modelId.split("/").pop()! : modelId;
36
+ return bare.startsWith("gpt-5.4");
37
+ }
38
+
39
+ // ─── Status Formatting ───────────────────────────────────────────────────────
40
+
41
+ /**
42
+ * Human-readable description of the current service tier setting.
43
+ */
44
+ export function formatServiceTierStatus(tier: ServiceTierSetting): string {
45
+ if (!tier) {
46
+ return [
47
+ "Service tier: disabled",
48
+ "",
49
+ "Usage:",
50
+ " /gsd fast on Set to priority (2x cost, faster)",
51
+ " /gsd fast flex Set to flex (0.5x cost, slower)",
52
+ " /gsd fast off Disable service tier",
53
+ "",
54
+ "Only affects gpt-5.4 models.",
55
+ ].join("\n");
56
+ }
57
+
58
+ const label = tier === "priority" ? "priority (2x cost, faster)" : "flex (0.5x cost, slower)";
59
+ return [
60
+ `Service tier: ${label}`,
61
+ "",
62
+ "Usage:",
63
+ " /gsd fast on Set to priority (2x cost, faster)",
64
+ " /gsd fast flex Set to flex (0.5x cost, slower)",
65
+ " /gsd fast off Disable service tier",
66
+ "",
67
+ "Only affects gpt-5.4 models.",
68
+ ].join("\n");
69
+ }
70
+
71
+ // ─── Icon Resolution ─────────────────────────────────────────────────────────
72
+
73
+ /**
74
+ * Returns the appropriate icon for the active service tier and model.
75
+ * Returns empty string when the tier is inactive or the model doesn't
76
+ * support service tiers.
77
+ */
78
+ export function resolveServiceTierIcon(tier: ServiceTierSetting, modelId: string): string {
79
+ if (!tier || !supportsServiceTier(modelId)) return "";
80
+ return tier === "priority" ? "⚡" : "💰";
81
+ }
82
+
83
+ // ─── Preference Read ─────────────────────────────────────────────────────────
84
+
85
+ /**
86
+ * Read the effective service_tier setting from preferences.
87
+ */
88
+ export function getEffectiveServiceTier(): ServiceTierSetting {
89
+ const prefs = loadEffectiveGSDPreferences()?.preferences;
90
+ const raw = prefs?.service_tier;
91
+ if (raw === "priority" || raw === "flex") return raw;
92
+ return undefined;
93
+ }
94
+
95
+ // ─── Preference Write ────────────────────────────────────────────────────────
96
+
97
+ function extractBodyAfterFrontmatter(content: string): string | null {
98
+ const start = content.startsWith("---\n") ? 4 : content.startsWith("---\r\n") ? 5 : -1;
99
+ if (start === -1) return null;
100
+ const closingIdx = content.indexOf("\n---", start);
101
+ if (closingIdx === -1) return null;
102
+ const after = content.slice(closingIdx + 4);
103
+ return after.trim() ? after : null;
104
+ }
105
+
106
+ async function writeGlobalServiceTier(
107
+ ctx: ExtensionCommandContext,
108
+ tier: ServiceTierSetting,
109
+ ): Promise<void> {
110
+ const path = getGlobalGSDPreferencesPath();
111
+ await ensurePreferencesFile(path, ctx, "global");
112
+
113
+ const existing = loadGlobalGSDPreferences();
114
+ const prefs: Record<string, unknown> = existing?.preferences ? { ...existing.preferences } : {};
115
+ prefs.version = prefs.version || 1;
116
+
117
+ if (tier) {
118
+ prefs.service_tier = tier;
119
+ } else {
120
+ delete prefs.service_tier;
121
+ }
122
+
123
+ const frontmatter = serializePreferencesToFrontmatter(prefs);
124
+ let body = "\n# GSD Skill Preferences\n\nSee `~/.gsd/agent/extensions/gsd/docs/preferences-reference.md` for full field documentation and examples.\n";
125
+ if (existsSync(path)) {
126
+ const preserved = extractBodyAfterFrontmatter(readFileSync(path, "utf-8"));
127
+ if (preserved) body = preserved;
128
+ }
129
+
130
+ await saveFile(path, `---\n${frontmatter}---${body}`);
131
+ await ctx.waitForIdle();
132
+ await ctx.reload();
133
+ }
134
+
135
+ // ─── Command Handler ─────────────────────────────────────────────────────────
136
+
137
+ /**
138
+ * Handle `/gsd fast [on|off|flex|status]`.
139
+ */
140
+ export async function handleFast(args: string, ctx: ExtensionCommandContext): Promise<void> {
141
+ const trimmed = args.trim().toLowerCase();
142
+
143
+ if (!trimmed || trimmed === "status") {
144
+ const tier = getEffectiveServiceTier();
145
+ ctx.ui.notify(formatServiceTierStatus(tier), "info");
146
+ return;
147
+ }
148
+
149
+ if (trimmed === "on") {
150
+ await writeGlobalServiceTier(ctx, "priority");
151
+ ctx.ui.notify("Service tier set to priority (2x cost, faster responses). Only affects gpt-5.4 models.", "info");
152
+ return;
153
+ }
154
+
155
+ if (trimmed === "off") {
156
+ await writeGlobalServiceTier(ctx, undefined);
157
+ ctx.ui.notify("Service tier disabled.", "info");
158
+ return;
159
+ }
160
+
161
+ if (trimmed === "flex") {
162
+ await writeGlobalServiceTier(ctx, "flex");
163
+ ctx.ui.notify("Service tier set to flex (0.5x cost, slower responses). Only affects gpt-5.4 models.", "info");
164
+ return;
165
+ }
166
+
167
+ ctx.ui.notify(
168
+ "Usage: /gsd fast [on|off|flex|status]\n\n on Priority tier (2x cost, faster)\n off Disable service tier\n flex Flex tier (0.5x cost, slower)\n status Show current setting",
169
+ "warning",
170
+ );
171
+ }
@@ -25,6 +25,7 @@ interface DecisionInput {
25
25
  choice: string;
26
26
  rationale: string;
27
27
  revisable: string;
28
+ made_by?: string;
28
29
  }
29
30
 
30
31
  interface RequirementInput {
@@ -61,6 +62,7 @@ export function formatDecisionCompact(decision: DecisionInput): string {
61
62
  decision.choice,
62
63
  decision.rationale,
63
64
  decision.revisable,
65
+ decision.made_by ?? 'agent',
64
66
  ].join(" | ");
65
67
  }
66
68
 
@@ -70,7 +72,7 @@ export function formatDecisionsCompact(decisions: DecisionInput[]): string {
70
72
  return "# Decisions (compact)\n(none)";
71
73
  }
72
74
 
73
- const header = "# Decisions (compact)\nFields: id | when | scope | decision | choice | rationale | revisable";
75
+ const header = "# Decisions (compact)\nFields: id | when | scope | decision | choice | rationale | revisable | made_by";
74
76
  const lines = decisions.map(formatDecisionCompact);
75
77
  return `${header}\n\n${lines.join("\n")}`;
76
78
  }
@@ -4,5 +4,5 @@
4
4
  To reverse a decision, add a new row that supersedes it.
5
5
  Read this file at the start of any planning or research phase. -->
6
6
 
7
- | # | When | Scope | Decision | Choice | Rationale | Revisable? |
8
- |---|------|-------|----------|--------|-----------|------------|
7
+ | # | When | Scope | Decision | Choice | Rationale | Revisable? | Made By |
8
+ |---|------|-------|----------|--------|-----------|------------|---------|
@@ -337,7 +337,7 @@ function makeMockDeps(
337
337
  pruneQueueOrder: () => {},
338
338
  isInAutoWorktree: () => false,
339
339
  shouldUseWorktreeIsolation: () => false,
340
- mergeMilestoneToMain: () => ({ pushed: false }),
340
+ mergeMilestoneToMain: () => ({ pushed: false, codeFilesChanged: true }),
341
341
  teardownAutoWorktree: () => {},
342
342
  createAutoWorktree: () => "/tmp/wt",
343
343
  captureIntegrationBranch: () => {},
@@ -416,6 +416,7 @@ function makeMockDeps(
416
416
  getSessionFile: () => "/tmp/session.json",
417
417
  rebuildState: async () => {},
418
418
  resolveModelId: (id: string, models: any[]) => models.find((m: any) => m.id === id),
419
+ emitJournalEvent: () => {},
419
420
  };
420
421
 
421
422
  const merged = { ...baseDeps, ...overrides, callLog };
@@ -2122,7 +2123,7 @@ test("autoLoop stops when worktree has no project files for execute-task (#1833)
2122
2123
  "should stop auto-mode when worktree has no project files",
2123
2124
  );
2124
2125
  const healthNotification = notifications.find(
2125
- (n) => n.includes("Worktree health check failed") && n.includes("no package.json or src/"),
2126
+ (n) => n.includes("Worktree health check failed") && n.includes("no recognized project files"),
2126
2127
  );
2127
2128
  assert.ok(
2128
2129
  healthNotification,
@@ -386,6 +386,91 @@ test("verifyExpectedArtifact plan-slice fails for plan with no tasks (#699)", ()
386
386
  }
387
387
  });
388
388
 
389
+ // ─── verifyExpectedArtifact: heading-style plan tasks (#1691) ─────────────
390
+
391
+ test("verifyExpectedArtifact accepts plan-slice with heading-style tasks (### T01 --)", () => {
392
+ const base = makeTmpBase();
393
+ try {
394
+ const sliceDir = join(base, ".gsd", "milestones", "M001", "slices", "S01");
395
+ const tasksDir = join(sliceDir, "tasks");
396
+ mkdirSync(tasksDir, { recursive: true });
397
+ writeFileSync(join(sliceDir, "S01-PLAN.md"), [
398
+ "# S01: Test Slice",
399
+ "",
400
+ "## Tasks",
401
+ "",
402
+ "### T01 -- Implement feature",
403
+ "",
404
+ "Feature description.",
405
+ "",
406
+ "### T02 -- Write tests",
407
+ "",
408
+ "Test description.",
409
+ ].join("\n"));
410
+ writeFileSync(join(tasksDir, "T01-PLAN.md"), "# T01 Plan");
411
+ writeFileSync(join(tasksDir, "T02-PLAN.md"), "# T02 Plan");
412
+ assert.strictEqual(
413
+ verifyExpectedArtifact("plan-slice", "M001/S01", base),
414
+ true,
415
+ "Heading-style plan with task entries should be treated as completed artifact",
416
+ );
417
+ } finally {
418
+ cleanup(base);
419
+ }
420
+ });
421
+
422
+ test("verifyExpectedArtifact accepts plan-slice with colon-style heading tasks (### T01:)", () => {
423
+ const base = makeTmpBase();
424
+ try {
425
+ const sliceDir = join(base, ".gsd", "milestones", "M001", "slices", "S01");
426
+ const tasksDir = join(sliceDir, "tasks");
427
+ mkdirSync(tasksDir, { recursive: true });
428
+ writeFileSync(join(sliceDir, "S01-PLAN.md"), [
429
+ "# S01: Test Slice",
430
+ "",
431
+ "## Tasks",
432
+ "",
433
+ "### T01: Implement feature",
434
+ "",
435
+ "Feature description.",
436
+ ].join("\n"));
437
+ writeFileSync(join(tasksDir, "T01-PLAN.md"), "# T01 Plan");
438
+ assert.strictEqual(
439
+ verifyExpectedArtifact("plan-slice", "M001/S01", base),
440
+ true,
441
+ "Colon heading-style plan should be treated as completed artifact",
442
+ );
443
+ } finally {
444
+ cleanup(base);
445
+ }
446
+ });
447
+
448
+ test("verifyExpectedArtifact execute-task passes for heading-style plan entry (#1691)", () => {
449
+ const base = makeTmpBase();
450
+ try {
451
+ const sliceDir = join(base, ".gsd", "milestones", "M001", "slices", "S01");
452
+ const tasksDir = join(sliceDir, "tasks");
453
+ mkdirSync(tasksDir, { recursive: true });
454
+ writeFileSync(join(sliceDir, "S01-PLAN.md"), [
455
+ "# S01: Test Slice",
456
+ "",
457
+ "## Tasks",
458
+ "",
459
+ "### T01 -- Implement feature",
460
+ "",
461
+ "Feature description.",
462
+ ].join("\n"));
463
+ writeFileSync(join(tasksDir, "T01-SUMMARY.md"), "# T01 Summary\n\nDone.");
464
+ assert.strictEqual(
465
+ verifyExpectedArtifact("execute-task", "M001/S01/T01", base),
466
+ true,
467
+ "execute-task should pass for heading-style plan entry when summary exists",
468
+ );
469
+ } finally {
470
+ cleanup(base);
471
+ }
472
+ });
473
+
389
474
  // ─── selfHealRuntimeRecords — worktree base path (#769) ──────────────────
390
475
 
391
476
  test("selfHealRuntimeRecords clears stale dispatched records (#769)", async () => {
@@ -569,6 +569,208 @@ async function main(): Promise<void> {
569
569
  assertTrue(existsSync(join(repo, "landed.ts")), "landed.ts present on main");
570
570
  }
571
571
 
572
+ // ─── Test 14: Stale branch ref — worktree HEAD ahead of branch (#1846) ─
573
+ console.log("\n=== stale branch ref — fast-forward before squash merge (#1846) ===");
574
+ {
575
+ const repo = freshRepo();
576
+ const wtPath = createAutoWorktree(repo, "M140");
577
+
578
+ // Add a first slice normally — this advances both the branch ref and HEAD
579
+ addSliceToMilestone(repo, wtPath, "M140", "S01", "Initial work", [
580
+ { file: "initial.ts", content: "export const initial = true;\n", message: "add initial" },
581
+ ]);
582
+
583
+ // Now simulate the bug: detach HEAD in the worktree, then make commits
584
+ // that advance HEAD but leave the milestone/M140 branch ref behind.
585
+ const branchRefBefore = run("git rev-parse milestone/M140", wtPath);
586
+ run("git checkout --detach HEAD", wtPath);
587
+
588
+ // Add multiple commits on the detached HEAD (simulates agent work)
589
+ writeFileSync(join(wtPath, "feature-a.ts"), "export const featureA = true;\n");
590
+ run("git add .", wtPath);
591
+ run('git commit -m "add feature-a"', wtPath);
592
+
593
+ writeFileSync(join(wtPath, "feature-b.ts"), "export const featureB = true;\n");
594
+ run("git add .", wtPath);
595
+ run('git commit -m "add feature-b"', wtPath);
596
+
597
+ writeFileSync(join(wtPath, "feature-c.ts"), "export const featureC = true;\n");
598
+ run("git add .", wtPath);
599
+ run('git commit -m "add feature-c"', wtPath);
600
+
601
+ // Verify: branch ref is stale, HEAD is ahead
602
+ const branchRefAfter = run("git rev-parse milestone/M140", wtPath);
603
+ const worktreeHead = run("git rev-parse HEAD", wtPath);
604
+ assertEq(branchRefBefore, branchRefAfter, "branch ref unchanged (stale)");
605
+ assertTrue(worktreeHead !== branchRefAfter, "worktree HEAD ahead of branch ref");
606
+
607
+ const roadmap = makeRoadmap("M140", "Stale ref milestone", [
608
+ { id: "S01", title: "Initial work" },
609
+ ]);
610
+
611
+ // The fix should fast-forward the branch ref to worktree HEAD before
612
+ // squash-merging, so ALL commits are captured.
613
+ let threw = false;
614
+ let errMsg = "";
615
+ try {
616
+ const result = mergeMilestoneToMain(repo, "M140", roadmap);
617
+ assertTrue(result.commitMessage.includes("feat(M140)"), "merge commit created");
618
+ } catch (err) {
619
+ threw = true;
620
+ errMsg = err instanceof Error ? err.message : String(err);
621
+ }
622
+ assertTrue(!threw, `should not throw with stale branch ref (got: ${errMsg})`);
623
+
624
+ // ALL files from detached HEAD commits must be on main — not just
625
+ // the ones from the stale branch ref
626
+ assertTrue(existsSync(join(repo, "initial.ts")), "initial.ts on main");
627
+ assertTrue(existsSync(join(repo, "feature-a.ts")), "feature-a.ts on main (#1846)");
628
+ assertTrue(existsSync(join(repo, "feature-b.ts")), "feature-b.ts on main (#1846)");
629
+ assertTrue(existsSync(join(repo, "feature-c.ts")), "feature-c.ts on main (#1846)");
630
+ }
631
+
632
+ // ─── Test 15: Diverged worktree HEAD — throws instead of losing data (#1846) ─
633
+ console.log("\n=== diverged worktree HEAD — throws on divergence (#1846) ===");
634
+ {
635
+ const repo = freshRepo();
636
+ const wtPath = createAutoWorktree(repo, "M150");
637
+
638
+ addSliceToMilestone(repo, wtPath, "M150", "S01", "Base work", [
639
+ { file: "base.ts", content: "export const base = true;\n", message: "add base" },
640
+ ]);
641
+
642
+ run("git checkout --detach HEAD", wtPath);
643
+ writeFileSync(join(wtPath, "detached-work.ts"), "export const detached = true;\n");
644
+ run("git add .", wtPath);
645
+ run('git commit -m "detached work"', wtPath);
646
+
647
+ run("git checkout milestone/M150", repo);
648
+ writeFileSync(join(repo, "diverged-work.ts"), "export const diverged = true;\n");
649
+ run("git add .", repo);
650
+ run('git commit -m "diverged work on branch"', repo);
651
+ run("git checkout main", repo);
652
+
653
+ process.chdir(wtPath);
654
+
655
+ const roadmap = makeRoadmap("M150", "Diverged milestone", [
656
+ { id: "S01", title: "Base work" },
657
+ ]);
658
+
659
+ let threw = false;
660
+ let errMsg = "";
661
+ try {
662
+ mergeMilestoneToMain(repo, "M150", roadmap);
663
+ } catch (err) {
664
+ threw = true;
665
+ errMsg = err instanceof Error ? err.message : String(err);
666
+ }
667
+ assertTrue(threw, "throws when worktree HEAD diverged from branch ref (#1846)");
668
+ assertTrue(errMsg.includes("diverged"), "error message mentions divergence (#1846)");
669
+
670
+ const branches = run("git branch", repo);
671
+ assertTrue(branches.includes("milestone/M150"), "milestone branch preserved on divergence (#1846)");
672
+ }
673
+
674
+ // ─── Test 16: #1853 Bug 1 — SQUASH_MSG cleaned up after squash-merge ──
675
+ console.log("\n=== #1853 bug 1: SQUASH_MSG cleaned up after successful squash-merge ===");
676
+ {
677
+ const repo = freshRepo();
678
+ const wtPath = createAutoWorktree(repo, "M160");
679
+
680
+ addSliceToMilestone(repo, wtPath, "M160", "S01", "SQUASH_MSG cleanup test", [
681
+ { file: "squash-cleanup.ts", content: "export const cleanup = true;\n", message: "add squash-cleanup" },
682
+ ]);
683
+
684
+ const roadmap = makeRoadmap("M160", "SQUASH_MSG cleanup", [
685
+ { id: "S01", title: "SQUASH_MSG cleanup test" },
686
+ ]);
687
+
688
+ const squashMsgPath = join(repo, ".git", "SQUASH_MSG");
689
+ writeFileSync(squashMsgPath, "leftover squash message\n");
690
+ assertTrue(existsSync(squashMsgPath), "SQUASH_MSG planted before merge");
691
+
692
+ const result = mergeMilestoneToMain(repo, "M160", roadmap);
693
+ assertTrue(result.commitMessage.includes("feat(M160)"), "merge commit created");
694
+
695
+ assertTrue(
696
+ !existsSync(squashMsgPath),
697
+ "#1853: SQUASH_MSG must not persist after successful squash-merge",
698
+ );
699
+ }
700
+
701
+ // ─── Test 17: #1853 Bug 2 — uncommitted worktree code survives teardown ──
702
+ console.log("\n=== #1853 bug 2: uncommitted worktree changes committed before teardown ===");
703
+ {
704
+ const repo = freshRepo();
705
+ const wtPath = createAutoWorktree(repo, "M170");
706
+
707
+ addSliceToMilestone(repo, wtPath, "M170", "S01", "Teardown safety test", [
708
+ { file: "safe-file.ts", content: "export const safe = true;\n", message: "add safe file" },
709
+ ]);
710
+
711
+ writeFileSync(join(wtPath, "uncommitted-agent-code.ts"), "export const lost = true;\n");
712
+
713
+ const roadmap = makeRoadmap("M170", "Teardown safety", [
714
+ { id: "S01", title: "Teardown safety test" },
715
+ ]);
716
+
717
+ const result = mergeMilestoneToMain(repo, "M170", roadmap);
718
+ assertTrue(result.commitMessage.includes("feat(M170)"), "merge commit created");
719
+
720
+ assertTrue(
721
+ existsSync(join(repo, "uncommitted-agent-code.ts")),
722
+ "#1853: uncommitted worktree code must survive teardown",
723
+ );
724
+ }
725
+
726
+ // ─── Test 18: #1906 — codeFilesChanged false when only .gsd/ metadata merged ──
727
+ console.log("\n=== #1906: codeFilesChanged=false when only .gsd/ metadata merged ===");
728
+ {
729
+ const repo = freshRepo();
730
+ const wtPath = createAutoWorktree(repo, "M180");
731
+
732
+ // Only add .gsd/ metadata files — no actual code
733
+ mkdirSync(join(wtPath, ".gsd", "milestones", "M180"), { recursive: true });
734
+ writeFileSync(
735
+ join(wtPath, ".gsd", "milestones", "M180", "SUMMARY.md"),
736
+ "# M180 Summary\n\nThis milestone was planned but not implemented.\n",
737
+ );
738
+ run("git add .", wtPath);
739
+ run('git commit -m "chore: add milestone summary"', wtPath);
740
+
741
+ const roadmap = makeRoadmap("M180", "Metadata-only milestone", []);
742
+
743
+ const result = mergeMilestoneToMain(repo, "M180", roadmap);
744
+ assertEq(
745
+ result.codeFilesChanged,
746
+ false,
747
+ "#1906: codeFilesChanged must be false when only .gsd/ files were merged",
748
+ );
749
+ }
750
+
751
+ // ─── Test 19: #1906 — codeFilesChanged true when real code is merged ──
752
+ console.log("\n=== #1906: codeFilesChanged=true when real code is merged ===");
753
+ {
754
+ const repo = freshRepo();
755
+ const wtPath = createAutoWorktree(repo, "M190");
756
+
757
+ addSliceToMilestone(repo, wtPath, "M190", "S01", "Real code", [
758
+ { file: "real-code.ts", content: "export const real = true;\n", message: "add real code" },
759
+ ]);
760
+
761
+ const roadmap = makeRoadmap("M190", "Code milestone", [
762
+ { id: "S01", title: "Real code" },
763
+ ]);
764
+
765
+ const result = mergeMilestoneToMain(repo, "M190", roadmap);
766
+ assertEq(
767
+ result.codeFilesChanged,
768
+ true,
769
+ "#1906: codeFilesChanged must be true when real code files were merged",
770
+ );
771
+ assertTrue(existsSync(join(repo, "real-code.ts")), "real-code.ts merged to main");
772
+ }
773
+
572
774
  } finally {
573
775
  process.chdir(savedCwd);
574
776
  for (const d of tempDirs) {
@@ -51,17 +51,17 @@ console.log('\n=== context-store: query all active decisions ===');
51
51
  insertDecision({
52
52
  id: 'D001', when_context: 'M001/S01', scope: 'architecture',
53
53
  decision: 'use SQLite', choice: 'node:sqlite', rationale: 'built-in',
54
- revisable: 'yes', superseded_by: 'D003', // superseded!
54
+ revisable: 'yes', made_by: 'agent', superseded_by: 'D003', // superseded!
55
55
  });
56
56
  insertDecision({
57
57
  id: 'D002', when_context: 'M001/S01', scope: 'architecture',
58
58
  decision: 'use WAL mode', choice: 'WAL', rationale: 'concurrent reads',
59
- revisable: 'no', superseded_by: null,
59
+ revisable: 'no', made_by: 'agent', superseded_by: null,
60
60
  });
61
61
  insertDecision({
62
62
  id: 'D003', when_context: 'M002/S01', scope: 'performance',
63
63
  decision: 'use better-sqlite3', choice: 'better-sqlite3', rationale: 'faster',
64
- revisable: 'yes', superseded_by: null,
64
+ revisable: 'yes', made_by: 'agent', superseded_by: null,
65
65
  });
66
66
 
67
67
  const all = queryDecisions();
@@ -81,11 +81,13 @@ console.log('\n=== context-store: query decisions by milestone ===');
81
81
  insertDecision({
82
82
  id: 'D001', when_context: 'M001/S01', scope: 'architecture',
83
83
  decision: 'decision A', choice: 'A', rationale: 'r', revisable: 'yes',
84
+ made_by: 'agent',
84
85
  superseded_by: null,
85
86
  });
86
87
  insertDecision({
87
88
  id: 'D002', when_context: 'M002/S02', scope: 'architecture',
88
89
  decision: 'decision B', choice: 'B', rationale: 'r', revisable: 'yes',
90
+ made_by: 'agent',
89
91
  superseded_by: null,
90
92
  });
91
93
 
@@ -107,11 +109,13 @@ console.log('\n=== context-store: query decisions by scope ===');
107
109
  insertDecision({
108
110
  id: 'D001', when_context: 'M001/S01', scope: 'architecture',
109
111
  decision: 'decision A', choice: 'A', rationale: 'r', revisable: 'yes',
112
+ made_by: 'agent',
110
113
  superseded_by: null,
111
114
  });
112
115
  insertDecision({
113
116
  id: 'D002', when_context: 'M001/S01', scope: 'performance',
114
117
  decision: 'decision B', choice: 'B', rationale: 'r', revisable: 'yes',
118
+ made_by: 'agent',
115
119
  superseded_by: null,
116
120
  });
117
121
 
@@ -248,12 +252,12 @@ console.log('\n=== context-store: formatDecisionsForPrompt ===');
248
252
  {
249
253
  seq: 1, id: 'D001', when_context: 'M001/S01', scope: 'architecture',
250
254
  decision: 'use SQLite', choice: 'node:sqlite', rationale: 'built-in',
251
- revisable: 'yes', superseded_by: null,
255
+ revisable: 'yes', made_by: 'agent', superseded_by: null,
252
256
  },
253
257
  {
254
258
  seq: 2, id: 'D002', when_context: 'M001/S02', scope: 'performance',
255
259
  decision: 'use WAL', choice: 'WAL', rationale: 'concurrent',
256
- revisable: 'no', superseded_by: null,
260
+ revisable: 'no', made_by: 'human', superseded_by: null,
257
261
  },
258
262
  ]);
259
263
 
@@ -323,6 +327,7 @@ console.log('\n=== context-store: sub-5ms query timing ===');
323
327
  choice: `choice ${i}`,
324
328
  rationale: `rationale ${i}`,
325
329
  revisable: i % 3 === 0 ? 'no' : 'yes',
330
+ made_by: 'agent',
326
331
  superseded_by: null,
327
332
  });
328
333
  }
@@ -59,6 +59,7 @@ const SAMPLE_DECISIONS: Decision[] = [
59
59
  choice: 'better-sqlite3',
60
60
  rationale: 'Sync API',
61
61
  revisable: 'No',
62
+ made_by: 'collaborative',
62
63
  superseded_by: null,
63
64
  },
64
65
  {
@@ -70,6 +71,7 @@ const SAMPLE_DECISIONS: Decision[] = [
70
71
  choice: '.gsd/gsd.db',
71
72
  rationale: 'Derived state',
72
73
  revisable: 'No',
74
+ made_by: 'agent',
73
75
  superseded_by: null,
74
76
  },
75
77
  {
@@ -81,6 +83,7 @@ const SAMPLE_DECISIONS: Decision[] = [
81
83
  choice: 'node:sqlite fallback',
82
84
  rationale: 'Zero deps',
83
85
  revisable: 'Yes',
86
+ made_by: 'human',
84
87
  superseded_by: null,
85
88
  },
86
89
  ];
@@ -166,6 +169,7 @@ console.log('\n── generateDecisionsMd round-trip ──');
166
169
  assertEq(rt.choice, orig.choice, `decision ${orig.id} choice round-trips`);
167
170
  assertEq(rt.rationale, orig.rationale, `decision ${orig.id} rationale round-trips`);
168
171
  assertEq(rt.revisable, orig.revisable, `decision ${orig.id} revisable round-trips`);
172
+ assertEq(rt.made_by, orig.made_by, `decision ${orig.id} made_by round-trips`);
169
173
  }
170
174
  }
171
175
 
@@ -177,6 +181,7 @@ console.log('\n── generateDecisionsMd format ──');
177
181
  assertTrue(md.includes('<!-- Append-only'), 'contains HTML comment block');
178
182
  assertTrue(md.includes('| # | When | Scope'), 'contains table header');
179
183
  assertTrue(md.includes('|---|------|-------'), 'contains separator row');
184
+ assertTrue(md.includes('| Made By |'), 'contains Made By column header');
180
185
  }
181
186
 
182
187
  console.log('\n── generateDecisionsMd empty input ──');
@@ -200,6 +205,7 @@ console.log('\n── generateDecisionsMd pipe escaping ──');
200
205
  choice: 'A',
201
206
  rationale: 'Better',
202
207
  revisable: 'No',
208
+ made_by: 'agent',
203
209
  superseded_by: null,
204
210
  };
205
211
  const md = generateDecisionsMd([withPipe]);
@@ -291,6 +297,7 @@ console.log('\n── nextDecisionId ──');
291
297
  choice: 'test choice',
292
298
  rationale: 'test',
293
299
  revisable: 'No',
300
+ made_by: 'agent',
294
301
  superseded_by: null,
295
302
  });
296
303
  upsertDecision({
@@ -301,6 +308,7 @@ console.log('\n── nextDecisionId ──');
301
308
  choice: 'test choice',
302
309
  rationale: 'test',
303
310
  revisable: 'No',
311
+ made_by: 'agent',
304
312
  superseded_by: null,
305
313
  });
306
314
 
@@ -520,6 +528,7 @@ console.log('\n── Full DB round-trip: decisions ──');
520
528
  choice: d.choice,
521
529
  rationale: d.rationale,
522
530
  revisable: d.revisable,
531
+ made_by: d.made_by,
523
532
  superseded_by: d.superseded_by,
524
533
  });
525
534
  }
@@ -536,6 +545,7 @@ console.log('\n── Full DB round-trip: decisions ──');
536
545
  choice: row['choice'] as string,
537
546
  rationale: row['rationale'] as string,
538
547
  revisable: row['revisable'] as string,
548
+ made_by: (row['made_by'] as string as import('../types.js').DecisionMadeBy) ?? 'agent',
539
549
  superseded_by: (row['superseded_by'] as string) ?? null,
540
550
  }));
541
551