wave.py 45 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335
  1. """Setup helpers for wave execution drill scenarios.
  2. Each helper creates a test repository with a plan file that exercises a
  3. specific aspect of the wave decomposition algorithm:
  4. - create_wave_test_repo: full 5-task plan spanning 3 waves
  5. - create_wave_test_repo_minimal: smaller 3-task plan for faster runs
  6. - create_waves_file: full 5-task plan pre-decomposed to .waves.md
  7. - create_waves_file_minimal: 3-task plan pre-decomposed to .waves.md
  8. - create_waves_file_with_broken_task: 3-task plan where Task 3 is structurally
  9. impossible (exercises failure escalation)
  10. - create_false_overlap_repo: same filename in different directories
  11. - create_dependency_chain_repo: semantic (import-based) dependencies
  12. - create_conflict_surface_repo: implicit barrel-file conflicts
  13. """
  14. from __future__ import annotations
  15. from pathlib import Path
  16. from setup_helpers.base import _git
  17. # ----------------------------------------------------------------------------
  18. # Shared fixture content
  19. # ----------------------------------------------------------------------------
  20. PACKAGE_JSON = """\
  21. {
  22. "name": "wave-test-fixture",
  23. "version": "0.1.0",
  24. "private": true,
  25. "scripts": {
  26. "test": "jest",
  27. "lint": "echo 'no lint configured' && exit 0",
  28. "build": "tsc -p tsconfig.json"
  29. },
  30. "devDependencies": {
  31. "typescript": "^5.4.0",
  32. "jest": "^29.7.0",
  33. "@types/jest": "^29.5.12",
  34. "ts-jest": "^29.1.2"
  35. }
  36. }
  37. """
  38. TSCONFIG_JSON = """\
  39. {
  40. "compilerOptions": {
  41. "target": "ES2022",
  42. "module": "commonjs",
  43. "lib": ["ES2022"],
  44. "strict": true,
  45. "esModuleInterop": true,
  46. "skipLibCheck": true,
  47. "forceConsistentCasingInFileNames": true,
  48. "declaration": true,
  49. "outDir": "dist",
  50. "rootDir": "."
  51. },
  52. "include": ["src/**/*.ts", "tests/**/*.ts"],
  53. "exclude": ["node_modules", "dist"]
  54. }
  55. """
  56. # jest.config.js uses ts-jest preset so implementers can write TypeScript
  57. # test files that import from src/ without configuring anything themselves.
  58. # This is deliberately provided up-front so the implementer never has to
  59. # diagnose jest/ts-jest interop issues mid-task.
  60. JEST_CONFIG_JS = """\
  61. /** @type {import('jest').Config} */
  62. module.exports = {
  63. preset: 'ts-jest',
  64. testEnvironment: 'node',
  65. testMatch: ['<rootDir>/tests/**/*.test.ts'],
  66. rootDir: '.',
  67. moduleNameMapper: {
  68. '^@/(.*)$': '<rootDir>/src/$1',
  69. },
  70. };
  71. """
  72. CLAUDE_MD = """\
  73. # Project Commands
  74. **install**: npm ci
  75. **test**: npm test
  76. **lint**: npm run lint
  77. **build**: npm run build
  78. """
  79. README_MD = """\
  80. # Wave Test Fixture
  81. Synthetic project used by drill scenarios to exercise the wave decomposition
  82. algorithm. Do not edit by hand — this file is generated by
  83. `setup_helpers/wave.py`.
  84. """
  85. # ----------------------------------------------------------------------------
  86. # Internal helpers
  87. # ----------------------------------------------------------------------------
  88. def _init_base_repo(workdir: Path) -> None:
  89. """Create the base TypeScript repo on main with the standard fixture files."""
  90. workdir.mkdir(parents=True, exist_ok=True)
  91. _git(["git", "init", "-b", "main"], cwd=workdir)
  92. _git(["git", "config", "user.email", "drill@test.local"], cwd=workdir)
  93. _git(["git", "config", "user.name", "Drill Test"], cwd=workdir)
  94. (workdir / "package.json").write_text(PACKAGE_JSON)
  95. (workdir / "README.md").write_text(README_MD)
  96. (workdir / "tsconfig.json").write_text(TSCONFIG_JSON)
  97. (workdir / "jest.config.js").write_text(JEST_CONFIG_JS)
  98. (workdir / "CLAUDE.md").write_text(CLAUDE_MD)
  99. _git(
  100. ["git", "add", "package.json", "README.md", "tsconfig.json",
  101. "jest.config.js", "CLAUDE.md"],
  102. cwd=workdir,
  103. )
  104. _git(["git", "commit", "-m", "initial commit"], cwd=workdir)
  105. def _write_file(workdir: Path, rel_path: str, content: str) -> None:
  106. """Write a file, creating parent directories as needed."""
  107. target = workdir / rel_path
  108. target.parent.mkdir(parents=True, exist_ok=True)
  109. target.write_text(content)
  110. def _ensure_dir(workdir: Path, rel_path: str) -> None:
  111. """Create a directory and drop a .gitkeep so git can track it."""
  112. d = workdir / rel_path
  113. d.mkdir(parents=True, exist_ok=True)
  114. (d / ".gitkeep").write_text("")
  115. def _commit_all_on_feature_branch(workdir: Path) -> None:
  116. """Checkout feature/test-implementation and commit every remaining change."""
  117. _git(["git", "checkout", "-b", "feature/test-implementation"], cwd=workdir)
  118. _git(["git", "add", "-A"], cwd=workdir)
  119. _git(["git", "commit", "-m", "add wave test plan and fixtures"], cwd=workdir)
  120. # ----------------------------------------------------------------------------
  121. # Plan bodies
  122. # ----------------------------------------------------------------------------
  123. WAVE_TEST_PLAN = """\
  124. # Wave Decomposition Test Implementation Plan
  125. > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development
  126. > (recommended) or superpowers:executing-plans to implement this plan task-by-task.
  127. **Goal:** Exercise the full wave decomposition algorithm across 3 waves.
  128. **Architecture:** Foundation types feed independent services which are wired
  129. together by an API routes layer. This shape intentionally produces one
  130. sequential task in Wave 1, three parallel tasks in Wave 2, and one
  131. sequential integration task in Wave 3.
  132. **Tech Stack:** TypeScript, Jest.
  133. ---
  134. ### Task 1: Foundation types
  135. **Files:**
  136. - Create: `src/types/auth.ts`
  137. - Create: `src/types/users.ts`
  138. - Create: `src/types/billing.ts`
  139. - Modify: `src/types/index.ts`
  140. **Acceptance Criteria:**
  141. - `src/types/auth.ts` exports `User` and `Session` interfaces.
  142. - `src/types/users.ts` exports a `UserProfile` interface with `id` and `email`.
  143. - `src/types/billing.ts` exports `Plan` and `Subscription` interfaces.
  144. - `src/types/index.ts` re-exports everything from the three files above.
  145. - `npm run build` succeeds with no type errors.
  146. - [ ] **Step 1: Create src/types/auth.ts with User and Session interfaces.**
  147. - [ ] **Step 2: Create src/types/users.ts with UserProfile interface.**
  148. - [ ] **Step 3: Create src/types/billing.ts with Plan and Subscription interfaces.**
  149. - [ ] **Step 4: Update src/types/index.ts to re-export the three modules.**
  150. - [ ] **Step 5: Run `npm run build` and commit.**
  151. ---
  152. ### Task 2: Auth service
  153. **Files:**
  154. - Create: `src/services/auth.ts`
  155. - Create: `tests/auth.test.ts`
  156. **Acceptance Criteria:**
  157. - `src/services/auth.ts` exports an `AuthService` class with a `login(email, password)` method.
  158. - `AuthService.login` returns a `Session` imported from `src/types/auth.ts`.
  159. - `tests/auth.test.ts` covers the happy-path login case.
  160. - `tests/auth.test.ts` covers an invalid-credentials failure case.
  161. - `npm test -- tests/auth.test.ts` passes.
  162. - [ ] **Step 1: Write tests/auth.test.ts covering login success and failure.**
  163. - [ ] **Step 2: Implement src/services/auth.ts to make the tests pass.**
  164. - [ ] **Step 3: Run `npm test -- tests/auth.test.ts` and commit.**
  165. ---
  166. ### Task 3: Users service
  167. **Files:**
  168. - Create: `src/services/users.ts`
  169. - Create: `tests/users.test.ts`
  170. **Acceptance Criteria:**
  171. - `src/services/users.ts` exports a `UsersService` class with `getProfile(id)`.
  172. - `UsersService.getProfile` returns a `UserProfile` imported from `src/types/users.ts`.
  173. - `tests/users.test.ts` covers the happy-path lookup case.
  174. - `tests/users.test.ts` covers a not-found case.
  175. - `npm test -- tests/users.test.ts` passes.
  176. - [ ] **Step 1: Write tests/users.test.ts covering getProfile success and missing.**
  177. - [ ] **Step 2: Implement src/services/users.ts to make the tests pass.**
  178. - [ ] **Step 3: Run `npm test -- tests/users.test.ts` and commit.**
  179. ---
  180. ### Task 4: Billing service
  181. **Files:**
  182. - Create: `src/services/billing.ts`
  183. - Create: `tests/billing.test.ts`
  184. **Acceptance Criteria:**
  185. - `src/services/billing.ts` exports a `BillingService` class with `subscribe(userId, planId)`.
  186. - `BillingService.subscribe` returns a `Subscription` imported from `src/types/billing.ts`.
  187. - `tests/billing.test.ts` covers a successful subscription.
  188. - `tests/billing.test.ts` covers a failed subscription.
  189. - `npm test -- tests/billing.test.ts` passes.
  190. - [ ] **Step 1: Write tests/billing.test.ts covering subscribe success and failure.**
  191. - [ ] **Step 2: Implement src/services/billing.ts to make the tests pass.**
  192. - [ ] **Step 3: Run `npm test -- tests/billing.test.ts` and commit.**
  193. ---
  194. ### Task 5: API routes
  195. **Files:**
  196. - Create: `src/api/routes.ts`
  197. - Modify: `src/index.ts`
  198. **Acceptance Criteria:**
  199. - `src/api/routes.ts` imports `AuthService`, `UsersService`, and `BillingService`.
  200. - `src/api/routes.ts` exports a `registerRoutes(app)` function that wires the three services.
  201. - `src/index.ts` imports `registerRoutes` and calls it with the app.
  202. - `npm run build` succeeds.
  203. - `npm test` passes end to end.
  204. - [ ] **Step 1: Create src/api/routes.ts that composes the three services.**
  205. - [ ] **Step 2: Update src/index.ts to register the routes on startup.**
  206. - [ ] **Step 3: Run `npm run build && npm test` and commit.**
  207. """
  208. FALSE_OVERLAP_PLAN = """\
  209. # False Overlap Test Implementation Plan
  210. > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development
  211. > (recommended) or superpowers:executing-plans to implement this plan task-by-task.
  212. **Goal:** Verify that wave decomposition uses full paths, not bare filenames,
  213. when detecting file overlap between tasks.
  214. **Architecture:** Three fully-independent domains (auth, users, billing) each
  215. define a locally-scoped `types.ts`. A decomposer that keys on filename alone
  216. would serialize these tasks. A correct decomposer keys on full paths and
  217. parallelizes them.
  218. **Tech Stack:** TypeScript, Jest.
  219. ---
  220. ### Task 1: Auth domain scaffolding
  221. **Files:**
  222. - Create: `src/auth/types.ts`
  223. - Create: `src/auth/service.ts`
  224. **Acceptance Criteria:**
  225. - `src/auth/types.ts` exports an `AuthToken` interface local to the auth domain.
  226. - `src/auth/service.ts` exports an `AuthService` class that uses `AuthToken`.
  227. - Nothing outside `src/auth/` is touched.
  228. - `npm run build` succeeds.
  229. - [ ] **Step 1: Create src/auth/types.ts with AuthToken.**
  230. - [ ] **Step 2: Create src/auth/service.ts importing AuthToken locally.**
  231. - [ ] **Step 3: Run `npm run build` and commit.**
  232. ---
  233. ### Task 2: Users domain scaffolding
  234. **Files:**
  235. - Create: `src/users/types.ts`
  236. - Create: `src/users/service.ts`
  237. **Acceptance Criteria:**
  238. - `src/users/types.ts` exports a `UserRecord` interface local to the users domain.
  239. - `src/users/service.ts` exports a `UsersService` class that uses `UserRecord`.
  240. - Nothing outside `src/users/` is touched.
  241. - `npm run build` succeeds.
  242. - [ ] **Step 1: Create src/users/types.ts with UserRecord.**
  243. - [ ] **Step 2: Create src/users/service.ts importing UserRecord locally.**
  244. - [ ] **Step 3: Run `npm run build` and commit.**
  245. ---
  246. ### Task 3: Billing domain scaffolding
  247. **Files:**
  248. - Create: `src/billing/types.ts`
  249. - Create: `src/billing/service.ts`
  250. **Acceptance Criteria:**
  251. - `src/billing/types.ts` exports an `Invoice` interface local to the billing domain.
  252. - `src/billing/service.ts` exports a `BillingService` class that uses `Invoice`.
  253. - Nothing outside `src/billing/` is touched.
  254. - `npm run build` succeeds.
  255. - [ ] **Step 1: Create src/billing/types.ts with Invoice.**
  256. - [ ] **Step 2: Create src/billing/service.ts importing Invoice locally.**
  257. - [ ] **Step 3: Run `npm run build` and commit.**
  258. """
  259. DEPENDENCY_CHAIN_PLAN = """\
  260. # Dependency Chain Test Implementation Plan
  261. > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development
  262. > (recommended) or superpowers:executing-plans to implement this plan task-by-task.
  263. **Goal:** Verify that wave decomposition detects semantic (import-based)
  264. dependencies, not just file-overlap dependencies.
  265. **Architecture:** Two independent type modules (auth, billing) can be built
  266. in parallel. A session service consumes the auth types but never touches
  267. the billing types — the decomposer should recognize this asymmetric
  268. dependency via the import, even though there is no file overlap.
  269. **Tech Stack:** TypeScript, Jest.
  270. ---
  271. ### Task 1: Create auth types
  272. **Files:**
  273. - Create: `src/types/auth.ts`
  274. **Acceptance Criteria:**
  275. - `src/types/auth.ts` exports a `User` interface with `id` and `email`.
  276. - `src/types/auth.ts` exports a `Session` interface with `userId` and `token`.
  277. - No other file is modified.
  278. - `npm run build` succeeds.
  279. - [ ] **Step 1: Create src/types/auth.ts with User and Session interfaces.**
  280. - [ ] **Step 2: Run `npm run build` and commit.**
  281. ---
  282. ### Task 2: Create billing types
  283. **Files:**
  284. - Create: `src/types/billing.ts`
  285. **Acceptance Criteria:**
  286. - `src/types/billing.ts` exports a `Plan` interface with `id` and `price`.
  287. - `src/types/billing.ts` exports a `Subscription` interface with `userId` and `planId`.
  288. - No other file is modified.
  289. - `npm run build` succeeds.
  290. - [ ] **Step 1: Create src/types/billing.ts with Plan and Subscription interfaces.**
  291. - [ ] **Step 2: Run `npm run build` and commit.**
  292. ---
  293. ### Task 3: Create session service
  294. **Files:**
  295. - Create: `src/services/session.ts`
  296. **Acceptance Criteria:**
  297. - `src/services/session.ts` **imports** `User` and `Session` from `src/types/auth.ts`.
  298. - `src/services/session.ts` does **not** import from `src/types/billing.ts`.
  299. - `src/services/session.ts` does **not** modify `src/types/auth.ts`.
  300. - `src/services/session.ts` exports a `SessionService` class with `create(user: User): Session`.
  301. - `npm run build` succeeds.
  302. - [ ] **Step 1: Create src/services/session.ts importing User and Session from ../types/auth.**
  303. - [ ] **Step 2: Implement SessionService.create.**
  304. - [ ] **Step 3: Run `npm run build` and commit.**
  305. """
  306. WAVE_TEST_PLAN_MINIMAL = """\
  307. # Wave Execution Minimal Test Implementation Plan
  308. > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development
  309. > (recommended) or superpowers:executing-plans to implement this plan task-by-task.
  310. **Goal:** Exercise wave execution across two waves with the smallest
  311. possible surface — a single foundation task followed by two independent
  312. parallel services.
  313. **Architecture:** Foundation types feed two independent, parallel
  314. utility services. This produces one sequential task in Wave 1 and two
  315. parallel tasks in Wave 2.
  316. **Tech Stack:** TypeScript, Jest.
  317. ---
  318. ### Task 1: Foundation types
  319. **Files:**
  320. - Create: `src/types/core.ts`
  321. - Modify: `src/types/index.ts`
  322. **Acceptance Criteria:**
  323. - `src/types/core.ts` exports a `User` interface with `id` and `email`.
  324. - `src/types/core.ts` exports a `Session` interface with `userId` and `token`.
  325. - `src/types/index.ts` re-exports everything from `src/types/core.ts`.
  326. - `npm run build` succeeds with no type errors.
  327. - [ ] **Step 1: Create src/types/core.ts with User and Session interfaces.**
  328. - [ ] **Step 2: Update src/types/index.ts to re-export from ./core.**
  329. - [ ] **Step 3: Run `npm run build` and commit.**
  330. ---
  331. ### Task 2: Logger service
  332. **Files:**
  333. - Create: `src/services/logger.ts`
  334. - Create: `tests/logger.test.ts`
  335. **Acceptance Criteria:**
  336. - `src/services/logger.ts` exports a `Logger` class with an `info(message: string)` method.
  337. - `Logger.info` appends a timestamped entry to an internal buffer.
  338. - `tests/logger.test.ts` covers a happy-path info case.
  339. - `tests/logger.test.ts` covers a repeated-call buffering case.
  340. - `npm test -- tests/logger.test.ts` passes.
  341. - [ ] **Step 1: Write tests/logger.test.ts covering info and buffering.**
  342. - [ ] **Step 2: Implement src/services/logger.ts to make the tests pass.**
  343. - [ ] **Step 3: Run `npm test -- tests/logger.test.ts` and commit.**
  344. ---
  345. ### Task 3: Clock service
  346. **Files:**
  347. - Create: `src/services/clock.ts`
  348. - Create: `tests/clock.test.ts`
  349. **Acceptance Criteria:**
  350. - `src/services/clock.ts` exports a `Clock` class with a `now(): number` method.
  351. - `Clock.now` returns the current Unix timestamp in milliseconds.
  352. - `tests/clock.test.ts` covers a happy-path now case.
  353. - `tests/clock.test.ts` covers the return value being a finite number.
  354. - `npm test -- tests/clock.test.ts` passes.
  355. - [ ] **Step 1: Write tests/clock.test.ts covering now success and type.**
  356. - [ ] **Step 2: Implement src/services/clock.ts to make the tests pass.**
  357. - [ ] **Step 3: Run `npm test -- tests/clock.test.ts` and commit.**
  358. """
  359. CONFLICT_SURFACE_PLAN = """\
  360. # Conflict Surface Test Implementation Plan
  361. > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development
  362. > (recommended) or superpowers:executing-plans to implement this plan task-by-task.
  363. **Goal:** Verify the conflict-surface heuristic catches implicit barrel-file
  364. modifications that the task file lists intentionally omit.
  365. **Architecture:** `src/services/index.ts` exists as a barrel file before the
  366. plan runs. Each task creates a new service module and needs to add an
  367. export line to `src/services/index.ts`, but the task Files list only
  368. names the new module. A pure file-overlap decomposer would parallelize
  369. these tasks; the conflict-surface heuristic should recognize that every
  370. task needs to touch the barrel file and either serialize them or add the
  371. barrel file to each task's list.
  372. **Tech Stack:** TypeScript, Jest.
  373. ---
  374. ### Task 1: Create auth service
  375. **Files:**
  376. - Create: `src/services/auth.ts`
  377. **Acceptance Criteria:**
  378. - `src/services/auth.ts` exports an `AuthService` class with a `login` method.
  379. - `AuthService` is re-exported from `src/services/index.ts` (add export to index).
  380. - Importing `AuthService` from `src/services` works at build time.
  381. - `npm run build` succeeds.
  382. - [ ] **Step 1: Create src/services/auth.ts with AuthService.**
  383. - [ ] **Step 2: Add `export * from './auth';` to src/services/index.ts.**
  384. - [ ] **Step 3: Run `npm run build` and commit.**
  385. ---
  386. ### Task 2: Create users service
  387. **Files:**
  388. - Create: `src/services/users.ts`
  389. **Acceptance Criteria:**
  390. - `src/services/users.ts` exports a `UsersService` class with a `getProfile` method.
  391. - `UsersService` is re-exported from `src/services/index.ts` (add export to index).
  392. - Importing `UsersService` from `src/services` works at build time.
  393. - `npm run build` succeeds.
  394. - [ ] **Step 1: Create src/services/users.ts with UsersService.**
  395. - [ ] **Step 2: Add `export * from './users';` to src/services/index.ts.**
  396. - [ ] **Step 3: Run `npm run build` and commit.**
  397. ---
  398. ### Task 3: Create billing service
  399. **Files:**
  400. - Create: `src/services/billing.ts`
  401. **Acceptance Criteria:**
  402. - `src/services/billing.ts` exports a `BillingService` class with a `subscribe` method.
  403. - `BillingService` is re-exported from `src/services/index.ts` (add export to index).
  404. - Importing `BillingService` from `src/services` works at build time.
  405. - `npm run build` succeeds.
  406. - [ ] **Step 1: Create src/services/billing.ts with BillingService.**
  407. - [ ] **Step 2: Add `export * from './billing';` to src/services/index.ts.**
  408. - [ ] **Step 3: Run `npm run build` and commit.**
  409. """
  410. # ----------------------------------------------------------------------------
  411. # Public helpers
  412. # ----------------------------------------------------------------------------
  413. def create_wave_test_repo(workdir: Path) -> None:
  414. """Create a 5-task plan exercising the full wave decomposition algorithm.
  415. Expected decomposition:
  416. - Wave 1: Task 1 (foundation types)
  417. - Wave 2: Tasks 2, 3, 4 (parallel, independent service implementations)
  418. - Wave 3: Task 5 (API routes integration, depends on services)
  419. """
  420. workdir = Path(workdir)
  421. _init_base_repo(workdir)
  422. # Pre-create the barrel file and stub directories the plan references.
  423. _write_file(workdir, "src/types/index.ts", "export {};\n")
  424. for d in ("src/auth", "src/users", "src/billing", "src/api", "tests"):
  425. _ensure_dir(workdir, d)
  426. _write_file(workdir, "docs/superpowers/plans/test-plan.md", WAVE_TEST_PLAN)
  427. _commit_all_on_feature_branch(workdir)
  428. def create_false_overlap_repo(workdir: Path) -> None:
  429. """Create a plan where three tasks share a filename but no full-path overlap.
  430. Expected decomposition:
  431. - Wave 1: Tasks 1, 2, 3 all parallel (no true file overlap)
  432. """
  433. workdir = Path(workdir)
  434. _init_base_repo(workdir)
  435. for d in ("src/auth", "src/users", "src/billing"):
  436. _ensure_dir(workdir, d)
  437. _write_file(workdir, "docs/superpowers/plans/test-plan.md", FALSE_OVERLAP_PLAN)
  438. _commit_all_on_feature_branch(workdir)
  439. def create_dependency_chain_repo(workdir: Path) -> None:
  440. """Create a plan where Task 3 semantically depends on Task 1 via imports.
  441. Expected decomposition:
  442. - Wave 1: Tasks 1, 2 (parallel — independent type modules)
  443. - Wave 2: Task 3 (depends on Task 1's src/types/auth.ts)
  444. """
  445. workdir = Path(workdir)
  446. _init_base_repo(workdir)
  447. _ensure_dir(workdir, "src/types")
  448. _ensure_dir(workdir, "src/services")
  449. _write_file(workdir, "docs/superpowers/plans/test-plan.md", DEPENDENCY_CHAIN_PLAN)
  450. _commit_all_on_feature_branch(workdir)
  451. def create_wave_test_repo_minimal(workdir: Path) -> None:
  452. """Create a 3-task plan exercising wave execution with minimal surface.
  453. Expected decomposition:
  454. - Wave 1: Task 1 (foundation types)
  455. - Wave 2: Tasks 2, 3 (parallel, independent logger + clock services)
  456. """
  457. workdir = Path(workdir)
  458. _init_base_repo(workdir)
  459. # Pre-create the barrel file and stub directories the plan references.
  460. _write_file(workdir, "src/types/index.ts", "export {};\n")
  461. for d in ("src/services", "tests"):
  462. _ensure_dir(workdir, d)
  463. _write_file(workdir, "docs/superpowers/plans/test-plan.md", WAVE_TEST_PLAN_MINIMAL)
  464. _commit_all_on_feature_branch(workdir)
  465. # ----------------------------------------------------------------------------
  466. # Pre-decomposed waves files
  467. # ----------------------------------------------------------------------------
  468. WAVE_TEST_SPEC = """\
  469. # Wave Decomposition Test Specification
  470. ## Overview
  471. This specification describes a synthetic TypeScript project used to exercise
  472. the full wave execution pipeline. The feature is a small, illustrative API
  473. surface composed of three independent services (auth, users, billing) wired
  474. together behind a thin routes layer. It exists solely so drill scenarios can
  475. verify that an agent correctly runs an already-decomposed waves file from
  476. start to finish.
  477. ## Scope
  478. The spec covers:
  479. - A shared types module that declares the core domain interfaces.
  480. - Three independent service classes, each with a small happy-path and
  481. failure-path test suite.
  482. - An API routes module that composes the three services.
  483. ## Non-goals
  484. - Real persistence, real HTTP handling, real authentication. The exercise is
  485. purely about wave execution mechanics, not production-quality code.
  486. """
  487. WAVE_TEST_SPEC_MINIMAL = """\
  488. # Minimal Wave Execution Test Specification
  489. ## Overview
  490. This specification describes a minimal TypeScript project used to exercise
  491. the wave execution pipeline with the smallest possible task surface. The
  492. feature is a tiny utility layer composed of two independent services
  493. (logger, clock) built on top of a shared types module.
  494. ## Scope
  495. The spec covers:
  496. - A shared types module that declares `User` and `Session` interfaces.
  497. - A logger service with a buffered `info` method.
  498. - A clock service with a `now()` method returning the current Unix
  499. timestamp in milliseconds.
  500. ## Non-goals
  501. - Log rotation, log transport, time sources other than `Date.now()`, or
  502. any production-grade concerns. The fixture exists purely to exercise
  503. wave execution over a small set of parallelizable tasks.
  504. """
  505. WAVE_TEST_WAVES_FULL = """\
  506. ---
  507. run_id: testw5
  508. source_plan: docs/superpowers/plans/test-plan.md
  509. spec_path: docs/superpowers/specs/test-spec.md
  510. feature_branch: feature/test-implementation
  511. status: approved
  512. sequential_time: 8h
  513. parallel_time: 4h
  514. savings: 50%
  515. waves:
  516. - {wave: 1, strategy: sequential, tasks: [1], depends_on: []}
  517. - {wave: 2, strategy: parallel, tasks: [2, 3, 4], depends_on: [1]}
  518. - {wave: 3, strategy: sequential, tasks: [5], depends_on: [2, 3, 4]}
  519. ---
  520. # Wave Decomposition Test — Waves File
  521. ## Waves Overview
  522. | Wave | Strategy | Tasks | Depends On | Notes |
  523. |------|------------|-----------|------------|-----------------------------------------|
  524. | 1 | sequential | 1 | — | Foundation types, must land first |
  525. | 2 | parallel | 2, 3, 4 | 1 | Independent service implementations |
  526. | 3 | sequential | 5 | 2, 3, 4 | API routes integration glue |
  527. **Sequential time estimate:** 8h
  528. **Parallel time estimate:** 4h
  529. **Savings:** 50%
  530. ---
  531. ## Wave 1 — Foundation (sequential)
  532. Task 1 must land before any service work can begin because every Wave 2
  533. service imports from `src/types/index.ts`.
  534. ### Task 1: Foundation types
  535. **Files:**
  536. - Create: `src/types/auth.ts`
  537. - Create: `src/types/users.ts`
  538. - Create: `src/types/billing.ts`
  539. - Modify: `src/types/index.ts`
  540. **Acceptance Criteria:**
  541. - `src/types/auth.ts` exports `User` and `Session` interfaces.
  542. - `src/types/users.ts` exports a `UserProfile` interface with `id` and `email`.
  543. - `src/types/billing.ts` exports `Plan` and `Subscription` interfaces.
  544. - `src/types/index.ts` re-exports everything from the three files above.
  545. - `npm run build` succeeds with no type errors.
  546. - [ ] **Step 1: Create src/types/auth.ts with User and Session interfaces.**
  547. - [ ] **Step 2: Create src/types/users.ts with UserProfile interface.**
  548. - [ ] **Step 3: Create src/types/billing.ts with Plan and Subscription interfaces.**
  549. - [ ] **Step 4: Update src/types/index.ts to re-export the three modules.**
  550. - [ ] **Step 5: Run `npm run build` and commit.**
  551. ---
  552. ## Wave 2 — Independent services (parallel)
  553. Tasks 2, 3, and 4 have no file overlap and no cross-task imports; they
  554. can be executed in parallel in isolated worktrees and merged at the
  555. wave boundary.
  556. ### File ownership
  557. ```
  558. Task 2 (auth service):
  559. - src/services/auth.ts [create]
  560. - tests/auth.test.ts [create]
  561. Task 3 (users service):
  562. - src/services/users.ts [create]
  563. - tests/users.test.ts [create]
  564. Task 4 (billing service):
  565. - src/services/billing.ts [create]
  566. - tests/billing.test.ts [create]
  567. ```
  568. No two tasks in Wave 2 touch the same path.
  569. ### Task 2: Auth service
  570. **Files:**
  571. - Create: `src/services/auth.ts`
  572. - Create: `tests/auth.test.ts`
  573. **Acceptance Criteria:**
  574. - `src/services/auth.ts` exports an `AuthService` class with a `login(email, password)` method.
  575. - `AuthService.login` returns a `Session` imported from `src/types/auth.ts`.
  576. - `tests/auth.test.ts` covers the happy-path login case.
  577. - `tests/auth.test.ts` covers an invalid-credentials failure case.
  578. - `npm test -- tests/auth.test.ts` passes.
  579. - [ ] **Step 1: Write tests/auth.test.ts covering login success and failure.**
  580. - [ ] **Step 2: Implement src/services/auth.ts to make the tests pass.**
  581. - [ ] **Step 3: Run `npm test -- tests/auth.test.ts` and commit.**
  582. ### Task 3: Users service
  583. **Files:**
  584. - Create: `src/services/users.ts`
  585. - Create: `tests/users.test.ts`
  586. **Acceptance Criteria:**
  587. - `src/services/users.ts` exports a `UsersService` class with `getProfile(id)`.
  588. - `UsersService.getProfile` returns a `UserProfile` imported from `src/types/users.ts`.
  589. - `tests/users.test.ts` covers the happy-path lookup case.
  590. - `tests/users.test.ts` covers a not-found case.
  591. - `npm test -- tests/users.test.ts` passes.
  592. - [ ] **Step 1: Write tests/users.test.ts covering getProfile success and missing.**
  593. - [ ] **Step 2: Implement src/services/users.ts to make the tests pass.**
  594. - [ ] **Step 3: Run `npm test -- tests/users.test.ts` and commit.**
  595. ### Task 4: Billing service
  596. **Files:**
  597. - Create: `src/services/billing.ts`
  598. - Create: `tests/billing.test.ts`
  599. **Acceptance Criteria:**
  600. - `src/services/billing.ts` exports a `BillingService` class with `subscribe(userId, planId)`.
  601. - `BillingService.subscribe` returns a `Subscription` imported from `src/types/billing.ts`.
  602. - `tests/billing.test.ts` covers a successful subscription.
  603. - `tests/billing.test.ts` covers a failed subscription.
  604. - `npm test -- tests/billing.test.ts` passes.
  605. - [ ] **Step 1: Write tests/billing.test.ts covering subscribe success and failure.**
  606. - [ ] **Step 2: Implement src/services/billing.ts to make the tests pass.**
  607. - [ ] **Step 3: Run `npm test -- tests/billing.test.ts` and commit.**
  608. ---
  609. ## Wave 3 — Integration (sequential)
  610. Task 5 depends on every Wave 2 service being merged; it can only start
  611. once Wave 2 is fully integrated onto the feature branch.
  612. ### Task 5: API routes
  613. **Files:**
  614. - Create: `src/api/routes.ts`
  615. - Modify: `src/index.ts`
  616. **Acceptance Criteria:**
  617. - `src/api/routes.ts` imports `AuthService`, `UsersService`, and `BillingService`.
  618. - `src/api/routes.ts` exports a `registerRoutes(app)` function that wires the three services.
  619. - `src/index.ts` imports `registerRoutes` and calls it with the app.
  620. - `npm run build` succeeds.
  621. - `npm test` passes end to end.
  622. - [ ] **Step 1: Create src/api/routes.ts that composes the three services.**
  623. - [ ] **Step 2: Update src/index.ts to register the routes on startup.**
  624. - [ ] **Step 3: Run `npm run build && npm test` and commit.**
  625. """
  626. WAVE_TEST_WAVES_MINIMAL = """\
  627. ---
  628. run_id: testw3
  629. source_plan: docs/superpowers/plans/test-plan.md
  630. spec_path: docs/superpowers/specs/test-spec.md
  631. feature_branch: feature/test-implementation
  632. status: approved
  633. sequential_time: 3h
  634. parallel_time: 2h
  635. savings: 33%
  636. waves:
  637. - {wave: 1, strategy: sequential, tasks: [1], depends_on: []}
  638. - {wave: 2, strategy: parallel, tasks: [2, 3], depends_on: [1]}
  639. ---
  640. # Minimal Wave Execution — Waves File
  641. ## Waves Overview
  642. | Wave | Strategy | Tasks | Depends On | Notes |
  643. |------|------------|-------|------------|-----------------------------------|
  644. | 1 | sequential | 1 | — | Foundation types, must land first |
  645. | 2 | parallel | 2, 3 | 1 | Independent logger + clock |
  646. **Sequential time estimate:** 3h
  647. **Parallel time estimate:** 2h
  648. **Savings:** 33%
  649. ---
  650. ## Wave 1 — Foundation (sequential)
  651. ### Task 1: Foundation types
  652. **Files:**
  653. - Create: `src/types/core.ts`
  654. - Modify: `src/types/index.ts`
  655. **Acceptance Criteria:**
  656. - `src/types/core.ts` exports a `User` interface with `id` and `email`.
  657. - `src/types/core.ts` exports a `Session` interface with `userId` and `token`.
  658. - `src/types/index.ts` re-exports everything from `src/types/core.ts`.
  659. - `npm run build` succeeds with no type errors.
  660. - [ ] **Step 1: Create src/types/core.ts with User and Session interfaces.**
  661. - [ ] **Step 2: Update src/types/index.ts to re-export from ./core.**
  662. - [ ] **Step 3: Run `npm run build` and commit.**
  663. ---
  664. ## Wave 2 — Independent services (parallel)
  665. Tasks 2 and 3 have no file overlap and no cross-task imports; they can
  666. be executed in parallel in isolated worktrees and merged at the wave
  667. boundary.
  668. ### File ownership
  669. ```
  670. Task 2 (logger service):
  671. - src/services/logger.ts [create]
  672. - tests/logger.test.ts [create]
  673. Task 3 (clock service):
  674. - src/services/clock.ts [create]
  675. - tests/clock.test.ts [create]
  676. ```
  677. No two tasks in Wave 2 touch the same path.
  678. ### Task 2: Logger service
  679. **Files:**
  680. - Create: `src/services/logger.ts`
  681. - Create: `tests/logger.test.ts`
  682. **Acceptance Criteria:**
  683. - `src/services/logger.ts` exports a `Logger` class with an `info(message: string)` method.
  684. - `Logger.info` appends a timestamped entry to an internal buffer.
  685. - `tests/logger.test.ts` covers a happy-path info case.
  686. - `tests/logger.test.ts` covers a repeated-call buffering case.
  687. - `npm test -- tests/logger.test.ts` passes.
  688. - [ ] **Step 1: Write tests/logger.test.ts covering info and buffering.**
  689. - [ ] **Step 2: Implement src/services/logger.ts to make the tests pass.**
  690. - [ ] **Step 3: Run `npm test -- tests/logger.test.ts` and commit.**
  691. ### Task 3: Clock service
  692. **Files:**
  693. - Create: `src/services/clock.ts`
  694. - Create: `tests/clock.test.ts`
  695. **Acceptance Criteria:**
  696. - `src/services/clock.ts` exports a `Clock` class with a `now(): number` method.
  697. - `Clock.now` returns the current Unix timestamp in milliseconds.
  698. - `tests/clock.test.ts` covers a happy-path now case.
  699. - `tests/clock.test.ts` covers the return value being a finite number.
  700. - `npm test -- tests/clock.test.ts` passes.
  701. - [ ] **Step 1: Write tests/clock.test.ts covering now success and type.**
  702. - [ ] **Step 2: Implement src/services/clock.ts to make the tests pass.**
  703. - [ ] **Step 3: Run `npm test -- tests/clock.test.ts` and commit.**
  704. """
  705. WAVE_TEST_PLAN_BROKEN_TASK = """\
  706. # Wave Execution Failure Test Implementation Plan
  707. > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development
  708. > (recommended) or superpowers:executing-plans to implement this plan task-by-task.
  709. **Goal:** Exercise wave execution's failure escalation path. Tasks 1 and 2
  710. should succeed normally. Task 3 is **structurally impossible** — its
  711. pre-existing test file contains mutually contradictory assertions that
  712. no implementation can satisfy, and the task scope explicitly forbids
  713. modifying the test file.
  714. **Architecture:** Foundation types feed two parallel services. The second
  715. parallel service (Task 3) is wired up so that the orchestrator must
  716. detect a real failure, retry once, and escalate to the user.
  717. **Tech Stack:** TypeScript, Jest.
  718. ---
  719. ### Task 1: Foundation types
  720. **Files:**
  721. - Create: `src/types/core.ts`
  722. - Modify: `src/types/index.ts`
  723. **Acceptance Criteria:**
  724. - `src/types/core.ts` exports a `User` interface with `id` and `email`.
  725. - `src/types/core.ts` exports a `Session` interface with `userId` and `token`.
  726. - `src/types/index.ts` re-exports everything from `src/types/core.ts`.
  727. - `npm run build` succeeds with no type errors.
  728. - [ ] **Step 1: Create src/types/core.ts with User and Session interfaces.**
  729. - [ ] **Step 2: Update src/types/index.ts to re-export from ./core.**
  730. - [ ] **Step 3: Run `npm run build` and commit.**
  731. ---
  732. ### Task 2: Logger service
  733. **Files:**
  734. - Create: `src/services/logger.ts`
  735. - Create: `tests/logger.test.ts`
  736. **Acceptance Criteria:**
  737. - `src/services/logger.ts` exports a `Logger` class with an `info(message: string)` method.
  738. - `Logger.info` appends a timestamped entry to an internal buffer.
  739. - `tests/logger.test.ts` covers a happy-path info case.
  740. - `tests/logger.test.ts` covers a repeated-call buffering case.
  741. - `npm test -- tests/logger.test.ts` passes.
  742. - [ ] **Step 1: Write tests/logger.test.ts covering info and buffering.**
  743. - [ ] **Step 2: Implement src/services/logger.ts to make the tests pass.**
  744. - [ ] **Step 3: Run `npm test -- tests/logger.test.ts` and commit.**
  745. ---
  746. ### Task 3: Counter service (impossible — DO NOT modify test file)
  747. **Files:**
  748. - Create: `src/services/counter.ts`
  749. - Pre-existing (DO NOT modify): `tests/counter.test.ts`
  750. **Acceptance Criteria:**
  751. - `src/services/counter.ts` exports a `Counter` class with a `compute(n: number): number` method.
  752. - `tests/counter.test.ts` already exists. **You MUST NOT modify or delete it.**
  753. - `npm test -- tests/counter.test.ts` passes against the pre-existing test file.
  754. - `npm run build` succeeds.
  755. - [ ] **Step 1: Read the pre-existing tests/counter.test.ts and understand its assertions.**
  756. - [ ] **Step 2: Implement src/services/counter.ts to satisfy every assertion in the existing test file (without changing the test file).**
  757. - [ ] **Step 3: Run `npm test -- tests/counter.test.ts` and `npm run build`, then commit.**
  758. """
  759. # Pre-existing test file for the broken Task 3. This file is committed
  760. # to the repo BEFORE the implementer runs. It contains mutually
  761. # contradictory assertions: compute(1) is asserted to equal both 1 AND
  762. # 2 in two separate `it` blocks. No implementation of `compute` can
  763. # satisfy both assertions simultaneously, so `npm test` will always
  764. # report a failing test for one of the two cases, no matter what the
  765. # implementer writes. The implementer cannot modify the test file
  766. # because the task acceptance criteria explicitly forbid it. This
  767. # produces a structural failure that the orchestrator must detect.
  768. COUNTER_FAILING_TEST = """\
  769. import { Counter } from '../src/services/counter';
  770. describe('Counter', () => {
  771. // The two assertions below are mutually contradictory by design.
  772. // No implementation of compute(n) can make both tests pass at once,
  773. // and the task scope forbids modifying this file. The orchestrator
  774. // should detect the failure, retry once, then escalate to the user.
  775. it('compute(1) returns 1', () => {
  776. const counter = new Counter();
  777. expect(counter.compute(1)).toBe(1);
  778. });
  779. it('compute(1) returns 2', () => {
  780. const counter = new Counter();
  781. expect(counter.compute(1)).toBe(2);
  782. });
  783. });
  784. """
  785. WAVE_TEST_SPEC_BROKEN_TASK = """\
  786. # Wave Execution Failure Test Specification
  787. ## Overview
  788. This specification describes a synthetic TypeScript project used to
  789. exercise the wave execution skill's failure-handling and escalation
  790. path. It is intentionally constructed so that one task in a parallel
  791. wave cannot succeed.
  792. ## Scope
  793. The spec covers:
  794. - A shared types module that declares `User` and `Session` interfaces.
  795. - A logger service with a buffered `info` method (Task 2 — should pass).
  796. - A counter service whose pre-existing test file contains mutually
  797. contradictory assertions (Task 3 — must fail).
  798. ## Non-goals
  799. - A working counter service. Task 3 is a deliberate failure injection,
  800. not a real feature. The fixture exists purely to verify that the
  801. orchestrator detects the failure, retries once per the failure
  802. handling matrix, and escalates to the user instead of silently
  803. proceeding.
  804. """
  805. WAVE_TEST_WAVES_BROKEN_TASK = """\
  806. ---
  807. run_id: testfwf
  808. source_plan: docs/superpowers/plans/test-plan.md
  809. spec_path: docs/superpowers/specs/test-spec.md
  810. feature_branch: feature/test-implementation
  811. status: approved
  812. sequential_time: 3h
  813. parallel_time: 2h
  814. savings: 33%
  815. waves:
  816. - {wave: 1, strategy: sequential, tasks: [1], depends_on: []}
  817. - {wave: 2, strategy: parallel, tasks: [2, 3], depends_on: [1]}
  818. ---
  819. # Wave Execution Failure Test — Waves File
  820. ## Waves Overview
  821. | Wave | Strategy | Tasks | Depends On | Notes |
  822. |------|------------|-------|------------|------------------------------------------------|
  823. | 1 | sequential | 1 | — | Foundation types, must land first |
  824. | 2 | parallel | 2, 3 | 1 | Logger (passes) + Counter (structurally fails) |
  825. **Sequential time estimate:** 3h
  826. **Parallel time estimate:** 2h
  827. **Savings:** 33%
  828. ---
  829. ## Wave 1 — Foundation (sequential)
  830. ### Task 1: Foundation types
  831. **Files:**
  832. - Create: `src/types/core.ts`
  833. - Modify: `src/types/index.ts`
  834. **Acceptance Criteria:**
  835. - `src/types/core.ts` exports a `User` interface with `id` and `email`.
  836. - `src/types/core.ts` exports a `Session` interface with `userId` and `token`.
  837. - `src/types/index.ts` re-exports everything from `src/types/core.ts`.
  838. - `npm run build` succeeds with no type errors.
  839. - [ ] **Step 1: Create src/types/core.ts with User and Session interfaces.**
  840. - [ ] **Step 2: Update src/types/index.ts to re-export from ./core.**
  841. - [ ] **Step 3: Run `npm run build` and commit.**
  842. ---
  843. ## Wave 2 — Independent services (parallel)
  844. Tasks 2 and 3 have no file overlap and no cross-task imports; they can
  845. be executed in parallel in isolated worktrees and merged at the wave
  846. boundary.
  847. ### File ownership
  848. ```
  849. Task 2 (logger service):
  850. - src/services/logger.ts [create]
  851. - tests/logger.test.ts [create]
  852. Task 3 (counter service):
  853. - src/services/counter.ts [create]
  854. - tests/counter.test.ts [pre-existing — DO NOT modify]
  855. ```
  856. No two tasks in Wave 2 touch the same path.
  857. ### Task 2: Logger service
  858. **Files:**
  859. - Create: `src/services/logger.ts`
  860. - Create: `tests/logger.test.ts`
  861. **Acceptance Criteria:**
  862. - `src/services/logger.ts` exports a `Logger` class with an `info(message: string)` method.
  863. - `Logger.info` appends a timestamped entry to an internal buffer.
  864. - `tests/logger.test.ts` covers a happy-path info case.
  865. - `tests/logger.test.ts` covers a repeated-call buffering case.
  866. - `npm test -- tests/logger.test.ts` passes.
  867. - [ ] **Step 1: Write tests/logger.test.ts covering info and buffering.**
  868. - [ ] **Step 2: Implement src/services/logger.ts to make the tests pass.**
  869. - [ ] **Step 3: Run `npm test -- tests/logger.test.ts` and commit.**
  870. ### Task 3: Counter service (impossible — DO NOT modify test file)
  871. **Files:**
  872. - Create: `src/services/counter.ts`
  873. - Pre-existing (DO NOT modify): `tests/counter.test.ts`
  874. **Acceptance Criteria:**
  875. - `src/services/counter.ts` exports a `Counter` class with a `compute(n: number): number` method.
  876. - `tests/counter.test.ts` already exists. **You MUST NOT modify or delete it.**
  877. - `npm test -- tests/counter.test.ts` passes against the pre-existing test file.
  878. - `npm run build` succeeds.
  879. - [ ] **Step 1: Read the pre-existing tests/counter.test.ts and understand its assertions.**
  880. - [ ] **Step 2: Implement src/services/counter.ts to satisfy every assertion in the existing test file (without changing the test file).**
  881. - [ ] **Step 3: Run `npm test -- tests/counter.test.ts` and `npm run build`, then commit.**
  882. """
  883. def _commit_waves_file(workdir: Path) -> None:
  884. """Stage and commit the waves file + spec on the feature branch.
  885. Assumes the caller already created the underlying plan repo and is
  886. sitting on feature/test-implementation (the create_wave_test_repo*
  887. helpers leave us there).
  888. """
  889. _git(["git", "add", "-A"], cwd=workdir)
  890. _git(["git", "commit", "-m", "add pre-decomposed waves file and spec"], cwd=workdir)
  891. def create_waves_file(workdir: Path) -> None:
  892. """Create the full 5-task repo with a pre-decomposed .waves.md file.
  893. This is the starting point for `executing-waves` scenarios that
  894. want the full 3-wave experience. The waves file is marked
  895. `status: approved` so the executing-waves pre-flight check passes.
  896. """
  897. workdir = Path(workdir)
  898. create_wave_test_repo(workdir)
  899. _write_file(
  900. workdir,
  901. "docs/superpowers/specs/test-spec.md",
  902. WAVE_TEST_SPEC,
  903. )
  904. _write_file(
  905. workdir,
  906. "docs/superpowers/plans/test-plan.waves.md",
  907. WAVE_TEST_WAVES_FULL,
  908. )
  909. _commit_waves_file(workdir)
  910. def create_waves_file_minimal(workdir: Path) -> None:
  911. """Create the 3-task minimal repo with a pre-decomposed .waves.md file.
  912. This is the starting point for smaller `executing-waves` scenarios
  913. that exercise the same execution pipeline over 1 sequential task +
  914. 2 parallel tasks. The waves file is marked `status: approved` so
  915. the executing-waves pre-flight check passes.
  916. """
  917. workdir = Path(workdir)
  918. create_wave_test_repo_minimal(workdir)
  919. _write_file(
  920. workdir,
  921. "docs/superpowers/specs/test-spec.md",
  922. WAVE_TEST_SPEC_MINIMAL,
  923. )
  924. _write_file(
  925. workdir,
  926. "docs/superpowers/plans/test-plan.waves.md",
  927. WAVE_TEST_WAVES_MINIMAL,
  928. )
  929. _commit_waves_file(workdir)
  930. def create_waves_file_with_broken_task(workdir: Path) -> None:
  931. """Create a 3-task waves repo where Task 3 is structurally impossible.
  932. This is the starting point for `executing-waves` failure scenarios.
  933. Layout:
  934. - Wave 1 (sequential): Task 1 — foundation types (passes normally)
  935. - Wave 2 (parallel): Task 2 — logger service (passes normally)
  936. Task 3 — counter service (always fails)
  937. Task 3's failure is structural, not a prompt trick: a pre-existing
  938. `tests/counter.test.ts` file is committed before the implementer
  939. runs and contains two contradictory assertions (`compute(1) === 1`
  940. AND `compute(1) === 2`). The acceptance criteria explicitly forbid
  941. modifying the test file. No implementation can make both tests
  942. pass, so `npm test` always reports a failure for one of the two
  943. cases.
  944. Expected orchestrator behavior (per failure-handling.md):
  945. 1. Detect Task 3 failure after the parallel wave runs.
  946. 2. Merge Task 2 (the successful task) onto the feature branch.
  947. 3. Retry Task 3 once from the updated tip.
  948. 4. Retry also fails.
  949. 5. Escalate to the user with the standard escalation message.
  950. """
  951. workdir = Path(workdir)
  952. create_wave_test_repo_minimal(workdir)
  953. # Overwrite the plan with the broken-task variant.
  954. _write_file(
  955. workdir,
  956. "docs/superpowers/plans/test-plan.md",
  957. WAVE_TEST_PLAN_BROKEN_TASK,
  958. )
  959. # Pre-create the failing test fixture for Task 3. The implementer
  960. # must NOT modify it (per the task acceptance criteria), so the
  961. # contradictory assertions guarantee a structural failure.
  962. _write_file(
  963. workdir,
  964. "tests/counter.test.ts",
  965. COUNTER_FAILING_TEST,
  966. )
  967. _write_file(
  968. workdir,
  969. "docs/superpowers/specs/test-spec.md",
  970. WAVE_TEST_SPEC_BROKEN_TASK,
  971. )
  972. _write_file(
  973. workdir,
  974. "docs/superpowers/plans/test-plan.waves.md",
  975. WAVE_TEST_WAVES_BROKEN_TASK,
  976. )
  977. _commit_waves_file(workdir)
  978. def create_conflict_surface_repo(workdir: Path) -> None:
  979. """Create a plan where three tasks implicitly modify the same barrel file.
  980. The `src/services/index.ts` barrel file is pre-created so the
  981. decomposer sees it during directory scanning. Each task in the plan
  982. lists only its new module file but the steps mention adding an
  983. export to the barrel — the conflict-surface heuristic should notice
  984. this and either add the barrel file to each task's list or serialize
  985. the tasks.
  986. Expected decomposition (under a correct heuristic): either
  987. - all tasks in one wave with `src/services/index.ts` added to each
  988. task's file list, or
  989. - sequential waves (serialized) to avoid the shared barrel.
  990. """
  991. workdir = Path(workdir)
  992. _init_base_repo(workdir)
  993. # The barrel file MUST exist before the plan runs.
  994. _write_file(workdir, "src/services/index.ts", "export {};\n")
  995. _write_file(workdir, "docs/superpowers/plans/test-plan.md", CONFLICT_SURFACE_PLAN)
  996. _commit_all_on_feature_branch(workdir)