chati-dev 1.3.3 → 2.0.1

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 (215) hide show
  1. package/README.md +7 -6
  2. package/framework/agents/build/dev.md +343 -0
  3. package/framework/agents/clarity/architect.md +113 -0
  4. package/framework/agents/clarity/brief.md +183 -0
  5. package/framework/agents/clarity/brownfield-wu.md +182 -0
  6. package/framework/agents/clarity/detail.md +111 -0
  7. package/framework/agents/clarity/greenfield-wu.md +154 -0
  8. package/framework/agents/clarity/phases.md +1 -0
  9. package/framework/agents/clarity/tasks.md +1 -0
  10. package/framework/agents/clarity/ux.md +113 -0
  11. package/framework/agents/deploy/devops.md +1 -0
  12. package/framework/agents/quality/qa-implementation.md +1 -0
  13. package/framework/agents/quality/qa-planning.md +1 -0
  14. package/framework/config.yaml +3 -3
  15. package/framework/constitution.md +58 -1
  16. package/framework/context/governance.md +37 -0
  17. package/framework/context/protocols.md +34 -0
  18. package/framework/context/quality.md +27 -0
  19. package/framework/context/root.md +24 -0
  20. package/framework/data/entity-registry.yaml +1 -1
  21. package/framework/domains/agents/architect.yaml +51 -0
  22. package/framework/domains/agents/brief.yaml +47 -0
  23. package/framework/domains/agents/brownfield-wu.yaml +49 -0
  24. package/framework/domains/agents/detail.yaml +47 -0
  25. package/framework/domains/agents/dev.yaml +49 -0
  26. package/framework/domains/agents/devops.yaml +43 -0
  27. package/framework/domains/agents/greenfield-wu.yaml +47 -0
  28. package/framework/domains/agents/orchestrator.yaml +49 -0
  29. package/framework/domains/agents/phases.yaml +47 -0
  30. package/framework/domains/agents/qa-implementation.yaml +43 -0
  31. package/framework/domains/agents/qa-planning.yaml +44 -0
  32. package/framework/domains/agents/tasks.yaml +48 -0
  33. package/framework/domains/agents/ux.yaml +50 -0
  34. package/framework/domains/constitution.yaml +77 -0
  35. package/framework/domains/global.yaml +64 -0
  36. package/framework/domains/workflows/brownfield-discovery.yaml +16 -0
  37. package/framework/domains/workflows/brownfield-fullstack.yaml +26 -0
  38. package/framework/domains/workflows/brownfield-service.yaml +22 -0
  39. package/framework/domains/workflows/brownfield-ui.yaml +22 -0
  40. package/framework/domains/workflows/greenfield-fullstack.yaml +26 -0
  41. package/framework/hooks/constitution-guard.js +101 -0
  42. package/framework/hooks/mode-governance.js +92 -0
  43. package/framework/hooks/model-governance.js +76 -0
  44. package/framework/hooks/prism-engine.js +89 -0
  45. package/framework/hooks/session-digest.js +60 -0
  46. package/framework/hooks/settings.json +44 -0
  47. package/framework/i18n/en.yaml +3 -3
  48. package/framework/i18n/es.yaml +3 -3
  49. package/framework/i18n/fr.yaml +3 -3
  50. package/framework/i18n/pt.yaml +3 -3
  51. package/framework/intelligence/context-engine.md +2 -2
  52. package/framework/intelligence/decision-engine.md +1 -1
  53. package/framework/migrations/v1.4-to-v2.0.yaml +167 -0
  54. package/framework/migrations/v2.0-to-v2.0.1.yaml +132 -0
  55. package/framework/orchestrator/chati.md +350 -7
  56. package/framework/schemas/session.schema.json +15 -0
  57. package/framework/tasks/architect-api-design.md +63 -0
  58. package/framework/tasks/architect-consolidate.md +47 -0
  59. package/framework/tasks/architect-db-design.md +73 -0
  60. package/framework/tasks/architect-design.md +95 -0
  61. package/framework/tasks/architect-security-review.md +62 -0
  62. package/framework/tasks/architect-stack-selection.md +53 -0
  63. package/framework/tasks/brief-consolidate.md +249 -0
  64. package/framework/tasks/brief-constraint-identify.md +277 -0
  65. package/framework/tasks/brief-extract-requirements.md +339 -0
  66. package/framework/tasks/brief-stakeholder-map.md +176 -0
  67. package/framework/tasks/brief-validate-completeness.md +121 -0
  68. package/framework/tasks/brownfield-wu-architecture-map.md +394 -0
  69. package/framework/tasks/brownfield-wu-deep-discovery.md +312 -0
  70. package/framework/tasks/brownfield-wu-dependency-scan.md +359 -0
  71. package/framework/tasks/brownfield-wu-migration-plan.md +483 -0
  72. package/framework/tasks/brownfield-wu-report.md +325 -0
  73. package/framework/tasks/brownfield-wu-risk-assess.md +424 -0
  74. package/framework/tasks/detail-acceptance-criteria.md +372 -0
  75. package/framework/tasks/detail-consolidate.md +138 -0
  76. package/framework/tasks/detail-edge-case-analysis.md +300 -0
  77. package/framework/tasks/detail-expand-prd.md +389 -0
  78. package/framework/tasks/detail-nfr-extraction.md +223 -0
  79. package/framework/tasks/dev-code-review.md +404 -0
  80. package/framework/tasks/dev-consolidate.md +543 -0
  81. package/framework/tasks/dev-debug.md +322 -0
  82. package/framework/tasks/dev-implement.md +252 -0
  83. package/framework/tasks/dev-iterate.md +411 -0
  84. package/framework/tasks/dev-pr-prepare.md +497 -0
  85. package/framework/tasks/dev-refactor.md +342 -0
  86. package/framework/tasks/dev-test-write.md +306 -0
  87. package/framework/tasks/devops-ci-setup.md +412 -0
  88. package/framework/tasks/devops-consolidate.md +712 -0
  89. package/framework/tasks/devops-deploy-config.md +598 -0
  90. package/framework/tasks/devops-monitoring-setup.md +658 -0
  91. package/framework/tasks/devops-release-prepare.md +673 -0
  92. package/framework/tasks/greenfield-wu-analyze-empty.md +169 -0
  93. package/framework/tasks/greenfield-wu-report.md +266 -0
  94. package/framework/tasks/greenfield-wu-scaffold-detection.md +203 -0
  95. package/framework/tasks/greenfield-wu-tech-stack-assess.md +255 -0
  96. package/framework/tasks/orchestrator-deviation.md +260 -0
  97. package/framework/tasks/orchestrator-escalate.md +276 -0
  98. package/framework/tasks/orchestrator-handoff.md +243 -0
  99. package/framework/tasks/orchestrator-health.md +372 -0
  100. package/framework/tasks/orchestrator-mode-switch.md +262 -0
  101. package/framework/tasks/orchestrator-resume.md +189 -0
  102. package/framework/tasks/orchestrator-route.md +169 -0
  103. package/framework/tasks/orchestrator-spawn-terminal.md +358 -0
  104. package/framework/tasks/orchestrator-status.md +260 -0
  105. package/framework/tasks/orchestrator-suggest-mode.md +372 -0
  106. package/framework/tasks/phases-breakdown.md +91 -0
  107. package/framework/tasks/phases-dependency-mapping.md +67 -0
  108. package/framework/tasks/phases-mvp-scoping.md +94 -0
  109. package/framework/tasks/qa-impl-consolidate.md +522 -0
  110. package/framework/tasks/qa-impl-performance-test.md +487 -0
  111. package/framework/tasks/qa-impl-regression-check.md +413 -0
  112. package/framework/tasks/qa-impl-sast-scan.md +402 -0
  113. package/framework/tasks/qa-impl-test-execute.md +344 -0
  114. package/framework/tasks/qa-impl-verdict.md +339 -0
  115. package/framework/tasks/qa-planning-consolidate.md +309 -0
  116. package/framework/tasks/qa-planning-coverage-plan.md +338 -0
  117. package/framework/tasks/qa-planning-gate-define.md +339 -0
  118. package/framework/tasks/qa-planning-risk-matrix.md +631 -0
  119. package/framework/tasks/qa-planning-test-strategy.md +217 -0
  120. package/framework/tasks/tasks-acceptance-write.md +75 -0
  121. package/framework/tasks/tasks-consolidate.md +57 -0
  122. package/framework/tasks/tasks-decompose.md +80 -0
  123. package/framework/tasks/tasks-estimate.md +66 -0
  124. package/framework/tasks/ux-a11y-check.md +49 -0
  125. package/framework/tasks/ux-component-map.md +55 -0
  126. package/framework/tasks/ux-consolidate.md +46 -0
  127. package/framework/tasks/ux-user-flow.md +46 -0
  128. package/framework/tasks/ux-wireframe.md +76 -0
  129. package/package.json +1 -1
  130. package/scripts/bundle-framework.js +2 -0
  131. package/scripts/changelog-generator.js +222 -0
  132. package/scripts/codebase-mapper.js +728 -0
  133. package/scripts/commit-message-generator.js +167 -0
  134. package/scripts/coverage-analyzer.js +260 -0
  135. package/scripts/dependency-analyzer.js +280 -0
  136. package/scripts/framework-analyzer.js +308 -0
  137. package/scripts/generate-constitution-domain.js +253 -0
  138. package/scripts/health-check.js +481 -0
  139. package/scripts/ide-sync.js +327 -0
  140. package/scripts/performance-analyzer.js +325 -0
  141. package/scripts/plan-tracker.js +278 -0
  142. package/scripts/populate-entity-registry.js +481 -0
  143. package/scripts/pr-review.js +317 -0
  144. package/scripts/rollback-manager.js +310 -0
  145. package/scripts/stuck-detector.js +343 -0
  146. package/scripts/test-quality-assessment.js +257 -0
  147. package/scripts/validate-agents.js +367 -0
  148. package/scripts/validate-tasks.js +465 -0
  149. package/src/autonomy/autonomous-gate.js +293 -0
  150. package/src/autonomy/index.js +51 -0
  151. package/src/autonomy/mode-manager.js +225 -0
  152. package/src/autonomy/mode-suggester.js +283 -0
  153. package/src/autonomy/progress-reporter.js +268 -0
  154. package/src/autonomy/safety-net.js +320 -0
  155. package/src/context/bracket-tracker.js +79 -0
  156. package/src/context/domain-loader.js +107 -0
  157. package/src/context/engine.js +144 -0
  158. package/src/context/formatter.js +184 -0
  159. package/src/context/index.js +4 -0
  160. package/src/context/layers/l0-constitution.js +28 -0
  161. package/src/context/layers/l1-global.js +37 -0
  162. package/src/context/layers/l2-agent.js +39 -0
  163. package/src/context/layers/l3-workflow.js +42 -0
  164. package/src/context/layers/l4-task.js +24 -0
  165. package/src/decision/analyzer.js +167 -0
  166. package/src/decision/engine.js +270 -0
  167. package/src/decision/index.js +38 -0
  168. package/src/decision/registry-healer.js +450 -0
  169. package/src/decision/registry-updater.js +330 -0
  170. package/src/gates/circuit-breaker.js +119 -0
  171. package/src/gates/g1-planning-complete.js +153 -0
  172. package/src/gates/g2-qa-planning.js +153 -0
  173. package/src/gates/g3-implementation.js +188 -0
  174. package/src/gates/g4-qa-implementation.js +207 -0
  175. package/src/gates/g5-deploy-ready.js +180 -0
  176. package/src/gates/gate-base.js +144 -0
  177. package/src/gates/index.js +46 -0
  178. package/src/installer/brownfield-upgrader.js +249 -0
  179. package/src/installer/core.js +55 -3
  180. package/src/installer/file-hasher.js +51 -0
  181. package/src/installer/manifest.js +117 -0
  182. package/src/installer/templates.js +17 -15
  183. package/src/installer/transaction.js +229 -0
  184. package/src/installer/validator.js +18 -1
  185. package/src/intelligence/registry-manager.js +2 -2
  186. package/src/memory/agent-memory.js +255 -0
  187. package/src/memory/gotchas-injector.js +72 -0
  188. package/src/memory/gotchas.js +361 -0
  189. package/src/memory/index.js +35 -0
  190. package/src/memory/search.js +233 -0
  191. package/src/memory/session-digest.js +239 -0
  192. package/src/merger/env-merger.js +112 -0
  193. package/src/merger/index.js +56 -0
  194. package/src/merger/replace-merger.js +51 -0
  195. package/src/merger/yaml-merger.js +127 -0
  196. package/src/orchestrator/agent-selector.js +285 -0
  197. package/src/orchestrator/deviation-handler.js +350 -0
  198. package/src/orchestrator/handoff-engine.js +271 -0
  199. package/src/orchestrator/index.js +67 -0
  200. package/src/orchestrator/intent-classifier.js +264 -0
  201. package/src/orchestrator/pipeline-manager.js +492 -0
  202. package/src/orchestrator/pipeline-state.js +223 -0
  203. package/src/orchestrator/session-manager.js +409 -0
  204. package/src/tasks/executor.js +195 -0
  205. package/src/tasks/handoff.js +226 -0
  206. package/src/tasks/index.js +4 -0
  207. package/src/tasks/loader.js +210 -0
  208. package/src/tasks/router.js +182 -0
  209. package/src/terminal/collector.js +216 -0
  210. package/src/terminal/index.js +30 -0
  211. package/src/terminal/isolation.js +129 -0
  212. package/src/terminal/monitor.js +277 -0
  213. package/src/terminal/spawner.js +269 -0
  214. package/src/upgrade/checker.js +1 -1
  215. package/src/wizard/i18n.js +3 -3
