popeye-cli 1.10.0 → 2.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 (326) hide show
  1. package/CHANGELOG.md +114 -0
  2. package/CONTRIBUTING.md +38 -3
  3. package/README.md +104 -18
  4. package/dist/adapters/gemini.js +3 -3
  5. package/dist/adapters/openai.js +2 -2
  6. package/dist/adapters/openai.js.map +1 -1
  7. package/dist/auth/gemini.js +1 -1
  8. package/dist/cli/commands/create.d.ts.map +1 -1
  9. package/dist/cli/commands/create.js +11 -5
  10. package/dist/cli/commands/create.js.map +1 -1
  11. package/dist/cli/commands/resume.d.ts.map +1 -1
  12. package/dist/cli/commands/resume.js +9 -1
  13. package/dist/cli/commands/resume.js.map +1 -1
  14. package/dist/cli/interactive.d.ts.map +1 -1
  15. package/dist/cli/interactive.js +29 -3
  16. package/dist/cli/interactive.js.map +1 -1
  17. package/dist/config/defaults.d.ts.map +1 -1
  18. package/dist/config/defaults.js +7 -2
  19. package/dist/config/defaults.js.map +1 -1
  20. package/dist/config/index.d.ts +1 -7
  21. package/dist/config/index.d.ts.map +1 -1
  22. package/dist/config/popeye-md.d.ts +32 -0
  23. package/dist/config/popeye-md.d.ts.map +1 -0
  24. package/dist/config/popeye-md.js +111 -0
  25. package/dist/config/popeye-md.js.map +1 -0
  26. package/dist/config/schema.d.ts +3 -21
  27. package/dist/config/schema.d.ts.map +1 -1
  28. package/dist/config/schema.js +21 -8
  29. package/dist/config/schema.js.map +1 -1
  30. package/dist/pipeline/artifact-manager.d.ts +47 -0
  31. package/dist/pipeline/artifact-manager.d.ts.map +1 -0
  32. package/dist/pipeline/artifact-manager.js +251 -0
  33. package/dist/pipeline/artifact-manager.js.map +1 -0
  34. package/dist/pipeline/artifact-validators.d.ts +29 -0
  35. package/dist/pipeline/artifact-validators.d.ts.map +1 -0
  36. package/dist/pipeline/artifact-validators.js +173 -0
  37. package/dist/pipeline/artifact-validators.js.map +1 -0
  38. package/dist/pipeline/bridges/review-bridge.d.ts +70 -0
  39. package/dist/pipeline/bridges/review-bridge.d.ts.map +1 -0
  40. package/dist/pipeline/bridges/review-bridge.js +266 -0
  41. package/dist/pipeline/bridges/review-bridge.js.map +1 -0
  42. package/dist/pipeline/change-request.d.ts +47 -0
  43. package/dist/pipeline/change-request.d.ts.map +1 -0
  44. package/dist/pipeline/change-request.js +91 -0
  45. package/dist/pipeline/change-request.js.map +1 -0
  46. package/dist/pipeline/check-runner.d.ts +47 -0
  47. package/dist/pipeline/check-runner.d.ts.map +1 -0
  48. package/dist/pipeline/check-runner.js +417 -0
  49. package/dist/pipeline/check-runner.js.map +1 -0
  50. package/dist/pipeline/command-resolver.d.ts +9 -0
  51. package/dist/pipeline/command-resolver.d.ts.map +1 -0
  52. package/dist/pipeline/command-resolver.js +140 -0
  53. package/dist/pipeline/command-resolver.js.map +1 -0
  54. package/dist/pipeline/consensus/consensus-runner.d.ts +44 -0
  55. package/dist/pipeline/consensus/consensus-runner.d.ts.map +1 -0
  56. package/dist/pipeline/consensus/consensus-runner.js +212 -0
  57. package/dist/pipeline/consensus/consensus-runner.js.map +1 -0
  58. package/dist/pipeline/constitution.d.ts +45 -0
  59. package/dist/pipeline/constitution.d.ts.map +1 -0
  60. package/dist/pipeline/constitution.js +82 -0
  61. package/dist/pipeline/constitution.js.map +1 -0
  62. package/dist/pipeline/gate-engine.d.ts +55 -0
  63. package/dist/pipeline/gate-engine.d.ts.map +1 -0
  64. package/dist/pipeline/gate-engine.js +270 -0
  65. package/dist/pipeline/gate-engine.js.map +1 -0
  66. package/dist/pipeline/index.d.ts +26 -0
  67. package/dist/pipeline/index.d.ts.map +1 -0
  68. package/dist/pipeline/index.js +35 -0
  69. package/dist/pipeline/index.js.map +1 -0
  70. package/dist/pipeline/migration.d.ts +15 -0
  71. package/dist/pipeline/migration.d.ts.map +1 -0
  72. package/dist/pipeline/migration.js +76 -0
  73. package/dist/pipeline/migration.js.map +1 -0
  74. package/dist/pipeline/orchestrator.d.ts +30 -0
  75. package/dist/pipeline/orchestrator.d.ts.map +1 -0
  76. package/dist/pipeline/orchestrator.js +242 -0
  77. package/dist/pipeline/orchestrator.js.map +1 -0
  78. package/dist/pipeline/packets/audit-report-builder.d.ts +11 -0
  79. package/dist/pipeline/packets/audit-report-builder.d.ts.map +1 -0
  80. package/dist/pipeline/packets/audit-report-builder.js +32 -0
  81. package/dist/pipeline/packets/audit-report-builder.js.map +1 -0
  82. package/dist/pipeline/packets/consensus-packet-builder.d.ts +35 -0
  83. package/dist/pipeline/packets/consensus-packet-builder.d.ts.map +1 -0
  84. package/dist/pipeline/packets/consensus-packet-builder.js +80 -0
  85. package/dist/pipeline/packets/consensus-packet-builder.js.map +1 -0
  86. package/dist/pipeline/packets/index.d.ts +12 -0
  87. package/dist/pipeline/packets/index.d.ts.map +1 -0
  88. package/dist/pipeline/packets/index.js +8 -0
  89. package/dist/pipeline/packets/index.js.map +1 -0
  90. package/dist/pipeline/packets/plan-packet-builder.d.ts +21 -0
  91. package/dist/pipeline/packets/plan-packet-builder.d.ts.map +1 -0
  92. package/dist/pipeline/packets/plan-packet-builder.js +27 -0
  93. package/dist/pipeline/packets/plan-packet-builder.js.map +1 -0
  94. package/dist/pipeline/packets/rca-packet-builder.d.ts +19 -0
  95. package/dist/pipeline/packets/rca-packet-builder.d.ts.map +1 -0
  96. package/dist/pipeline/packets/rca-packet-builder.js +22 -0
  97. package/dist/pipeline/packets/rca-packet-builder.js.map +1 -0
  98. package/dist/pipeline/phases/architecture.d.ts +7 -0
  99. package/dist/pipeline/phases/architecture.d.ts.map +1 -0
  100. package/dist/pipeline/phases/architecture.js +60 -0
  101. package/dist/pipeline/phases/architecture.js.map +1 -0
  102. package/dist/pipeline/phases/audit.d.ts +8 -0
  103. package/dist/pipeline/phases/audit.d.ts.map +1 -0
  104. package/dist/pipeline/phases/audit.js +144 -0
  105. package/dist/pipeline/phases/audit.js.map +1 -0
  106. package/dist/pipeline/phases/consensus-architecture.d.ts +7 -0
  107. package/dist/pipeline/phases/consensus-architecture.d.ts.map +1 -0
  108. package/dist/pipeline/phases/consensus-architecture.js +84 -0
  109. package/dist/pipeline/phases/consensus-architecture.js.map +1 -0
  110. package/dist/pipeline/phases/consensus-master-plan.d.ts +7 -0
  111. package/dist/pipeline/phases/consensus-master-plan.d.ts.map +1 -0
  112. package/dist/pipeline/phases/consensus-master-plan.js +81 -0
  113. package/dist/pipeline/phases/consensus-master-plan.js.map +1 -0
  114. package/dist/pipeline/phases/consensus-role-plans.d.ts +7 -0
  115. package/dist/pipeline/phases/consensus-role-plans.d.ts.map +1 -0
  116. package/dist/pipeline/phases/consensus-role-plans.js +85 -0
  117. package/dist/pipeline/phases/consensus-role-plans.js.map +1 -0
  118. package/dist/pipeline/phases/done.d.ts +7 -0
  119. package/dist/pipeline/phases/done.d.ts.map +1 -0
  120. package/dist/pipeline/phases/done.js +45 -0
  121. package/dist/pipeline/phases/done.js.map +1 -0
  122. package/dist/pipeline/phases/implementation.d.ts +8 -0
  123. package/dist/pipeline/phases/implementation.d.ts.map +1 -0
  124. package/dist/pipeline/phases/implementation.js +45 -0
  125. package/dist/pipeline/phases/implementation.js.map +1 -0
  126. package/dist/pipeline/phases/index.d.ts +20 -0
  127. package/dist/pipeline/phases/index.d.ts.map +1 -0
  128. package/dist/pipeline/phases/index.js +19 -0
  129. package/dist/pipeline/phases/index.js.map +1 -0
  130. package/dist/pipeline/phases/intake.d.ts +8 -0
  131. package/dist/pipeline/phases/intake.d.ts.map +1 -0
  132. package/dist/pipeline/phases/intake.js +49 -0
  133. package/dist/pipeline/phases/intake.js.map +1 -0
  134. package/dist/pipeline/phases/phase-context.d.ts +30 -0
  135. package/dist/pipeline/phases/phase-context.d.ts.map +1 -0
  136. package/dist/pipeline/phases/phase-context.js +33 -0
  137. package/dist/pipeline/phases/phase-context.js.map +1 -0
  138. package/dist/pipeline/phases/production-gate.d.ts +8 -0
  139. package/dist/pipeline/phases/production-gate.d.ts.map +1 -0
  140. package/dist/pipeline/phases/production-gate.js +84 -0
  141. package/dist/pipeline/phases/production-gate.js.map +1 -0
  142. package/dist/pipeline/phases/qa-validation.d.ts +7 -0
  143. package/dist/pipeline/phases/qa-validation.d.ts.map +1 -0
  144. package/dist/pipeline/phases/qa-validation.js +50 -0
  145. package/dist/pipeline/phases/qa-validation.js.map +1 -0
  146. package/dist/pipeline/phases/recovery-loop.d.ts +7 -0
  147. package/dist/pipeline/phases/recovery-loop.d.ts.map +1 -0
  148. package/dist/pipeline/phases/recovery-loop.js +93 -0
  149. package/dist/pipeline/phases/recovery-loop.js.map +1 -0
  150. package/dist/pipeline/phases/review.d.ts +8 -0
  151. package/dist/pipeline/phases/review.d.ts.map +1 -0
  152. package/dist/pipeline/phases/review.js +127 -0
  153. package/dist/pipeline/phases/review.js.map +1 -0
  154. package/dist/pipeline/phases/role-planning.d.ts +7 -0
  155. package/dist/pipeline/phases/role-planning.d.ts.map +1 -0
  156. package/dist/pipeline/phases/role-planning.js +75 -0
  157. package/dist/pipeline/phases/role-planning.js.map +1 -0
  158. package/dist/pipeline/phases/stuck.d.ts +7 -0
  159. package/dist/pipeline/phases/stuck.d.ts.map +1 -0
  160. package/dist/pipeline/phases/stuck.js +51 -0
  161. package/dist/pipeline/phases/stuck.js.map +1 -0
  162. package/dist/pipeline/repo-snapshot.d.ts +24 -0
  163. package/dist/pipeline/repo-snapshot.d.ts.map +1 -0
  164. package/dist/pipeline/repo-snapshot.js +343 -0
  165. package/dist/pipeline/repo-snapshot.js.map +1 -0
  166. package/dist/pipeline/role-execution-adapter.d.ts +59 -0
  167. package/dist/pipeline/role-execution-adapter.d.ts.map +1 -0
  168. package/dist/pipeline/role-execution-adapter.js +159 -0
  169. package/dist/pipeline/role-execution-adapter.js.map +1 -0
  170. package/dist/pipeline/skill-loader.d.ts +34 -0
  171. package/dist/pipeline/skill-loader.d.ts.map +1 -0
  172. package/dist/pipeline/skill-loader.js +156 -0
  173. package/dist/pipeline/skill-loader.js.map +1 -0
  174. package/dist/pipeline/skills/defaults.d.ts +16 -0
  175. package/dist/pipeline/skills/defaults.d.ts.map +1 -0
  176. package/dist/pipeline/skills/defaults.js +189 -0
  177. package/dist/pipeline/skills/defaults.js.map +1 -0
  178. package/dist/pipeline/type-defs/artifacts.d.ts +207 -0
  179. package/dist/pipeline/type-defs/artifacts.d.ts.map +1 -0
  180. package/dist/pipeline/type-defs/artifacts.js +67 -0
  181. package/dist/pipeline/type-defs/artifacts.js.map +1 -0
  182. package/dist/pipeline/type-defs/audit.d.ts +259 -0
  183. package/dist/pipeline/type-defs/audit.d.ts.map +1 -0
  184. package/dist/pipeline/type-defs/audit.js +54 -0
  185. package/dist/pipeline/type-defs/audit.js.map +1 -0
  186. package/dist/pipeline/type-defs/checks.d.ts +82 -0
  187. package/dist/pipeline/type-defs/checks.d.ts.map +1 -0
  188. package/dist/pipeline/type-defs/checks.js +38 -0
  189. package/dist/pipeline/type-defs/checks.js.map +1 -0
  190. package/dist/pipeline/type-defs/enums.d.ts +43 -0
  191. package/dist/pipeline/type-defs/enums.d.ts.map +1 -0
  192. package/dist/pipeline/type-defs/enums.js +55 -0
  193. package/dist/pipeline/type-defs/enums.js.map +1 -0
  194. package/dist/pipeline/type-defs/index.d.ts +12 -0
  195. package/dist/pipeline/type-defs/index.d.ts.map +1 -0
  196. package/dist/pipeline/type-defs/index.js +12 -0
  197. package/dist/pipeline/type-defs/index.js.map +1 -0
  198. package/dist/pipeline/type-defs/packets.d.ts +821 -0
  199. package/dist/pipeline/type-defs/packets.d.ts.map +1 -0
  200. package/dist/pipeline/type-defs/packets.js +109 -0
  201. package/dist/pipeline/type-defs/packets.js.map +1 -0
  202. package/dist/pipeline/type-defs/snapshot.d.ts +52 -0
  203. package/dist/pipeline/type-defs/snapshot.d.ts.map +1 -0
  204. package/dist/pipeline/type-defs/snapshot.js +35 -0
  205. package/dist/pipeline/type-defs/snapshot.js.map +1 -0
  206. package/dist/pipeline/type-defs/state.d.ts +455 -0
  207. package/dist/pipeline/type-defs/state.d.ts.map +1 -0
  208. package/dist/pipeline/type-defs/state.js +90 -0
  209. package/dist/pipeline/type-defs/state.js.map +1 -0
  210. package/dist/pipeline/types.d.ts +16 -0
  211. package/dist/pipeline/types.d.ts.map +1 -0
  212. package/dist/pipeline/types.js +16 -0
  213. package/dist/pipeline/types.js.map +1 -0
  214. package/dist/types/audit.d.ts +6 -6
  215. package/dist/types/consensus.d.ts +5 -1
  216. package/dist/types/consensus.d.ts.map +1 -1
  217. package/dist/types/consensus.js +15 -4
  218. package/dist/types/consensus.js.map +1 -1
  219. package/dist/types/index.d.ts +1 -1
  220. package/dist/types/index.d.ts.map +1 -1
  221. package/dist/types/index.js +1 -1
  222. package/dist/types/index.js.map +1 -1
  223. package/dist/types/project.d.ts +1 -1
  224. package/dist/types/project.d.ts.map +1 -1
  225. package/dist/types/project.js +39 -10
  226. package/dist/types/project.js.map +1 -1
  227. package/dist/types/workflow.d.ts +1 -7
  228. package/dist/types/workflow.d.ts.map +1 -1
  229. package/dist/types/workflow.js +1 -1
  230. package/dist/types/workflow.js.map +1 -1
  231. package/dist/upgrade/handlers.js +5 -5
  232. package/dist/upgrade/handlers.js.map +1 -1
  233. package/dist/workflow/index.d.ts.map +1 -1
  234. package/dist/workflow/index.js +52 -0
  235. package/dist/workflow/index.js.map +1 -1
  236. package/dist/workflow/website-strategy.js +1 -1
  237. package/dist/workflow/website-strategy.js.map +1 -1
  238. package/package.json +1 -1
  239. package/skills/PHASE_GATE_ENGINE_SPEC.md +113 -20
  240. package/skills/POPEYE_FULL_AUTONOMY_PIPELINE.md +66 -13
  241. package/src/adapters/gemini.ts +3 -3
  242. package/src/adapters/openai.ts +2 -2
  243. package/src/auth/gemini.ts +1 -1
  244. package/src/cli/commands/create.ts +12 -6
  245. package/src/cli/commands/resume.ts +9 -1
  246. package/src/cli/interactive.ts +32 -3
  247. package/src/config/defaults.ts +7 -2
  248. package/src/config/popeye-md.ts +139 -0
  249. package/src/config/schema.ts +21 -8
  250. package/src/pipeline/artifact-manager.ts +339 -0
  251. package/src/pipeline/artifact-validators.ts +224 -0
  252. package/src/pipeline/bridges/review-bridge.ts +371 -0
  253. package/src/pipeline/change-request.ts +119 -0
  254. package/src/pipeline/check-runner.ts +504 -0
  255. package/src/pipeline/command-resolver.ts +168 -0
  256. package/src/pipeline/consensus/consensus-runner.ts +317 -0
  257. package/src/pipeline/constitution.ts +109 -0
  258. package/src/pipeline/gate-engine.ts +347 -0
  259. package/src/pipeline/index.ts +82 -0
  260. package/src/pipeline/migration.ts +91 -0
  261. package/src/pipeline/orchestrator.ts +322 -0
  262. package/src/pipeline/packets/audit-report-builder.ts +47 -0
  263. package/src/pipeline/packets/consensus-packet-builder.ts +112 -0
  264. package/src/pipeline/packets/index.ts +15 -0
  265. package/src/pipeline/packets/plan-packet-builder.ts +52 -0
  266. package/src/pipeline/packets/rca-packet-builder.ts +38 -0
  267. package/src/pipeline/phases/architecture.ts +73 -0
  268. package/src/pipeline/phases/audit.ts +193 -0
  269. package/src/pipeline/phases/consensus-architecture.ts +104 -0
  270. package/src/pipeline/phases/consensus-master-plan.ts +100 -0
  271. package/src/pipeline/phases/consensus-role-plans.ts +105 -0
  272. package/src/pipeline/phases/done.ts +68 -0
  273. package/src/pipeline/phases/implementation.ts +52 -0
  274. package/src/pipeline/phases/index.ts +21 -0
  275. package/src/pipeline/phases/intake.ts +68 -0
  276. package/src/pipeline/phases/phase-context.ts +86 -0
  277. package/src/pipeline/phases/production-gate.ts +113 -0
  278. package/src/pipeline/phases/qa-validation.ts +63 -0
  279. package/src/pipeline/phases/recovery-loop.ts +120 -0
  280. package/src/pipeline/phases/review.ts +149 -0
  281. package/src/pipeline/phases/role-planning.ts +92 -0
  282. package/src/pipeline/phases/stuck.ts +62 -0
  283. package/src/pipeline/repo-snapshot.ts +395 -0
  284. package/src/pipeline/role-execution-adapter.ts +238 -0
  285. package/src/pipeline/skill-loader.ts +192 -0
  286. package/src/pipeline/skills/defaults.ts +215 -0
  287. package/src/pipeline/type-defs/artifacts.ts +82 -0
  288. package/src/pipeline/type-defs/audit.ts +67 -0
  289. package/src/pipeline/type-defs/checks.ts +47 -0
  290. package/src/pipeline/type-defs/enums.ts +62 -0
  291. package/src/pipeline/type-defs/index.ts +12 -0
  292. package/src/pipeline/type-defs/packets.ts +131 -0
  293. package/src/pipeline/type-defs/snapshot.ts +55 -0
  294. package/src/pipeline/type-defs/state.ts +167 -0
  295. package/src/pipeline/types.ts +16 -0
  296. package/src/types/consensus.ts +16 -4
  297. package/src/types/index.ts +1 -0
  298. package/src/types/project.ts +39 -10
  299. package/src/types/workflow.ts +1 -1
  300. package/src/upgrade/handlers.ts +5 -5
  301. package/src/workflow/index.ts +52 -0
  302. package/src/workflow/website-strategy.ts +1 -1
  303. package/tests/cli/model-command.test.ts +19 -9
  304. package/tests/config/config.test.ts +3 -3
  305. package/tests/config/popeye-md.test.ts +168 -0
  306. package/tests/pipeline/artifact-manager.test.ts +183 -0
  307. package/tests/pipeline/artifact-validators.test.ts +207 -0
  308. package/tests/pipeline/bridges/review-bridge.test.ts +243 -0
  309. package/tests/pipeline/change-request.test.ts +180 -0
  310. package/tests/pipeline/check-runner.test.ts +157 -0
  311. package/tests/pipeline/command-resolver.test.ts +159 -0
  312. package/tests/pipeline/consensus-runner.test.ts +206 -0
  313. package/tests/pipeline/consensus-scoring.test.ts +163 -0
  314. package/tests/pipeline/constitution.test.ts +122 -0
  315. package/tests/pipeline/gate-engine.test.ts +195 -0
  316. package/tests/pipeline/migration.test.ts +133 -0
  317. package/tests/pipeline/orchestrator.test.ts +614 -0
  318. package/tests/pipeline/packets/builders.test.ts +347 -0
  319. package/tests/pipeline/repo-snapshot.test.ts +189 -0
  320. package/tests/pipeline/role-execution-adapter.test.ts +299 -0
  321. package/tests/pipeline/session-guidance.test.ts +205 -0
  322. package/tests/pipeline/skill-loader.test.ts +186 -0
  323. package/tests/pipeline/start-env-checks.test.ts +123 -0
  324. package/tests/pipeline/types.test.ts +156 -0
  325. package/tests/types/consensus.test.ts +1 -1
  326. package/tests/workflow/pipeline-bootstrap.test.ts +162 -0
