@kynetic-ai/spec 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (278) hide show
  1. package/README.md +263 -0
  2. package/dist/acp/client.d.ts +159 -0
  3. package/dist/acp/client.d.ts.map +1 -0
  4. package/dist/acp/client.js +255 -0
  5. package/dist/acp/client.js.map +1 -0
  6. package/dist/acp/framing.d.ts +119 -0
  7. package/dist/acp/framing.d.ts.map +1 -0
  8. package/dist/acp/framing.js +302 -0
  9. package/dist/acp/framing.js.map +1 -0
  10. package/dist/acp/index.d.ts +14 -0
  11. package/dist/acp/index.d.ts.map +1 -0
  12. package/dist/acp/index.js +13 -0
  13. package/dist/acp/index.js.map +1 -0
  14. package/dist/acp/types.d.ts +89 -0
  15. package/dist/acp/types.d.ts.map +1 -0
  16. package/dist/acp/types.js +99 -0
  17. package/dist/acp/types.js.map +1 -0
  18. package/dist/agents/adapters.d.ts +55 -0
  19. package/dist/agents/adapters.d.ts.map +1 -0
  20. package/dist/agents/adapters.js +84 -0
  21. package/dist/agents/adapters.js.map +1 -0
  22. package/dist/agents/index.d.ts +8 -0
  23. package/dist/agents/index.d.ts.map +1 -0
  24. package/dist/agents/index.js +10 -0
  25. package/dist/agents/index.js.map +1 -0
  26. package/dist/agents/spawner.d.ts +53 -0
  27. package/dist/agents/spawner.d.ts.map +1 -0
  28. package/dist/agents/spawner.js +83 -0
  29. package/dist/agents/spawner.js.map +1 -0
  30. package/dist/cli/batch.d.ts +82 -0
  31. package/dist/cli/batch.d.ts.map +1 -0
  32. package/dist/cli/batch.js +162 -0
  33. package/dist/cli/batch.js.map +1 -0
  34. package/dist/cli/commands/clone-for-testing.d.ts +6 -0
  35. package/dist/cli/commands/clone-for-testing.d.ts.map +1 -0
  36. package/dist/cli/commands/clone-for-testing.js +176 -0
  37. package/dist/cli/commands/clone-for-testing.js.map +1 -0
  38. package/dist/cli/commands/derive.d.ts +6 -0
  39. package/dist/cli/commands/derive.d.ts.map +1 -0
  40. package/dist/cli/commands/derive.js +450 -0
  41. package/dist/cli/commands/derive.js.map +1 -0
  42. package/dist/cli/commands/help.d.ts +6 -0
  43. package/dist/cli/commands/help.d.ts.map +1 -0
  44. package/dist/cli/commands/help.js +196 -0
  45. package/dist/cli/commands/help.js.map +1 -0
  46. package/dist/cli/commands/inbox.d.ts +6 -0
  47. package/dist/cli/commands/inbox.d.ts.map +1 -0
  48. package/dist/cli/commands/inbox.js +235 -0
  49. package/dist/cli/commands/inbox.js.map +1 -0
  50. package/dist/cli/commands/index.d.ts +20 -0
  51. package/dist/cli/commands/index.d.ts.map +1 -0
  52. package/dist/cli/commands/index.js +21 -0
  53. package/dist/cli/commands/index.js.map +1 -0
  54. package/dist/cli/commands/init.d.ts +6 -0
  55. package/dist/cli/commands/init.d.ts.map +1 -0
  56. package/dist/cli/commands/init.js +245 -0
  57. package/dist/cli/commands/init.js.map +1 -0
  58. package/dist/cli/commands/item.d.ts +6 -0
  59. package/dist/cli/commands/item.d.ts.map +1 -0
  60. package/dist/cli/commands/item.js +1311 -0
  61. package/dist/cli/commands/item.js.map +1 -0
  62. package/dist/cli/commands/link.d.ts +6 -0
  63. package/dist/cli/commands/link.d.ts.map +1 -0
  64. package/dist/cli/commands/link.js +288 -0
  65. package/dist/cli/commands/link.js.map +1 -0
  66. package/dist/cli/commands/log.d.ts +16 -0
  67. package/dist/cli/commands/log.d.ts.map +1 -0
  68. package/dist/cli/commands/log.js +291 -0
  69. package/dist/cli/commands/log.js.map +1 -0
  70. package/dist/cli/commands/meta.d.ts +15 -0
  71. package/dist/cli/commands/meta.d.ts.map +1 -0
  72. package/dist/cli/commands/meta.js +1378 -0
  73. package/dist/cli/commands/meta.js.map +1 -0
  74. package/dist/cli/commands/module.d.ts +6 -0
  75. package/dist/cli/commands/module.d.ts.map +1 -0
  76. package/dist/cli/commands/module.js +102 -0
  77. package/dist/cli/commands/module.js.map +1 -0
  78. package/dist/cli/commands/ralph.d.ts +9 -0
  79. package/dist/cli/commands/ralph.d.ts.map +1 -0
  80. package/dist/cli/commands/ralph.js +465 -0
  81. package/dist/cli/commands/ralph.js.map +1 -0
  82. package/dist/cli/commands/search.d.ts +6 -0
  83. package/dist/cli/commands/search.d.ts.map +1 -0
  84. package/dist/cli/commands/search.js +134 -0
  85. package/dist/cli/commands/search.js.map +1 -0
  86. package/dist/cli/commands/session.d.ts +164 -0
  87. package/dist/cli/commands/session.d.ts.map +1 -0
  88. package/dist/cli/commands/session.js +745 -0
  89. package/dist/cli/commands/session.js.map +1 -0
  90. package/dist/cli/commands/setup.d.ts +26 -0
  91. package/dist/cli/commands/setup.d.ts.map +1 -0
  92. package/dist/cli/commands/setup.js +586 -0
  93. package/dist/cli/commands/setup.js.map +1 -0
  94. package/dist/cli/commands/shadow.d.ts +6 -0
  95. package/dist/cli/commands/shadow.d.ts.map +1 -0
  96. package/dist/cli/commands/shadow.js +299 -0
  97. package/dist/cli/commands/shadow.js.map +1 -0
  98. package/dist/cli/commands/task.d.ts +6 -0
  99. package/dist/cli/commands/task.d.ts.map +1 -0
  100. package/dist/cli/commands/task.js +1514 -0
  101. package/dist/cli/commands/task.js.map +1 -0
  102. package/dist/cli/commands/tasks.d.ts +6 -0
  103. package/dist/cli/commands/tasks.d.ts.map +1 -0
  104. package/dist/cli/commands/tasks.js +347 -0
  105. package/dist/cli/commands/tasks.js.map +1 -0
  106. package/dist/cli/commands/trait.d.ts +10 -0
  107. package/dist/cli/commands/trait.d.ts.map +1 -0
  108. package/dist/cli/commands/trait.js +295 -0
  109. package/dist/cli/commands/trait.js.map +1 -0
  110. package/dist/cli/commands/validate.d.ts +6 -0
  111. package/dist/cli/commands/validate.d.ts.map +1 -0
  112. package/dist/cli/commands/validate.js +626 -0
  113. package/dist/cli/commands/validate.js.map +1 -0
  114. package/dist/cli/exit-codes.d.ts +62 -0
  115. package/dist/cli/exit-codes.d.ts.map +1 -0
  116. package/dist/cli/exit-codes.js +65 -0
  117. package/dist/cli/exit-codes.js.map +1 -0
  118. package/dist/cli/help/content.d.ts +35 -0
  119. package/dist/cli/help/content.d.ts.map +1 -0
  120. package/dist/cli/help/content.js +312 -0
  121. package/dist/cli/help/content.js.map +1 -0
  122. package/dist/cli/index.d.ts +5 -0
  123. package/dist/cli/index.d.ts.map +1 -0
  124. package/dist/cli/index.js +85 -0
  125. package/dist/cli/index.js.map +1 -0
  126. package/dist/cli/introspection.d.ts +87 -0
  127. package/dist/cli/introspection.d.ts.map +1 -0
  128. package/dist/cli/introspection.js +127 -0
  129. package/dist/cli/introspection.js.map +1 -0
  130. package/dist/cli/output.d.ts +56 -0
  131. package/dist/cli/output.d.ts.map +1 -0
  132. package/dist/cli/output.js +467 -0
  133. package/dist/cli/output.js.map +1 -0
  134. package/dist/cli/suggest.d.ts +16 -0
  135. package/dist/cli/suggest.d.ts.map +1 -0
  136. package/dist/cli/suggest.js +72 -0
  137. package/dist/cli/suggest.js.map +1 -0
  138. package/dist/index.d.ts +3 -0
  139. package/dist/index.d.ts.map +1 -0
  140. package/dist/index.js +5 -0
  141. package/dist/index.js.map +1 -0
  142. package/dist/parser/alignment.d.ts +113 -0
  143. package/dist/parser/alignment.d.ts.map +1 -0
  144. package/dist/parser/alignment.js +261 -0
  145. package/dist/parser/alignment.js.map +1 -0
  146. package/dist/parser/assess.d.ts +81 -0
  147. package/dist/parser/assess.d.ts.map +1 -0
  148. package/dist/parser/assess.js +197 -0
  149. package/dist/parser/assess.js.map +1 -0
  150. package/dist/parser/convention-validation.d.ts +48 -0
  151. package/dist/parser/convention-validation.d.ts.map +1 -0
  152. package/dist/parser/convention-validation.js +167 -0
  153. package/dist/parser/convention-validation.js.map +1 -0
  154. package/dist/parser/fix.d.ts +38 -0
  155. package/dist/parser/fix.d.ts.map +1 -0
  156. package/dist/parser/fix.js +185 -0
  157. package/dist/parser/fix.js.map +1 -0
  158. package/dist/parser/index.d.ts +12 -0
  159. package/dist/parser/index.d.ts.map +1 -0
  160. package/dist/parser/index.js +13 -0
  161. package/dist/parser/index.js.map +1 -0
  162. package/dist/parser/items.d.ts +138 -0
  163. package/dist/parser/items.d.ts.map +1 -0
  164. package/dist/parser/items.js +321 -0
  165. package/dist/parser/items.js.map +1 -0
  166. package/dist/parser/meta.d.ts +120 -0
  167. package/dist/parser/meta.d.ts.map +1 -0
  168. package/dist/parser/meta.js +441 -0
  169. package/dist/parser/meta.js.map +1 -0
  170. package/dist/parser/refs.d.ts +185 -0
  171. package/dist/parser/refs.d.ts.map +1 -0
  172. package/dist/parser/refs.js +404 -0
  173. package/dist/parser/refs.js.map +1 -0
  174. package/dist/parser/shadow.d.ts +253 -0
  175. package/dist/parser/shadow.d.ts.map +1 -0
  176. package/dist/parser/shadow.js +1053 -0
  177. package/dist/parser/shadow.js.map +1 -0
  178. package/dist/parser/traits.d.ts +72 -0
  179. package/dist/parser/traits.d.ts.map +1 -0
  180. package/dist/parser/traits.js +120 -0
  181. package/dist/parser/traits.js.map +1 -0
  182. package/dist/parser/validate.d.ts +89 -0
  183. package/dist/parser/validate.d.ts.map +1 -0
  184. package/dist/parser/validate.js +817 -0
  185. package/dist/parser/validate.js.map +1 -0
  186. package/dist/parser/yaml.d.ts +326 -0
  187. package/dist/parser/yaml.d.ts.map +1 -0
  188. package/dist/parser/yaml.js +1383 -0
  189. package/dist/parser/yaml.js.map +1 -0
  190. package/dist/ralph/cli-renderer.d.ts +20 -0
  191. package/dist/ralph/cli-renderer.d.ts.map +1 -0
  192. package/dist/ralph/cli-renderer.js +179 -0
  193. package/dist/ralph/cli-renderer.js.map +1 -0
  194. package/dist/ralph/events.d.ts +65 -0
  195. package/dist/ralph/events.d.ts.map +1 -0
  196. package/dist/ralph/events.js +397 -0
  197. package/dist/ralph/events.js.map +1 -0
  198. package/dist/ralph/index.d.ts +8 -0
  199. package/dist/ralph/index.d.ts.map +1 -0
  200. package/dist/ralph/index.js +10 -0
  201. package/dist/ralph/index.js.map +1 -0
  202. package/dist/schema/common.d.ts +46 -0
  203. package/dist/schema/common.d.ts.map +1 -0
  204. package/dist/schema/common.js +71 -0
  205. package/dist/schema/common.js.map +1 -0
  206. package/dist/schema/inbox.d.ts +90 -0
  207. package/dist/schema/inbox.d.ts.map +1 -0
  208. package/dist/schema/inbox.js +30 -0
  209. package/dist/schema/inbox.js.map +1 -0
  210. package/dist/schema/index.d.ts +6 -0
  211. package/dist/schema/index.d.ts.map +1 -0
  212. package/dist/schema/index.js +7 -0
  213. package/dist/schema/index.js.map +1 -0
  214. package/dist/schema/meta.d.ts +762 -0
  215. package/dist/schema/meta.d.ts.map +1 -0
  216. package/dist/schema/meta.js +144 -0
  217. package/dist/schema/meta.js.map +1 -0
  218. package/dist/schema/spec.d.ts +912 -0
  219. package/dist/schema/spec.d.ts.map +1 -0
  220. package/dist/schema/spec.js +104 -0
  221. package/dist/schema/spec.js.map +1 -0
  222. package/dist/schema/task.d.ts +664 -0
  223. package/dist/schema/task.d.ts.map +1 -0
  224. package/dist/schema/task.js +130 -0
  225. package/dist/schema/task.js.map +1 -0
  226. package/dist/sessions/index.d.ts +11 -0
  227. package/dist/sessions/index.d.ts.map +1 -0
  228. package/dist/sessions/index.js +13 -0
  229. package/dist/sessions/index.js.map +1 -0
  230. package/dist/sessions/store.d.ts +144 -0
  231. package/dist/sessions/store.d.ts.map +1 -0
  232. package/dist/sessions/store.js +325 -0
  233. package/dist/sessions/store.js.map +1 -0
  234. package/dist/sessions/types.d.ts +157 -0
  235. package/dist/sessions/types.d.ts.map +1 -0
  236. package/dist/sessions/types.js +90 -0
  237. package/dist/sessions/types.js.map +1 -0
  238. package/dist/strings/errors.d.ts +420 -0
  239. package/dist/strings/errors.d.ts.map +1 -0
  240. package/dist/strings/errors.js +282 -0
  241. package/dist/strings/errors.js.map +1 -0
  242. package/dist/strings/guidance.d.ts +65 -0
  243. package/dist/strings/guidance.d.ts.map +1 -0
  244. package/dist/strings/guidance.js +66 -0
  245. package/dist/strings/guidance.js.map +1 -0
  246. package/dist/strings/index.d.ts +12 -0
  247. package/dist/strings/index.d.ts.map +1 -0
  248. package/dist/strings/index.js +12 -0
  249. package/dist/strings/index.js.map +1 -0
  250. package/dist/strings/labels.d.ts +74 -0
  251. package/dist/strings/labels.d.ts.map +1 -0
  252. package/dist/strings/labels.js +75 -0
  253. package/dist/strings/labels.js.map +1 -0
  254. package/dist/strings/validation.d.ts +126 -0
  255. package/dist/strings/validation.d.ts.map +1 -0
  256. package/dist/strings/validation.js +135 -0
  257. package/dist/strings/validation.js.map +1 -0
  258. package/dist/utils/commit.d.ts +23 -0
  259. package/dist/utils/commit.d.ts.map +1 -0
  260. package/dist/utils/commit.js +67 -0
  261. package/dist/utils/commit.js.map +1 -0
  262. package/dist/utils/git.d.ts +57 -0
  263. package/dist/utils/git.d.ts.map +1 -0
  264. package/dist/utils/git.js +192 -0
  265. package/dist/utils/git.js.map +1 -0
  266. package/dist/utils/grep.d.ts +28 -0
  267. package/dist/utils/grep.d.ts.map +1 -0
  268. package/dist/utils/grep.js +86 -0
  269. package/dist/utils/grep.js.map +1 -0
  270. package/dist/utils/index.d.ts +8 -0
  271. package/dist/utils/index.d.ts.map +1 -0
  272. package/dist/utils/index.js +6 -0
  273. package/dist/utils/index.js.map +1 -0
  274. package/dist/utils/time.d.ts +18 -0
  275. package/dist/utils/time.d.ts.map +1 -0
  276. package/dist/utils/time.js +61 -0
  277. package/dist/utils/time.js.map +1 -0
  278. package/package.json +62 -0