@@ -0,0 +1,409 @@
1
+ /**
2
+ * Session Manager — Complete session lifecycle management.
3
+ *
4
+ * Manages session initialization, state updates, mode transitions,
5
+ * agent completions, and session validation for the chati.dev pipeline.
6
+ */
7
+
8
+ import yaml from 'js-yaml';
9
+ import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs';
10
+ import { join, dirname } from 'path';
11
+
12
+ const SESSION_FILE = '.chati/session.yaml';
13
+
14
+ /**
15
+ * Default session template.
16
+ */
17
+ const DEFAULT_SESSION = {
18
+ version: '1.0',
19
+ mode: 'clarity',
20
+ language: 'en',
21
+ project_type: 'greenfield',
22
+ started_at: null,
23
+ current_agent: '',
24
+ pipeline_position: 0,
25
+ execution_mode: 'interactive',
26
+ user_level: 'auto',
27
+ user_level_confidence: 0.0,
28
+ ides: [],
29
+ mcps: [],
30
+ completed_agents: [],
31
+ agent_results: {},
32
+ mode_transitions: [],
33
+ deviations: [],
34
+ agents: {
35
+ 'greenfield-wu': { status: 'pending', score: 0, criteria_count: 0, completed_at: null },
36
+ 'brownfield-wu': { status: 'pending', score: 0, criteria_count: 0, completed_at: null },
37
+ 'brief': { status: 'pending', score: 0, criteria_count: 0, completed_at: null },
38
+ 'detail': { status: 'pending', score: 0, criteria_count: 0, completed_at: null },
39
+ 'architect': { status: 'pending', score: 0, criteria_count: 0, completed_at: null },
40
+ 'ux': { status: 'pending', score: 0, criteria_count: 0, completed_at: null },
41
+ 'phases': { status: 'pending', score: 0, criteria_count: 0, completed_at: null },
42
+ 'tasks': { status: 'pending', score: 0, criteria_count: 0, completed_at: null },
43
+ 'qa-planning': { status: 'pending', score: 0, criteria_count: 0, completed_at: null },
44
+ 'dev': { status: 'pending', score: 0, criteria_count: 0, completed_at: null },
45
+ 'qa-implementation': { status: 'pending', score: 0, criteria_count: 0, completed_at: null },
46
+ 'devops': { status: 'pending', score: 0, criteria_count: 0, completed_at: null },
47
+ },
48
+ backlog: [],
49
+ last_handoff: '',
50
+ };
51
+
52
+ /**
53
+ * Initialize a new session.
54
+ * @param {string} projectDir
55
+ * @param {object} options - { mode, isGreenfield, language }
56
+ * @returns {{ created: boolean, session: object, path: string }}
57
+ */
58
+ export function initSession(projectDir, options = {}) {
59
+ const sessionPath = join(projectDir, SESSION_FILE);
60
+ const dir = dirname(sessionPath);
61
+
62
+ if (!existsSync(dir)) {
63
+ mkdirSync(dir, { recursive: true });
64
+ }
65
+
66
+ const session = {
67
+ ...DEFAULT_SESSION,
68
+ mode: options.mode || 'clarity',
69
+ language: options.language || 'en',
70
+ project_type: options.isGreenfield === false ? 'brownfield' : 'greenfield',
71
+ started_at: new Date().toISOString(),
72
+ ides: options.ides || [],
73
+ mcps: options.mcps || [],
74
+ };
75
+
76
+ try {
77
+ const yamlContent = yaml.dump(session, {
78
+ lineWidth: -1,
79
+ noRefs: true,
80
+ });
81
+
82
+ writeFileSync(sessionPath, yamlContent, 'utf-8');
83
+
84
+ return {
85
+ created: true,
86
+ session,
87
+ path: sessionPath,
88
+ };
89
+ } catch (err) {
90
+ return {
91
+ created: false,
92
+ session: null,
93
+ path: sessionPath,
94
+ error: `Failed to create session: ${err.message}`,
95
+ };
96
+ }
97
+ }
98
+
99
+ /**
100
+ * Load current session state.
101
+ * @param {string} projectDir
102
+ * @returns {{ loaded: boolean, session: object|null, error: string|null }}
103
+ */
104
+ export function loadSession(projectDir) {
105
+ const sessionPath = join(projectDir, SESSION_FILE);
106
+
107
+ if (!existsSync(sessionPath)) {
108
+ return {
109
+ loaded: false,
110
+ session: null,
111
+ error: 'Session file not found',
112
+ };
113
+ }
114
+
115
+ try {
116
+ const content = readFileSync(sessionPath, 'utf-8');
117
+ const session = yaml.load(content);
118
+
119
+ return {
120
+ loaded: true,
121
+ session,
122
+ error: null,
123
+ };
124
+ } catch (err) {
125
+ return {
126
+ loaded: false,
127
+ session: null,
128
+ error: `Failed to load session: ${err.message}`,
129
+ };
130
+ }
131
+ }
132
+
133
+ /**
134
+ * Update session state (merge with existing).
135
+ * @param {string} projectDir
136
+ * @param {object} updates - Partial session updates
137
+ * @returns {{ saved: boolean }}
138
+ */
139
+ export function updateSession(projectDir, updates) {
140
+ const sessionPath = join(projectDir, SESSION_FILE);
141
+
142
+ // Load existing session
143
+ const loadResult = loadSession(projectDir);
144
+
145
+ if (!loadResult.loaded) {
146
+ return {
147
+ saved: false,
148
+ error: loadResult.error,
149
+ };
150
+ }
151
+
152
+ try {
153
+ // Merge updates
154
+ const merged = {
155
+ ...loadResult.session,
156
+ ...updates,
157
+ };
158
+
159
+ // Deep merge agents if provided
160
+ if (updates.agents) {
161
+ merged.agents = {
162
+ ...loadResult.session.agents,
163
+ ...updates.agents,
164
+ };
165
+
166
+ // Merge individual agent data
167
+ for (const [agentName, agentData] of Object.entries(updates.agents)) {
168
+ merged.agents[agentName] = {
169
+ ...loadResult.session.agents[agentName],
170
+ ...agentData,
171
+ };
172
+ }
173
+ }
174
+
175
+ // Write back
176
+ const yamlContent = yaml.dump(merged, {
177
+ lineWidth: -1,
178
+ noRefs: true,
179
+ });
180
+
181
+ writeFileSync(sessionPath, yamlContent, 'utf-8');
182
+
183
+ return {
184
+ saved: true,
185
+ session: merged,
186
+ };
187
+ } catch (err) {
188
+ return {
189
+ saved: false,
190
+ error: `Failed to update session: ${err.message}`,
191
+ };
192
+ }
193
+ }
194
+
195
+ /**
196
+ * Record a mode transition in session history.
197
+ * @param {string} projectDir
198
+ * @param {object} transition - { from, to, trigger, reason }
199
+ * @returns {{ saved: boolean }}
200
+ */
201
+ export function recordModeTransition(projectDir, transition) {
202
+ const loadResult = loadSession(projectDir);
203
+
204
+ if (!loadResult.loaded) {
205
+ return {
206
+ saved: false,
207
+ error: loadResult.error,
208
+ };
209
+ }
210
+
211
+ const session = loadResult.session;
212
+
213
+ // Add transition to history
214
+ session.mode_transitions = session.mode_transitions || [];
215
+ session.mode_transitions.push({
216
+ from: transition.from,
217
+ to: transition.to,
218
+ trigger: transition.trigger || 'manual',
219
+ reason: transition.reason || '',
220
+ timestamp: new Date().toISOString(),
221
+ });
222
+
223
+ // Update current mode
224
+ session.mode = transition.to;
225
+
226
+ return updateSession(projectDir, session);
227
+ }
228
+
229
+ /**
230
+ * Record an agent completion in session.
231
+ * @param {string} projectDir
232
+ * @param {object} completion - { agent, status, score, outputs }
233
+ * @returns {{ saved: boolean }}
234
+ */
235
+ export function recordAgentCompletion(projectDir, completion) {
236
+ const loadResult = loadSession(projectDir);
237
+
238
+ if (!loadResult.loaded) {
239
+ return {
240
+ saved: false,
241
+ error: loadResult.error,
242
+ };
243
+ }
244
+
245
+ const session = loadResult.session;
246
+ const { agent, status, score, outputs = [] } = completion;
247
+
248
+ // Update agent data
249
+ if (!session.agents[agent]) {
250
+ session.agents[agent] = {
251
+ status: 'pending',
252
+ score: 0,
253
+ criteria_count: 0,
254
+ completed_at: null,
255
+ };
256
+ }
257
+
258
+ session.agents[agent].status = status;
259
+ session.agents[agent].score = score || 0;
260
+ session.agents[agent].completed_at = new Date().toISOString();
261
+
262
+ // Add to completed agents list
263
+ session.completed_agents = session.completed_agents || [];
264
+ if (status === 'completed' && !session.completed_agents.includes(agent)) {
265
+ session.completed_agents.push(agent);
266
+ }
267
+
268
+ // Store detailed results
269
+ session.agent_results = session.agent_results || {};
270
+ session.agent_results[agent] = {
271
+ status,
272
+ score,
273
+ outputs,
274
+ completed_at: session.agents[agent].completed_at,
275
+ };
276
+
277
+ // Update last handoff
278
+ session.last_handoff = agent;
279
+
280
+ return updateSession(projectDir, session);
281
+ }
282
+
283
+ /**
284
+ * Get session summary for display.
285
+ * @param {string} projectDir
286
+ * @returns {object} Summary with progress, mode, agents, timeline
287
+ */
288
+ export function getSessionSummary(projectDir) {
289
+ const loadResult = loadSession(projectDir);
290
+
291
+ if (!loadResult.loaded) {
292
+ return {
293
+ error: loadResult.error,
294
+ summary: null,
295
+ };
296
+ }
297
+
298
+ const session = loadResult.session;
299
+
300
+ // Calculate progress
301
+ const totalAgents = Object.keys(session.agents || {}).length;
302
+ const completedCount = (session.completed_agents || []).length;
303
+ const progressPercent = totalAgents > 0
304
+ ? Math.round((completedCount / totalAgents) * 100)
305
+ : 0;
306
+
307
+ // Get active agents
308
+ const activeAgents = Object.entries(session.agents || {})
309
+ .filter(([, data]) => data.status === 'in_progress')
310
+ .map(([name]) => name);
311
+
312
+ // Calculate duration
313
+ let durationMs = 0;
314
+ if (session.started_at) {
315
+ durationMs = Date.now() - new Date(session.started_at).getTime();
316
+ }
317
+
318
+ const durationHours = Math.floor(durationMs / (1000 * 60 * 60));
319
+ const durationMinutes = Math.floor((durationMs % (1000 * 60 * 60)) / (1000 * 60));
320
+
321
+ return {
322
+ mode: session.mode,
323
+ language: session.language,
324
+ project_type: session.project_type,
325
+ current_agent: session.current_agent,
326
+ progress: {
327
+ percent: progressPercent,
328
+ completed: completedCount,
329
+ total: totalAgents,
330
+ },
331
+ active_agents: activeAgents,
332
+ completed_agents: session.completed_agents || [],
333
+ duration: {
334
+ ms: durationMs,
335
+ hours: durationHours,
336
+ minutes: durationMinutes,
337
+ formatted: `${durationHours}h ${durationMinutes}m`,
338
+ },
339
+ mode_transitions: (session.mode_transitions || []).length,
340
+ deviations: (session.deviations || []).length,
341
+ started_at: session.started_at,
342
+ };
343
+ }
344
+
345
+ /**
346
+ * Check if a session exists and is valid.
347
+ * @param {string} projectDir
348
+ * @returns {{ exists: boolean, valid: boolean, reason: string }}
349
+ */
350
+ export function validateSession(projectDir) {
351
+ const sessionPath = join(projectDir, SESSION_FILE);
352
+
353
+ if (!existsSync(sessionPath)) {
354
+ return {
355
+ exists: false,
356
+ valid: false,
357
+ reason: 'Session file does not exist',
358
+ };
359
+ }
360
+
361
+ const loadResult = loadSession(projectDir);
362
+
363
+ if (!loadResult.loaded) {
364
+ return {
365
+ exists: true,
366
+ valid: false,
367
+ reason: loadResult.error,
368
+ };
369
+ }
370
+
371
+ const session = loadResult.session;
372
+
373
+ // Validate required fields
374
+ const requiredFields = ['version', 'mode', 'language', 'project_type', 'agents'];
375
+ for (const field of requiredFields) {
376
+ if (!session[field]) {
377
+ return {
378
+ exists: true,
379
+ valid: false,
380
+ reason: `Missing required field: ${field}`,
381
+ };
382
+ }
383
+ }
384
+
385
+ // Validate mode
386
+ const validModes = ['clarity', 'build', 'validate', 'deploy', 'completed'];
387
+ if (!validModes.includes(session.mode)) {
388
+ return {
389
+ exists: true,
390
+ valid: false,
391
+ reason: `Invalid mode: ${session.mode}`,
392
+ };
393
+ }
394
+
395
+ // Validate agents structure
396
+ if (typeof session.agents !== 'object') {
397
+ return {
398
+ exists: true,
399
+ valid: false,
400
+ reason: 'Invalid agents structure',
401
+ };
402
+ }
403
+
404
+ return {
405
+ exists: true,
406
+ valid: true,
407
+ reason: 'Session is valid',
408
+ };
409
+ }
@@ -0,0 +1,195 @@
1
+ /**
2
+ * Task Executor — Executes a task definition with PRISM context injection.
3
+ *
4
+ * The executor prepares the full execution context for an agent by:
5
+ * 1. Loading the task definition
6
+ * 2. Injecting PRISM context layers
7
+ * 3. Building the instruction payload
8
+ * 4. Validating criteria post-execution (for autonomous mode)
9
+ */
10
+
11
+ /**
12
+ * Build the execution payload for a task.
13
+ *
14
+ * This produces the complete context that gets injected into
15
+ * the agent's prompt when executing a task.
16
+ *
17
+ * @param {object} task - Task definition from loader
18
+ * @param {object} options
19
+ * @param {object} [options.prismContext] - PRISM context output (xml, bracket, layers)
20
+ * @param {object} [options.handoff] - Handoff data from previous agent
21
+ * @param {object} [options.sessionState] - Current session state
22
+ * @param {string} [options.mode] - Execution mode ('human-in-the-loop' | 'autonomous')
23
+ * @returns {{ payload: object, warnings: string[] }}
24
+ */
25
+ export function buildExecutionPayload(task, options = {}) {
26
+ const warnings = [];
27
+
28
+ if (!task) {
29
+ return { payload: null, warnings: ['No task provided to executor.'] };
30
+ }
31
+
32
+ const mode = options.mode || 'human-in-the-loop';
33
+ const isAutonomous = mode === 'autonomous';
34
+
35
+ // Warn if task requires input but we're in autonomous mode
36
+ if (task.requires_input && isAutonomous) {
37
+ warnings.push(
38
+ `Task '${task.id}' requires user input but running in autonomous mode. ` +
39
+ 'Gate will pause for input.'
40
+ );
41
+ }
42
+
43
+ // Warn if task doesn't support autonomous gate but mode is autonomous
44
+ if (isAutonomous && !task.autonomous_gate) {
45
+ warnings.push(
46
+ `Task '${task.id}' does not have autonomous_gate enabled. ` +
47
+ 'Human validation will be required.'
48
+ );
49
+ }
50
+
51
+ const payload = {
52
+ task: {
53
+ id: task.id,
54
+ agent: task.agent,
55
+ phase: task.phase,
56
+ instructions: task.instructions,
57
+ criteria: task.criteria,
58
+ outputs: task.outputs,
59
+ handoff_to: task.handoff_to,
60
+ },
61
+ context: {
62
+ prism: options.prismContext || null,
63
+ handoff: options.handoff || null,
64
+ session: sanitizeSessionState(options.sessionState),
65
+ },
66
+ execution: {
67
+ mode,
68
+ autonomous_gate: task.autonomous_gate,
69
+ requires_input: task.requires_input,
70
+ parallelizable: task.parallelizable,
71
+ },
72
+ };
73
+
74
+ return { payload, warnings };
75
+ }
76
+
77
+ /**
78
+ * Validate task execution results against criteria.
79
+ *
80
+ * Each criterion is a string describing what must be true.
81
+ * The validator returns a structured result for the orchestrator
82
+ * to decide whether to proceed or require intervention.
83
+ *
84
+ * @param {object} task - Task definition
85
+ * @param {object} results - Execution results to validate
86
+ * @param {string[]} [results.completedCriteria] - Criteria marked as met
87
+ * @param {string[]} [results.outputs] - Produced output files
88
+ * @param {number} [results.confidence] - Self-reported confidence (0-100)
89
+ * @returns {{ valid: boolean, score: number, unmet: string[], details: string }}
90
+ */
91
+ export function validateResults(task, results = {}) {
92
+ if (!task || !task.criteria || task.criteria.length === 0) {
93
+ return { valid: true, score: 100, unmet: [], details: 'No criteria defined.' };
94
+ }
95
+
96
+ const completed = new Set(results.completedCriteria || []);
97
+ const unmet = [];
98
+
99
+ for (const criterion of task.criteria) {
100
+ if (!completed.has(criterion)) {
101
+ unmet.push(criterion);
102
+ }
103
+ }
104
+
105
+ const score = Math.round(((task.criteria.length - unmet.length) / task.criteria.length) * 100);
106
+
107
+ // Check outputs
108
+ const expectedOutputs = task.outputs || [];
109
+ const producedOutputs = results.outputs || [];
110
+ const missingOutputs = expectedOutputs.filter(o => !producedOutputs.includes(o));
111
+
112
+ if (missingOutputs.length > 0) {
113
+ unmet.push(`Missing outputs: ${missingOutputs.join(', ')}`);
114
+ }
115
+
116
+ // Confidence check for autonomous mode
117
+ const confidence = results.confidence ?? 100;
118
+ const confidenceMet = confidence >= 90;
119
+
120
+ const valid = unmet.length === 0 && confidenceMet;
121
+
122
+ let details;
123
+ if (valid) {
124
+ details = `All ${task.criteria.length} criteria met. Confidence: ${confidence}%.`;
125
+ } else {
126
+ const parts = [];
127
+ if (unmet.length > 0) parts.push(`${unmet.length} unmet criteria`);
128
+ if (!confidenceMet) parts.push(`Low confidence: ${confidence}%`);
129
+ details = parts.join('. ');
130
+ }
131
+
132
+ return { valid, score, unmet, details };
133
+ }
134
+
135
+ /**
136
+ * Determine the post-execution action based on validation results.
137
+ *
138
+ * @param {object} validation - Result from validateResults
139
+ * @param {string} mode - 'autonomous' | 'human-in-the-loop'
140
+ * @param {object} task - Task definition
141
+ * @returns {{ action: string, reason: string }}
142
+ * action: 'proceed' | 'retry' | 'escalate' | 'gate'
143
+ */
144
+ export function determinePostAction(validation, mode, task) {
145
+ // Human-in-the-loop always gates
146
+ if (mode !== 'autonomous') {
147
+ return {
148
+ action: 'gate',
149
+ reason: 'Human-in-the-loop mode requires user approval.',
150
+ };
151
+ }
152
+
153
+ // Autonomous mode with autonomous gate
154
+ if (validation.valid) {
155
+ return {
156
+ action: 'proceed',
157
+ reason: `All criteria met (score: ${validation.score}%). Proceeding to ${task.handoff_to || 'next'}.`,
158
+ };
159
+ }
160
+
161
+ // Partial pass — try to determine severity
162
+ if (validation.score >= 90) {
163
+ return {
164
+ action: 'gate',
165
+ reason: `Score ${validation.score}% is close but below threshold. Human review recommended.`,
166
+ };
167
+ }
168
+
169
+ if (validation.score >= 50) {
170
+ return {
171
+ action: 'retry',
172
+ reason: `Score ${validation.score}%. ${validation.unmet.length} criteria unmet. Retry with focused attention.`,
173
+ };
174
+ }
175
+
176
+ return {
177
+ action: 'escalate',
178
+ reason: `Score ${validation.score}%. Major gaps detected. Escalating to orchestrator.`,
179
+ };
180
+ }
181
+
182
+ /**
183
+ * Sanitize session state to remove sensitive or oversized data.
184
+ */
185
+ function sanitizeSessionState(state) {
186
+ if (!state) return null;
187
+
188
+ return {
189
+ mode: state.mode || 'clarity',
190
+ current_agent: state.current_agent || null,
191
+ pipeline_position: state.pipeline_position || null,
192
+ completed_agents: state.completed_agents || [],
193
+ phase: state.phase || null,
194
+ };
195
+ }