@@ -0,0 +1,122 @@
1
+ /**
2
+ * Constitution tests — artifact creation, hash computation, verification.
3
+ */
4
+
5
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
6
+ import { mkdirSync, writeFileSync, rmSync, existsSync } from 'node:fs';
7
+ import { join } from 'node:path';
8
+ import { createHash } from 'node:crypto';
9
+ import {
10
+ computeConstitutionHash,
11
+ createConstitutionArtifact,
12
+ verifyConstitution,
13
+ } from '../../src/pipeline/constitution.js';
14
+ import { createArtifactManager } from '../../src/pipeline/artifact-manager.js';
15
+ import { createDefaultPipelineState } from '../../src/pipeline/types.js';
16
+
17
+ const TEST_DIR = join(process.cwd(), 'tmp-constitution-test');
18
+
19
+ beforeEach(() => {
20
+ mkdirSync(join(TEST_DIR, 'skills'), { recursive: true });
21
+ mkdirSync(join(TEST_DIR, 'docs'), { recursive: true });
22
+ });
23
+
24
+ afterEach(() => {
25
+ if (existsSync(TEST_DIR)) {
26
+ rmSync(TEST_DIR, { recursive: true, force: true });
27
+ }
28
+ });
29
+
30
+ describe('computeConstitutionHash', () => {
31
+ it('should compute SHA-256 of constitution file', () => {
32
+ const content = '# POPEYE CONSTITUTION\nRule 1: Be deterministic';
33
+ writeFileSync(join(TEST_DIR, 'skills', 'POPEYE_CONSTITUTION.md'), content);
34
+
35
+ const hash = computeConstitutionHash(TEST_DIR);
36
+ const expected = createHash('sha256').update(content, 'utf-8').digest('hex');
37
+ expect(hash).toBe(expected);
38
+ });
39
+
40
+ it('should return empty string when file not found', () => {
41
+ const hash = computeConstitutionHash(TEST_DIR + '-nonexistent');
42
+ expect(hash).toBe('');
43
+ });
44
+
45
+ it('should produce different hashes for different content', () => {
46
+ writeFileSync(join(TEST_DIR, 'skills', 'POPEYE_CONSTITUTION.md'), 'Version 1');
47
+ const hash1 = computeConstitutionHash(TEST_DIR);
48
+
49
+ writeFileSync(join(TEST_DIR, 'skills', 'POPEYE_CONSTITUTION.md'), 'Version 2');
50
+ const hash2 = computeConstitutionHash(TEST_DIR);
51
+
52
+ expect(hash1).not.toBe(hash2);
53
+ });
54
+ });
55
+
56
+ describe('createConstitutionArtifact', () => {
57
+ it('should create an artifact from constitution file', () => {
58
+ writeFileSync(
59
+ join(TEST_DIR, 'skills', 'POPEYE_CONSTITUTION.md'),
60
+ '# Constitution\nRule 1',
61
+ );
62
+ const am = createArtifactManager(TEST_DIR);
63
+ const entry = createConstitutionArtifact(TEST_DIR, am);
64
+
65
+ expect(entry).not.toBeNull();
66
+ expect(entry!.type).toBe('constitution');
67
+ expect(entry!.phase).toBe('INTAKE');
68
+ });
69
+
70
+ it('should return null when constitution file missing', () => {
71
+ const am = createArtifactManager(TEST_DIR);
72
+ const entry = createConstitutionArtifact(TEST_DIR + '-nope', am);
73
+ expect(entry).toBeNull();
74
+ });
75
+ });
76
+
77
+ describe('verifyConstitution', () => {
78
+ it('should pass when hash matches', () => {
79
+ const content = '# Constitution\nRule 1: Immutable';
80
+ writeFileSync(join(TEST_DIR, 'skills', 'POPEYE_CONSTITUTION.md'), content);
81
+
82
+ const pipeline = createDefaultPipelineState();
83
+ pipeline.constitutionHash = computeConstitutionHash(TEST_DIR);
84
+
85
+ const result = verifyConstitution(pipeline, TEST_DIR);
86
+ expect(result.valid).toBe(true);
87
+ });
88
+
89
+ it('should fail when constitution has been modified', () => {
90
+ writeFileSync(join(TEST_DIR, 'skills', 'POPEYE_CONSTITUTION.md'), 'Original');
91
+ const pipeline = createDefaultPipelineState();
92
+ pipeline.constitutionHash = computeConstitutionHash(TEST_DIR);
93
+
94
+ // Modify the file
95
+ writeFileSync(join(TEST_DIR, 'skills', 'POPEYE_CONSTITUTION.md'), 'Modified!');
96
+
97
+ const result = verifyConstitution(pipeline, TEST_DIR);
98
+ expect(result.valid).toBe(false);
99
+ expect(result.reason).toContain('modified');
100
+ });
101
+
102
+ it('should skip verification when no hash stored (pre-INTAKE)', () => {
103
+ const pipeline = createDefaultPipelineState();
104
+ // constitutionHash is '' by default
105
+
106
+ const result = verifyConstitution(pipeline, TEST_DIR);
107
+ expect(result.valid).toBe(true);
108
+ });
109
+
110
+ it('should fail when constitution file deleted after hash stored', () => {
111
+ writeFileSync(join(TEST_DIR, 'skills', 'POPEYE_CONSTITUTION.md'), 'Content');
112
+ const pipeline = createDefaultPipelineState();
113
+ pipeline.constitutionHash = computeConstitutionHash(TEST_DIR);
114
+
115
+ // Delete the file
116
+ rmSync(join(TEST_DIR, 'skills', 'POPEYE_CONSTITUTION.md'));
117
+
118
+ const result = verifyConstitution(pipeline, TEST_DIR);
119
+ expect(result.valid).toBe(false);
120
+ expect(result.reason).toContain('not found');
121
+ });
122
+ });
@@ -0,0 +1,195 @@
1
+ /**
2
+ * Gate Engine tests — all transition rules, gate definitions, edge cases.
3
+ */
4
+
5
+ import { describe, it, expect } from 'vitest';
6
+ import { createGateEngine } from '../../src/pipeline/gate-engine.js';
7
+ import { createDefaultPipelineState } from '../../src/pipeline/types.js';
8
+ import type { PipelinePhase, PipelineState, ArtifactEntry } from '../../src/pipeline/types.js';
9
+
10
+ function makeArtifact(type: string, phase: string): ArtifactEntry {
11
+ return {
12
+ id: `test-${type}-${phase}`,
13
+ type: type as ArtifactEntry['type'],
14
+ phase: phase as PipelinePhase,
15
+ version: 1,
16
+ path: `docs/${type}.md`,
17
+ sha256: 'abc123',
18
+ timestamp: new Date().toISOString(),
19
+ immutable: true,
20
+ content_type: 'markdown',
21
+ group_id: `group-${type}`,
22
+ };
23
+ }
24
+
25
+ describe('GateEngine', () => {
26
+ const engine = createGateEngine();
27
+
28
+ describe('getGateDefinition', () => {
29
+ it('should return definitions for all 14 phases', () => {
30
+ const phases: PipelinePhase[] = [
31
+ 'INTAKE', 'CONSENSUS_MASTER_PLAN', 'ARCHITECTURE',
32
+ 'CONSENSUS_ARCHITECTURE', 'ROLE_PLANNING', 'CONSENSUS_ROLE_PLANS',
33
+ 'IMPLEMENTATION', 'QA_VALIDATION', 'REVIEW', 'AUDIT',
34
+ 'PRODUCTION_GATE', 'RECOVERY_LOOP', 'DONE', 'STUCK',
35
+ ];
36
+
37
+ for (const phase of phases) {
38
+ const def = engine.getGateDefinition(phase);
39
+ expect(def).toBeDefined();
40
+ expect(def.phase).toBe(phase);
41
+ expect(def.failTransition).toBeDefined();
42
+ }
43
+ });
44
+
45
+ it('should require master_plan and repo_snapshot for INTAKE', () => {
46
+ const def = engine.getGateDefinition('INTAKE');
47
+ expect(def.requiredArtifacts).toContain('master_plan');
48
+ expect(def.requiredArtifacts).toContain('repo_snapshot');
49
+ expect(def.allowedTransitions).toEqual(['CONSENSUS_MASTER_PLAN']);
50
+ });
51
+
52
+ it('should require consensus threshold for consensus phases', () => {
53
+ const consensusPhases: PipelinePhase[] = [
54
+ 'CONSENSUS_MASTER_PLAN', 'CONSENSUS_ARCHITECTURE', 'CONSENSUS_ROLE_PLANS',
55
+ ];
56
+ for (const phase of consensusPhases) {
57
+ const def = engine.getGateDefinition(phase);
58
+ expect(def.consensusThreshold).toBe(0.95);
59
+ expect(def.minReviewers).toBe(2);
60
+ }
61
+ });
62
+
63
+ it('should require build/test/lint/typecheck for PRODUCTION_GATE', () => {
64
+ const def = engine.getGateDefinition('PRODUCTION_GATE');
65
+ expect(def.requiredChecks).toContain('build');
66
+ expect(def.requiredChecks).toContain('test');
67
+ expect(def.requiredChecks).toContain('lint');
68
+ expect(def.requiredChecks).toContain('typecheck');
69
+ });
70
+
71
+ it('should have terminal DONE and STUCK phases', () => {
72
+ expect(engine.getGateDefinition('DONE').allowedTransitions).toEqual([]);
73
+ expect(engine.getGateDefinition('STUCK').allowedTransitions).toEqual([]);
74
+ });
75
+ });
76
+
77
+ describe('evaluateGate', () => {
78
+ it('should fail when required artifacts are missing', () => {
79
+ const pipeline = createDefaultPipelineState();
80
+ const result = engine.evaluateGate('INTAKE', pipeline);
81
+
82
+ expect(result.pass).toBe(false);
83
+ expect(result.missingArtifacts).toContain('master_plan');
84
+ expect(result.missingArtifacts).toContain('repo_snapshot');
85
+ expect(result.blockers.length).toBeGreaterThan(0);
86
+ });
87
+
88
+ it('should pass when all required artifacts exist', () => {
89
+ const pipeline = createDefaultPipelineState();
90
+ pipeline.artifacts.push(makeArtifact('master_plan', 'INTAKE'));
91
+ pipeline.artifacts.push(makeArtifact('repo_snapshot', 'INTAKE'));
92
+ pipeline.artifacts.push(makeArtifact('constitution', 'INTAKE'));
93
+
94
+ const result = engine.evaluateGate('INTAKE', pipeline);
95
+ expect(result.pass).toBe(true);
96
+ expect(result.blockers).toHaveLength(0);
97
+ });
98
+
99
+ it('should check gate check results for QA_VALIDATION', () => {
100
+ const pipeline = createDefaultPipelineState();
101
+ pipeline.artifacts.push(makeArtifact('qa_validation', 'QA_VALIDATION'));
102
+ // No test check result
103
+ const result = engine.evaluateGate('QA_VALIDATION', pipeline);
104
+ expect(result.pass).toBe(false);
105
+ expect(result.failedChecks).toContain('test');
106
+ });
107
+
108
+ it('should pass QA when test check passes', () => {
109
+ const pipeline = createDefaultPipelineState();
110
+ pipeline.artifacts.push(makeArtifact('qa_validation', 'QA_VALIDATION'));
111
+ pipeline.gateChecks['QA_VALIDATION'] = [{
112
+ check_type: 'test',
113
+ status: 'pass',
114
+ command: 'npm test',
115
+ exit_code: 0,
116
+ duration_ms: 1000,
117
+ timestamp: new Date().toISOString(),
118
+ }];
119
+
120
+ const result = engine.evaluateGate('QA_VALIDATION', pipeline);
121
+ expect(result.pass).toBe(true);
122
+ });
123
+
124
+ it('should fail when check result status is fail', () => {
125
+ const pipeline = createDefaultPipelineState();
126
+ pipeline.artifacts.push(makeArtifact('qa_validation', 'QA_VALIDATION'));
127
+ pipeline.gateChecks['QA_VALIDATION'] = [{
128
+ check_type: 'test',
129
+ status: 'fail',
130
+ command: 'npm test',
131
+ exit_code: 1,
132
+ duration_ms: 500,
133
+ timestamp: new Date().toISOString(),
134
+ }];
135
+
136
+ const result = engine.evaluateGate('QA_VALIDATION', pipeline);
137
+ expect(result.pass).toBe(false);
138
+ expect(result.failedChecks).toContain('test');
139
+ });
140
+ });
141
+
142
+ describe('getNextPhase', () => {
143
+ it('should follow the linear sequence', () => {
144
+ const passResult = {
145
+ phase: 'INTAKE' as PipelinePhase,
146
+ pass: true,
147
+ blockers: [],
148
+ missingArtifacts: [],
149
+ failedChecks: [],
150
+ timestamp: new Date().toISOString(),
151
+ };
152
+
153
+ expect(engine.getNextPhase('INTAKE', passResult)).toBe('CONSENSUS_MASTER_PLAN');
154
+ expect(engine.getNextPhase('CONSENSUS_MASTER_PLAN', passResult)).toBe('ARCHITECTURE');
155
+ expect(engine.getNextPhase('ARCHITECTURE', passResult)).toBe('CONSENSUS_ARCHITECTURE');
156
+ expect(engine.getNextPhase('PRODUCTION_GATE', passResult)).toBe('DONE');
157
+ });
158
+ });
159
+
160
+ describe('canTransition', () => {
161
+ it('should allow valid transitions when gate passes', () => {
162
+ const pipeline = createDefaultPipelineState();
163
+ pipeline.artifacts.push(makeArtifact('master_plan', 'INTAKE'));
164
+ pipeline.artifacts.push(makeArtifact('repo_snapshot', 'INTAKE'));
165
+ pipeline.artifacts.push(makeArtifact('constitution', 'INTAKE'));
166
+
167
+ const result = engine.canTransition('INTAKE', 'CONSENSUS_MASTER_PLAN', pipeline);
168
+ expect(result.allowed).toBe(true);
169
+ });
170
+
171
+ it('should reject invalid transitions', () => {
172
+ const pipeline = createDefaultPipelineState();
173
+ const result = engine.canTransition('INTAKE', 'PRODUCTION_GATE', pipeline);
174
+ expect(result.allowed).toBe(false);
175
+ expect(result.blockers.some((b) => b.includes('not allowed'))).toBe(true);
176
+ });
177
+ });
178
+
179
+ describe('getPhaseSequence', () => {
180
+ it('should return ordered phases ending with DONE', () => {
181
+ const seq = engine.getPhaseSequence();
182
+ expect(seq[0]).toBe('INTAKE');
183
+ expect(seq[seq.length - 1]).toBe('DONE');
184
+ expect(seq.length).toBe(12); // 12 phases in linear sequence
185
+ });
186
+ });
187
+
188
+ describe('getPhaseIndex', () => {
189
+ it('should return correct indices', () => {
190
+ expect(engine.getPhaseIndex('INTAKE')).toBe(0);
191
+ expect(engine.getPhaseIndex('DONE')).toBe(11);
192
+ expect(engine.getPhaseIndex('RECOVERY_LOOP')).toBe(-1); // Not in linear sequence
193
+ });
194
+ });
195
+ });
@@ -0,0 +1,133 @@
1
+ /**
2
+ * Migration tests — phase mapping, defaults, role derivation.
3
+ */
4
+
5
+ import { describe, it, expect } from 'vitest';
6
+ import {
7
+ toPipelinePhase,
8
+ toLegacyPhase,
9
+ migrateToPipelineState,
10
+ needsPipelineMigration,
11
+ } from '../../src/pipeline/migration.js';
12
+ import type { ProjectState } from '../../src/types/workflow.js';
13
+
14
+ function makeMinimalState(overrides: Partial<ProjectState> = {}): ProjectState {
15
+ return {
16
+ idea: 'test idea',
17
+ language: 'typescript',
18
+ phase: 'plan',
19
+ projectDir: '/tmp/test',
20
+ timestamp: new Date().toISOString(),
21
+ ...overrides,
22
+ } as ProjectState;
23
+ }
24
+
25
+ describe('Migration', () => {
26
+ describe('toPipelinePhase', () => {
27
+ it('should map plan to INTAKE', () => {
28
+ expect(toPipelinePhase('plan')).toBe('INTAKE');
29
+ });
30
+
31
+ it('should map execution to IMPLEMENTATION', () => {
32
+ expect(toPipelinePhase('execution')).toBe('IMPLEMENTATION');
33
+ });
34
+
35
+ it('should map complete to DONE', () => {
36
+ expect(toPipelinePhase('complete')).toBe('DONE');
37
+ });
38
+
39
+ it('should default to INTAKE for unknown phases', () => {
40
+ expect(toPipelinePhase('unknown' as any)).toBe('INTAKE');
41
+ });
42
+ });
43
+
44
+ describe('toLegacyPhase', () => {
45
+ it('should map planning phases to plan', () => {
46
+ expect(toLegacyPhase('INTAKE')).toBe('plan');
47
+ expect(toLegacyPhase('CONSENSUS_MASTER_PLAN')).toBe('plan');
48
+ expect(toLegacyPhase('ARCHITECTURE')).toBe('plan');
49
+ expect(toLegacyPhase('CONSENSUS_ARCHITECTURE')).toBe('plan');
50
+ expect(toLegacyPhase('ROLE_PLANNING')).toBe('plan');
51
+ expect(toLegacyPhase('CONSENSUS_ROLE_PLANS')).toBe('plan');
52
+ });
53
+
54
+ it('should map execution phases to execution', () => {
55
+ expect(toLegacyPhase('IMPLEMENTATION')).toBe('execution');
56
+ expect(toLegacyPhase('QA_VALIDATION')).toBe('execution');
57
+ expect(toLegacyPhase('REVIEW')).toBe('execution');
58
+ expect(toLegacyPhase('AUDIT')).toBe('execution');
59
+ expect(toLegacyPhase('PRODUCTION_GATE')).toBe('execution');
60
+ expect(toLegacyPhase('RECOVERY_LOOP')).toBe('execution');
61
+ });
62
+
63
+ it('should map terminal phases to complete', () => {
64
+ expect(toLegacyPhase('DONE')).toBe('complete');
65
+ expect(toLegacyPhase('STUCK')).toBe('complete');
66
+ });
67
+ });
68
+
69
+ describe('migrateToPipelineState', () => {
70
+ it('should create default pipeline state with correct phase', () => {
71
+ const state = makeMinimalState({ phase: 'plan' });
72
+ const pipeline = migrateToPipelineState(state);
73
+
74
+ expect(pipeline.pipelinePhase).toBe('INTAKE');
75
+ expect(pipeline.artifacts).toEqual([]);
76
+ expect(pipeline.recoveryCount).toBe(0);
77
+ expect(pipeline.maxRecoveryIterations).toBe(5);
78
+ });
79
+
80
+ it('should map execution phase', () => {
81
+ const state = makeMinimalState({ phase: 'execution' });
82
+ const pipeline = migrateToPipelineState(state);
83
+ expect(pipeline.pipelinePhase).toBe('IMPLEMENTATION');
84
+ });
85
+
86
+ it('should map complete phase', () => {
87
+ const state = makeMinimalState({ phase: 'complete' });
88
+ const pipeline = migrateToPipelineState(state);
89
+ expect(pipeline.pipelinePhase).toBe('DONE');
90
+ });
91
+
92
+ it('should derive roles for typescript project', () => {
93
+ const state = makeMinimalState({ language: 'typescript' });
94
+ const pipeline = migrateToPipelineState(state);
95
+
96
+ expect(pipeline.activeRoles).toContain('DISPATCHER');
97
+ expect(pipeline.activeRoles).toContain('ARCHITECT');
98
+ expect(pipeline.activeRoles).toContain('BACKEND_PROGRAMMER');
99
+ expect(pipeline.activeRoles).not.toContain('FRONTEND_PROGRAMMER');
100
+ });
101
+
102
+ it('should derive roles for fullstack project', () => {
103
+ const state = makeMinimalState({ language: 'fullstack' });
104
+ const pipeline = migrateToPipelineState(state);
105
+
106
+ expect(pipeline.activeRoles).toContain('DB_EXPERT');
107
+ expect(pipeline.activeRoles).toContain('BACKEND_PROGRAMMER');
108
+ expect(pipeline.activeRoles).toContain('FRONTEND_PROGRAMMER');
109
+ expect(pipeline.activeRoles).toContain('WEBSITE_PROGRAMMER');
110
+ expect(pipeline.activeRoles).toContain('UI_UX_SPECIALIST');
111
+ });
112
+
113
+ it('should derive roles for website project', () => {
114
+ const state = makeMinimalState({ language: 'website' });
115
+ const pipeline = migrateToPipelineState(state);
116
+
117
+ expect(pipeline.activeRoles).toContain('WEBSITE_PROGRAMMER');
118
+ expect(pipeline.activeRoles).toContain('MARKETING_EXPERT');
119
+ expect(pipeline.activeRoles).toContain('SOCIAL_EXPERT');
120
+ expect(pipeline.activeRoles).not.toContain('DB_EXPERT');
121
+ });
122
+ });
123
+
124
+ describe('needsPipelineMigration', () => {
125
+ it('should return true when pipeline is missing', () => {
126
+ expect(needsPipelineMigration({ phase: 'plan' })).toBe(true);
127
+ });
128
+
129
+ it('should return false when pipeline exists', () => {
130
+ expect(needsPipelineMigration({ pipeline: {} })).toBe(false);
131
+ });
132
+ });
133
+ });