@@ -0,0 +1,113 @@
1
+ /**
2
+ * Alignment tracking between specs and tasks.
3
+ *
4
+ * Provides bidirectional mapping from spec items to implementing tasks,
5
+ * and detects alignment issues like orphaned specs or stale implementation status.
6
+ */
7
+ import type { LoadedSpecItem, LoadedTask, KspecContext } from './yaml.js';
8
+ import type { ReferenceIndex } from './refs.js';
9
+ import type { ImplementationStatus } from '../schema/index.js';
10
+ /**
11
+ * Summary of a spec item's implementation status based on linked tasks
12
+ */
13
+ export interface SpecImplementationSummary {
14
+ specUlid: string;
15
+ specTitle: string;
16
+ currentStatus: ImplementationStatus;
17
+ expectedStatus: ImplementationStatus;
18
+ linkedTasks: LinkedTaskSummary[];
19
+ isAligned: boolean;
20
+ }
21
+ /**
22
+ * Summary of a task linked to a spec item
23
+ */
24
+ export interface LinkedTaskSummary {
25
+ taskUlid: string;
26
+ taskTitle: string;
27
+ taskStatus: string;
28
+ hasNotes: boolean;
29
+ }
30
+ /**
31
+ * Alignment warning
32
+ */
33
+ export interface AlignmentWarning {
34
+ type: 'orphaned_spec' | 'status_mismatch' | 'stale_implementation';
35
+ specUlid?: string;
36
+ specTitle?: string;
37
+ taskUlid?: string;
38
+ message: string;
39
+ }
40
+ /**
41
+ * Index for tracking spec-task alignment.
42
+ * Build once when loading, then query for alignment issues.
43
+ */
44
+ export declare class AlignmentIndex {
45
+ /** spec ULID → task ULIDs that reference it */
46
+ private specToTasks;
47
+ /** task ULID → spec ULID it references */
48
+ private taskToSpec;
49
+ /** All spec items by ULID */
50
+ private specItems;
51
+ /** All tasks by ULID */
52
+ private tasks;
53
+ /**
54
+ * Build index from loaded items
55
+ */
56
+ constructor(tasks: LoadedTask[], items: LoadedSpecItem[]);
57
+ /**
58
+ * Resolve task spec_refs and build the bidirectional index.
59
+ * Must be called with a ReferenceIndex to resolve @refs.
60
+ */
61
+ buildLinks(refIndex: ReferenceIndex): void;
62
+ /**
63
+ * Get tasks that implement a spec item
64
+ */
65
+ getTasksForSpec(specUlid: string): LoadedTask[];
66
+ /**
67
+ * Get the spec item a task implements
68
+ */
69
+ getSpecForTask(taskUlid: string, refIndex: ReferenceIndex): LoadedSpecItem | undefined;
70
+ /**
71
+ * Calculate expected implementation status based on linked task statuses
72
+ */
73
+ calculateExpectedStatus(specUlid: string): ImplementationStatus;
74
+ /**
75
+ * Get implementation summary for a spec item
76
+ */
77
+ getImplementationSummary(specUlid: string): SpecImplementationSummary | undefined;
78
+ /**
79
+ * Find all alignment issues
80
+ */
81
+ findAlignmentWarnings(): AlignmentWarning[];
82
+ /**
83
+ * Get all spec items with their implementation summary
84
+ */
85
+ getAllImplementationSummaries(): SpecImplementationSummary[];
86
+ /**
87
+ * Get stats about alignment
88
+ */
89
+ getStats(): {
90
+ totalSpecs: number;
91
+ specsWithTasks: number;
92
+ alignedSpecs: number;
93
+ orphanedSpecs: number;
94
+ };
95
+ }
96
+ /**
97
+ * Result of syncing spec implementation status
98
+ */
99
+ export interface SyncResult {
100
+ synced: boolean;
101
+ specUlid: string;
102
+ specTitle: string;
103
+ previousStatus: ImplementationStatus;
104
+ newStatus: ImplementationStatus;
105
+ }
106
+ /**
107
+ * Sync a spec item's implementation status based on its linked tasks.
108
+ * Called after task state changes (start, complete, etc.).
109
+ *
110
+ * @returns SyncResult if status changed, null if no change needed or no spec_ref
111
+ */
112
+ export declare function syncSpecImplementationStatus(ctx: KspecContext, task: LoadedTask, allTasks: LoadedTask[], allItems: LoadedSpecItem[], refIndex: ReferenceIndex): Promise<SyncResult | null>;
113
+ //# sourceMappingURL=alignment.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"alignment.d.ts","sourceRoot":"","sources":["../../src/parser/alignment.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAE1E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAChD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAM/D;;GAEG;AACH,MAAM,WAAW,yBAAyB;IACxC,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,oBAAoB,CAAC;IACpC,cAAc,EAAE,oBAAoB,CAAC;IACrC,WAAW,EAAE,iBAAiB,EAAE,CAAC;IACjC,SAAS,EAAE,OAAO,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,eAAe,GAAG,iBAAiB,GAAG,sBAAsB,CAAC;IACnE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;CACjB;AAMD;;;GAGG;AACH,qBAAa,cAAc;IACzB,+CAA+C;IAC/C,OAAO,CAAC,WAAW,CAA+B;IAElD,0CAA0C;IAC1C,OAAO,CAAC,UAAU,CAA6B;IAE/C,6BAA6B;IAC7B,OAAO,CAAC,SAAS,CAAqC;IAEtD,wBAAwB;IACxB,OAAO,CAAC,KAAK,CAAiC;IAE9C;;OAEG;gBACS,KAAK,EAAE,UAAU,EAAE,EAAE,KAAK,EAAE,cAAc,EAAE;IAkBxD;;;OAGG;IACH,UAAU,CAAC,QAAQ,EAAE,cAAc,GAAG,IAAI;IAa1C;;OAEG;IACH,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,UAAU,EAAE;IAO/C;;OAEG;IACH,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,cAAc,GAAG,cAAc,GAAG,SAAS;IAWtF;;OAEG;IACH,uBAAuB,CAAC,QAAQ,EAAE,MAAM,GAAG,oBAAoB;IA4B/D;;OAEG;IACH,wBAAwB,CAAC,QAAQ,EAAE,MAAM,GAAG,yBAAyB,GAAG,SAAS;IA4BjF;;OAEG;IACH,qBAAqB,IAAI,gBAAgB,EAAE;IA4C3C;;OAEG;IACH,6BAA6B,IAAI,yBAAyB,EAAE;IAW5D;;OAEG;IACH,QAAQ,IAAI;QACV,UAAU,EAAE,MAAM,CAAC;QACnB,cAAc,EAAE,MAAM,CAAC;QACvB,YAAY,EAAE,MAAM,CAAC;QACrB,aAAa,EAAE,MAAM,CAAC;KACvB;CAyBF;AAMD;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,OAAO,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,oBAAoB,CAAC;IACrC,SAAS,EAAE,oBAAoB,CAAC;CACjC;AAED;;;;;GAKG;AACH,wBAAsB,4BAA4B,CAChD,GAAG,EAAE,YAAY,EACjB,IAAI,EAAE,UAAU,EAChB,QAAQ,EAAE,UAAU,EAAE,EACtB,QAAQ,EAAE,cAAc,EAAE,EAC1B,QAAQ,EAAE,cAAc,GACvB,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CA6C5B"}
@@ -0,0 +1,261 @@
1
+ /**
2
+ * Alignment tracking between specs and tasks.
3
+ *
4
+ * Provides bidirectional mapping from spec items to implementing tasks,
5
+ * and detects alignment issues like orphaned specs or stale implementation status.
6
+ */
7
+ import { updateSpecItem } from './yaml.js';
8
+ // ============================================================
9
+ // ALIGNMENT INDEX
10
+ // ============================================================
11
+ /**
12
+ * Index for tracking spec-task alignment.
13
+ * Build once when loading, then query for alignment issues.
14
+ */
15
+ export class AlignmentIndex {
16
+ /** spec ULID → task ULIDs that reference it */
17
+ specToTasks = new Map();
18
+ /** task ULID → spec ULID it references */
19
+ taskToSpec = new Map();
20
+ /** All spec items by ULID */
21
+ specItems = new Map();
22
+ /** All tasks by ULID */
23
+ tasks = new Map();
24
+ /**
25
+ * Build index from loaded items
26
+ */
27
+ constructor(tasks, items) {
28
+ // Index spec items
29
+ for (const item of items) {
30
+ this.specItems.set(item._ulid, item);
31
+ this.specToTasks.set(item._ulid, []);
32
+ }
33
+ // Index tasks and build reverse mapping
34
+ for (const task of tasks) {
35
+ this.tasks.set(task._ulid, task);
36
+ if (task.spec_ref) {
37
+ // Store the raw ref - we'll resolve it when needed
38
+ this.taskToSpec.set(task._ulid, task.spec_ref);
39
+ }
40
+ }
41
+ }
42
+ /**
43
+ * Resolve task spec_refs and build the bidirectional index.
44
+ * Must be called with a ReferenceIndex to resolve @refs.
45
+ */
46
+ buildLinks(refIndex) {
47
+ for (const [taskUlid, specRef] of this.taskToSpec) {
48
+ const result = refIndex.resolve(specRef);
49
+ if (result.ok) {
50
+ const specUlid = result.ulid;
51
+ const existing = this.specToTasks.get(specUlid);
52
+ if (existing) {
53
+ existing.push(taskUlid);
54
+ }
55
+ }
56
+ }
57
+ }
58
+ /**
59
+ * Get tasks that implement a spec item
60
+ */
61
+ getTasksForSpec(specUlid) {
62
+ const taskUlids = this.specToTasks.get(specUlid) || [];
63
+ return taskUlids
64
+ .map(ulid => this.tasks.get(ulid))
65
+ .filter((t) => t !== undefined);
66
+ }
67
+ /**
68
+ * Get the spec item a task implements
69
+ */
70
+ getSpecForTask(taskUlid, refIndex) {
71
+ const specRef = this.taskToSpec.get(taskUlid);
72
+ if (!specRef)
73
+ return undefined;
74
+ const result = refIndex.resolve(specRef);
75
+ if (result.ok) {
76
+ return this.specItems.get(result.ulid);
77
+ }
78
+ return undefined;
79
+ }
80
+ /**
81
+ * Calculate expected implementation status based on linked task statuses
82
+ */
83
+ calculateExpectedStatus(specUlid) {
84
+ const taskUlids = this.specToTasks.get(specUlid) || [];
85
+ if (taskUlids.length === 0) {
86
+ return 'not_started';
87
+ }
88
+ const tasks = taskUlids
89
+ .map(ulid => this.tasks.get(ulid))
90
+ .filter((t) => t !== undefined);
91
+ if (tasks.length === 0) {
92
+ return 'not_started';
93
+ }
94
+ // Check task statuses
95
+ const hasInProgress = tasks.some(t => t.status === 'in_progress');
96
+ const allCompleted = tasks.every(t => t.status === 'completed');
97
+ const someCompleted = tasks.some(t => t.status === 'completed');
98
+ if (allCompleted) {
99
+ return 'implemented';
100
+ }
101
+ if (hasInProgress || someCompleted) {
102
+ return 'in_progress';
103
+ }
104
+ return 'not_started';
105
+ }
106
+ /**
107
+ * Get implementation summary for a spec item
108
+ */
109
+ getImplementationSummary(specUlid) {
110
+ const spec = this.specItems.get(specUlid);
111
+ if (!spec)
112
+ return undefined;
113
+ const taskUlids = this.specToTasks.get(specUlid) || [];
114
+ const linkedTasks = taskUlids
115
+ .map(ulid => this.tasks.get(ulid))
116
+ .filter((t) => t !== undefined)
117
+ .map(t => ({
118
+ taskUlid: t._ulid,
119
+ taskTitle: t.title,
120
+ taskStatus: t.status,
121
+ hasNotes: t.notes.length > 0,
122
+ }));
123
+ const currentStatus = spec.status?.implementation || 'not_started';
124
+ const expectedStatus = this.calculateExpectedStatus(specUlid);
125
+ return {
126
+ specUlid,
127
+ specTitle: spec.title,
128
+ currentStatus,
129
+ expectedStatus,
130
+ linkedTasks,
131
+ isAligned: currentStatus === expectedStatus,
132
+ };
133
+ }
134
+ /**
135
+ * Find all alignment issues
136
+ */
137
+ findAlignmentWarnings() {
138
+ const warnings = [];
139
+ // Check each spec item
140
+ for (const [specUlid, spec] of this.specItems) {
141
+ const taskUlids = this.specToTasks.get(specUlid) || [];
142
+ const currentStatus = spec.status?.implementation || 'not_started';
143
+ const expectedStatus = this.calculateExpectedStatus(specUlid);
144
+ // Orphaned spec (no tasks)
145
+ if (taskUlids.length === 0 && currentStatus === 'not_started') {
146
+ warnings.push({
147
+ type: 'orphaned_spec',
148
+ specUlid,
149
+ specTitle: spec.title,
150
+ message: `Spec item "${spec.title}" has no implementing tasks`,
151
+ });
152
+ }
153
+ // Status mismatch
154
+ if (currentStatus !== expectedStatus) {
155
+ warnings.push({
156
+ type: 'status_mismatch',
157
+ specUlid,
158
+ specTitle: spec.title,
159
+ message: `Spec "${spec.title}" status is "${currentStatus}" but should be "${expectedStatus}" based on task progress`,
160
+ });
161
+ }
162
+ }
163
+ // Check completed tasks with stale spec status
164
+ for (const [taskUlid, task] of this.tasks) {
165
+ if (task.status === 'completed' && task.spec_ref) {
166
+ const specRef = this.taskToSpec.get(taskUlid);
167
+ if (specRef) {
168
+ // Note: We already checked this via spec iteration above
169
+ // But this provides task-centric context
170
+ }
171
+ }
172
+ }
173
+ return warnings;
174
+ }
175
+ /**
176
+ * Get all spec items with their implementation summary
177
+ */
178
+ getAllImplementationSummaries() {
179
+ const summaries = [];
180
+ for (const specUlid of this.specItems.keys()) {
181
+ const summary = this.getImplementationSummary(specUlid);
182
+ if (summary) {
183
+ summaries.push(summary);
184
+ }
185
+ }
186
+ return summaries;
187
+ }
188
+ /**
189
+ * Get stats about alignment
190
+ */
191
+ getStats() {
192
+ let specsWithTasks = 0;
193
+ let alignedSpecs = 0;
194
+ let orphanedSpecs = 0;
195
+ for (const specUlid of this.specItems.keys()) {
196
+ const taskUlids = this.specToTasks.get(specUlid) || [];
197
+ if (taskUlids.length > 0) {
198
+ specsWithTasks++;
199
+ const summary = this.getImplementationSummary(specUlid);
200
+ if (summary?.isAligned) {
201
+ alignedSpecs++;
202
+ }
203
+ }
204
+ else {
205
+ orphanedSpecs++;
206
+ }
207
+ }
208
+ return {
209
+ totalSpecs: this.specItems.size,
210
+ specsWithTasks,
211
+ alignedSpecs,
212
+ orphanedSpecs,
213
+ };
214
+ }
215
+ }
216
+ /**
217
+ * Sync a spec item's implementation status based on its linked tasks.
218
+ * Called after task state changes (start, complete, etc.).
219
+ *
220
+ * @returns SyncResult if status changed, null if no change needed or no spec_ref
221
+ */
222
+ export async function syncSpecImplementationStatus(ctx, task, allTasks, allItems, refIndex) {
223
+ // Skip if task has no spec_ref
224
+ if (!task.spec_ref) {
225
+ return null;
226
+ }
227
+ // Resolve the spec reference
228
+ const result = refIndex.resolve(task.spec_ref);
229
+ if (!result.ok) {
230
+ return null;
231
+ }
232
+ // Find the spec item
233
+ const specItem = allItems.find(item => item._ulid === result.ulid);
234
+ if (!specItem) {
235
+ return null;
236
+ }
237
+ // Build alignment index to calculate expected status
238
+ const alignmentIndex = new AlignmentIndex(allTasks, allItems);
239
+ alignmentIndex.buildLinks(refIndex);
240
+ const expectedStatus = alignmentIndex.calculateExpectedStatus(specItem._ulid);
241
+ const currentStatus = specItem.status?.implementation || 'not_started';
242
+ // No change needed
243
+ if (currentStatus === expectedStatus) {
244
+ return null;
245
+ }
246
+ // Update the spec item
247
+ await updateSpecItem(ctx, specItem, {
248
+ status: {
249
+ maturity: specItem.status?.maturity || 'draft',
250
+ implementation: expectedStatus,
251
+ },
252
+ });
253
+ return {
254
+ synced: true,
255
+ specUlid: specItem._ulid,
256
+ specTitle: specItem.title,
257
+ previousStatus: currentStatus,
258
+ newStatus: expectedStatus,
259
+ };
260
+ }
261
+ //# sourceMappingURL=alignment.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"alignment.js","sourceRoot":"","sources":["../../src/parser/alignment.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,81 @@
1
+ /**
2
+ * Task automation assessment logic.
3
+ *
4
+ * Provides criteria checking for task automation eligibility.
5
+ * AC: @tasks-assess-automation
6
+ */
7
+ import type { LoadedTask, LoadedSpecItem } from './yaml.js';
8
+ import type { ReferenceIndex } from './refs.js';
9
+ /**
10
+ * Criterion check result
11
+ */
12
+ export interface CriterionResult {
13
+ pass: boolean;
14
+ /** For skipped criteria (neutral) */
15
+ skipped?: boolean;
16
+ /** Additional context */
17
+ detail?: string;
18
+ }
19
+ /**
20
+ * Full assessment result for a task
21
+ * AC: @tasks-assess-automation ac-3, ac-4
22
+ */
23
+ export interface TaskAssessment {
24
+ taskRef: string;
25
+ taskUlid: string;
26
+ taskTitle: string;
27
+ taskType: string;
28
+ criteria: {
29
+ has_spec_ref: CriterionResult & {
30
+ spec_ref?: string;
31
+ };
32
+ spec_has_acs: CriterionResult & {
33
+ ac_count?: number;
34
+ };
35
+ not_spike: CriterionResult;
36
+ };
37
+ recommendation: 'review_for_eligible' | 'needs_review' | 'manual_only';
38
+ reason: string;
39
+ }
40
+ /**
41
+ * Assessment summary counts
42
+ * AC: @tasks-assess-automation ac-5, ac-25
43
+ */
44
+ export interface AssessmentSummary {
45
+ review_for_eligible: number;
46
+ needs_review: number;
47
+ manual_only: number;
48
+ total: number;
49
+ }
50
+ /**
51
+ * Assess a single task's automation eligibility
52
+ * AC: @tasks-assess-automation ac-8 through ac-16
53
+ */
54
+ export declare function assessTask(task: LoadedTask, index: ReferenceIndex, items: LoadedSpecItem[]): TaskAssessment;
55
+ /**
56
+ * Filter tasks for assessment
57
+ * AC: @tasks-assess-automation ac-1, ac-2, ac-27, ac-28
58
+ */
59
+ export declare function filterTasksForAssessment(tasks: LoadedTask[], options: {
60
+ all?: boolean;
61
+ taskRef?: string;
62
+ }, index: ReferenceIndex): LoadedTask[];
63
+ /**
64
+ * Compute summary counts from assessments
65
+ * AC: @tasks-assess-automation ac-5, ac-25
66
+ */
67
+ export declare function computeSummary(assessments: TaskAssessment[]): AssessmentSummary;
68
+ /**
69
+ * Determine what changes auto mode would make
70
+ * AC: @tasks-assess-automation ac-17, ac-18, ac-21
71
+ */
72
+ export interface AutoModeChange {
73
+ taskRef: string;
74
+ taskUlid: string;
75
+ taskTitle: string;
76
+ action: 'set_manual_only' | 'set_needs_review' | 'no_change';
77
+ newStatus?: 'manual_only' | 'needs_review';
78
+ reason: string;
79
+ }
80
+ export declare function computeAutoModeChanges(assessments: TaskAssessment[]): AutoModeChange[];
81
+ //# sourceMappingURL=assess.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"assess.d.ts","sourceRoot":"","sources":["../../src/parser/assess.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAC5D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAMhD;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,OAAO,CAAC;IACd,qCAAqC;IACrC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,yBAAyB;IACzB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE;QACR,YAAY,EAAE,eAAe,GAAG;YAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC;QACtD,YAAY,EAAE,eAAe,GAAG;YAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC;QACtD,SAAS,EAAE,eAAe,CAAC;KAC5B,CAAC;IACF,cAAc,EAAE,qBAAqB,GAAG,cAAc,GAAG,aAAa,CAAC;IACvE,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;GAGG;AACH,MAAM,WAAW,iBAAiB;IAChC,mBAAmB,EAAE,MAAM,CAAC;IAC5B,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;CACf;AAMD;;;GAGG;AACH,wBAAgB,UAAU,CACxB,IAAI,EAAE,UAAU,EAChB,KAAK,EAAE,cAAc,EACrB,KAAK,EAAE,cAAc,EAAE,GACtB,cAAc,CAmChB;AA8GD;;;GAGG;AACH,wBAAgB,wBAAwB,CACtC,KAAK,EAAE,UAAU,EAAE,EACnB,OAAO,EAAE;IAAE,GAAG,CAAC,EAAE,OAAO,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,EAC5C,KAAK,EAAE,cAAc,GACpB,UAAU,EAAE,CAsBd;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,WAAW,EAAE,cAAc,EAAE,GAAG,iBAAiB,CAa/E;AAED;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,iBAAiB,GAAG,kBAAkB,GAAG,WAAW,CAAC;IAC7D,SAAS,CAAC,EAAE,aAAa,GAAG,cAAc,CAAC;IAC3C,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,wBAAgB,sBAAsB,CAAC,WAAW,EAAE,cAAc,EAAE,GAAG,cAAc,EAAE,CAmCtF"}
@@ -0,0 +1,197 @@
1
+ /**
2
+ * Task automation assessment logic.
3
+ *
4
+ * Provides criteria checking for task automation eligibility.
5
+ * AC: @tasks-assess-automation
6
+ */
7
+ // ============================================================
8
+ // ASSESSMENT LOGIC
9
+ // ============================================================
10
+ /**
11
+ * Assess a single task's automation eligibility
12
+ * AC: @tasks-assess-automation ac-8 through ac-16
13
+ */
14
+ export function assessTask(task, index, items) {
15
+ const taskRef = task.slugs.length > 0 ? `@${task.slugs[0]}` : `@${task._ulid.slice(0, 8)}`;
16
+ const taskType = task.type || 'task';
17
+ // AC: @tasks-assess-automation ac-8, ac-9 - Check has_spec_ref
18
+ const hasSpecRefResult = checkHasSpecRef(task, index);
19
+ // AC: @tasks-assess-automation ac-10, ac-11 - Check spec_has_acs
20
+ const specHasAcsResult = checkSpecHasAcs(task, index, items);
21
+ // AC: @tasks-assess-automation ac-12, ac-13 - Check not_spike
22
+ const notSpikeResult = checkNotSpike(task);
23
+ // Compute recommendation
24
+ // AC: @tasks-assess-automation ac-14, ac-15, ac-16
25
+ const { recommendation, reason } = computeRecommendation(hasSpecRefResult, specHasAcsResult, notSpikeResult, taskType);
26
+ return {
27
+ taskRef,
28
+ taskUlid: task._ulid,
29
+ taskTitle: task.title,
30
+ taskType,
31
+ criteria: {
32
+ has_spec_ref: hasSpecRefResult,
33
+ spec_has_acs: specHasAcsResult,
34
+ not_spike: notSpikeResult,
35
+ },
36
+ recommendation,
37
+ reason,
38
+ };
39
+ }
40
+ /**
41
+ * Check if task has spec_ref pointing to resolvable spec
42
+ * AC: @tasks-assess-automation ac-8, ac-9
43
+ */
44
+ function checkHasSpecRef(task, index) {
45
+ if (!task.spec_ref) {
46
+ return { pass: false, detail: 'missing' };
47
+ }
48
+ const resolved = index.resolve(task.spec_ref);
49
+ if (!resolved.ok) {
50
+ return { pass: false, detail: 'unresolvable', spec_ref: task.spec_ref };
51
+ }
52
+ return { pass: true, spec_ref: task.spec_ref };
53
+ }
54
+ /**
55
+ * Check if linked spec has acceptance criteria
56
+ * AC: @tasks-assess-automation ac-10, ac-11
57
+ */
58
+ function checkSpecHasAcs(task, index, items) {
59
+ // AC: @tasks-assess-automation ac-11 - skipped if no spec_ref
60
+ if (!task.spec_ref) {
61
+ return { pass: false, skipped: true, detail: 'no spec to check' };
62
+ }
63
+ const resolved = index.resolve(task.spec_ref);
64
+ if (!resolved.ok) {
65
+ return { pass: false, skipped: true, detail: 'spec not resolvable' };
66
+ }
67
+ // Find the spec item
68
+ const specItem = items.find(i => i._ulid === resolved.ulid);
69
+ if (!specItem) {
70
+ return { pass: false, skipped: true, detail: 'spec not found in items' };
71
+ }
72
+ const acCount = specItem.acceptance_criteria?.length || 0;
73
+ if (acCount === 0) {
74
+ return { pass: false, ac_count: 0, detail: 'spec has no acceptance criteria' };
75
+ }
76
+ return { pass: true, ac_count: acCount };
77
+ }
78
+ /**
79
+ * Check if task type is not spike
80
+ * AC: @tasks-assess-automation ac-12, ac-13
81
+ */
82
+ function checkNotSpike(task) {
83
+ const taskType = task.type || 'task';
84
+ if (taskType === 'spike') {
85
+ return { pass: false, detail: 'type: spike' };
86
+ }
87
+ return { pass: true, detail: `type: ${taskType}` };
88
+ }
89
+ /**
90
+ * Compute recommendation based on criteria results
91
+ * AC: @tasks-assess-automation ac-14, ac-15, ac-16
92
+ */
93
+ function computeRecommendation(hasSpecRef, specHasAcs, notSpike, taskType) {
94
+ // AC: @tasks-assess-automation ac-14 - Spikes are always manual_only
95
+ if (!notSpike.pass) {
96
+ return {
97
+ recommendation: 'manual_only',
98
+ reason: 'Spikes output knowledge, not automatable code',
99
+ };
100
+ }
101
+ // AC: @tasks-assess-automation ac-15 - Missing spec or no ACs → needs_review
102
+ const reasons = [];
103
+ if (!hasSpecRef.pass) {
104
+ reasons.push('missing spec_ref');
105
+ }
106
+ if (!specHasAcs.pass && !specHasAcs.skipped) {
107
+ reasons.push('spec has no acceptance criteria');
108
+ }
109
+ else if (specHasAcs.skipped && !hasSpecRef.pass) {
110
+ // Only add this if spec is missing (not if spec is unresolvable)
111
+ }
112
+ if (reasons.length > 0) {
113
+ return {
114
+ recommendation: 'needs_review',
115
+ reason: reasons.join(', '),
116
+ };
117
+ }
118
+ // AC: @tasks-assess-automation ac-16 - All criteria pass → review_for_eligible
119
+ return {
120
+ recommendation: 'review_for_eligible',
121
+ reason: 'Criteria pass - verify spec is appropriate and ACs are adequate',
122
+ };
123
+ }
124
+ /**
125
+ * Filter tasks for assessment
126
+ * AC: @tasks-assess-automation ac-1, ac-2, ac-27, ac-28
127
+ */
128
+ export function filterTasksForAssessment(tasks, options, index) {
129
+ let filtered = tasks;
130
+ // AC: @tasks-assess-automation ac-28 - Exclude non-pending tasks
131
+ filtered = filtered.filter(t => t.status === 'pending');
132
+ // AC: @tasks-assess-automation ac-6 - Single task assessment
133
+ if (options.taskRef) {
134
+ const resolved = index.resolve(options.taskRef);
135
+ if (!resolved.ok) {
136
+ return []; // Will be handled by caller
137
+ }
138
+ filtered = filtered.filter(t => t._ulid === resolved.ulid);
139
+ return filtered;
140
+ }
141
+ // AC: @tasks-assess-automation ac-1, ac-27 - Filter by unassessed unless --all
142
+ if (!options.all) {
143
+ filtered = filtered.filter(t => !t.automation);
144
+ }
145
+ return filtered;
146
+ }
147
+ /**
148
+ * Compute summary counts from assessments
149
+ * AC: @tasks-assess-automation ac-5, ac-25
150
+ */
151
+ export function computeSummary(assessments) {
152
+ const summary = {
153
+ review_for_eligible: 0,
154
+ needs_review: 0,
155
+ manual_only: 0,
156
+ total: assessments.length,
157
+ };
158
+ for (const assessment of assessments) {
159
+ summary[assessment.recommendation]++;
160
+ }
161
+ return summary;
162
+ }
163
+ export function computeAutoModeChanges(assessments) {
164
+ return assessments.map(assessment => {
165
+ // AC: @tasks-assess-automation ac-17 - Spikes → manual_only
166
+ if (assessment.recommendation === 'manual_only') {
167
+ return {
168
+ taskRef: assessment.taskRef,
169
+ taskUlid: assessment.taskUlid,
170
+ taskTitle: assessment.taskTitle,
171
+ action: 'set_manual_only',
172
+ newStatus: 'manual_only',
173
+ reason: assessment.reason,
174
+ };
175
+ }
176
+ // AC: @tasks-assess-automation ac-17 - Missing criteria → needs_review
177
+ if (assessment.recommendation === 'needs_review') {
178
+ return {
179
+ taskRef: assessment.taskRef,
180
+ taskUlid: assessment.taskUlid,
181
+ taskTitle: assessment.taskTitle,
182
+ action: 'set_needs_review',
183
+ newStatus: 'needs_review',
184
+ reason: assessment.reason,
185
+ };
186
+ }
187
+ // AC: @tasks-assess-automation ac-18, ac-21 - review_for_eligible → no change
188
+ return {
189
+ taskRef: assessment.taskRef,
190
+ taskUlid: assessment.taskUlid,
191
+ taskTitle: assessment.taskTitle,
192
+ action: 'no_change',
193
+ reason: 'Passes criteria - requires agent/human review to mark eligible',
194
+ };
195
+ });
196
+ }
197
+ //# sourceMappingURL=assess.js.map