skilldrop-cli 0.16.29 → 0.16.31

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 (231) hide show
  1. package/bin/skilldrop.js +160 -33
  2. package/catalogue.json +10 -4
  3. package/model-routing.json +24 -0
  4. package/package.json +1 -1
  5. package/packs/ai-engineering/skills/mcp-pairing/SKILL.md +93 -0
  6. package/packs/ai-engineering/skills/mcp-pairing/evals/eval_queries.json +13 -0
  7. package/packs/ai-engineering/skills/mcp-pairing/evals/evals.json +76 -0
  8. package/packs/ai-engineering/skills/mcp-pairing/evals/files/2/mcp.json +32 -0
  9. package/packs/ai-engineering/skills/mcp-pairing/evals/files/4/.mcp.json +47 -0
  10. package/packs/ai-engineering/skills/mcp-pairing/evals/files/4/docs/pr-loop.md +12 -0
  11. package/packs/ai-engineering/skills/mcp-pairing/evals/files/5/docs/oncall-triage.md +18 -0
  12. package/packs/ai-engineering/skills/mcp-pairing/examples/build-loop-github-jira-datadog.md +69 -0
  13. package/packs/ai-engineering/skills/mcp-pairing/manifest.json +17 -0
  14. package/packs/ai-engineering/skills/mcp-pairing/reference.md +136 -0
  15. package/packs/ai-engineering/skills/mcp-pairing/templates/mcp-pairing.md +43 -0
  16. package/packs/dev-team/loops/build/LOOP.md +4 -2
  17. package/packs/dev-team/loops/build/loop.json +2 -2
  18. package/packs/dev-team/loops/release/LOOP.md +7 -5
  19. package/packs/dev-team/loops/release/loop.json +2 -2
  20. package/packs/dev-team/skills/codebase-tour/SKILL.md +115 -0
  21. package/packs/dev-team/skills/codebase-tour/evals/eval_queries.json +13 -0
  22. package/packs/dev-team/skills/codebase-tour/evals/evals.json +77 -0
  23. package/packs/dev-team/skills/codebase-tour/evals/files/1/.github/workflows/ci.yml +23 -0
  24. package/packs/dev-team/skills/codebase-tour/evals/files/1/README.md +23 -0
  25. package/packs/dev-team/skills/codebase-tour/evals/files/1/openapi.yaml +21 -0
  26. package/packs/dev-team/skills/codebase-tour/evals/files/1/package-lock.json +7 -0
  27. package/packs/dev-team/skills/codebase-tour/evals/files/1/package.json +29 -0
  28. package/packs/dev-team/skills/codebase-tour/evals/files/1/src/config.ts +20 -0
  29. package/packs/dev-team/skills/codebase-tour/evals/files/1/src/domain/shipments/filter.ts +24 -0
  30. package/packs/dev-team/skills/codebase-tour/evals/files/1/src/domain/shipments/repository.ts +15 -0
  31. package/packs/dev-team/skills/codebase-tour/evals/files/1/src/errors.ts +9 -0
  32. package/packs/dev-team/skills/codebase-tour/evals/files/1/src/generated/api-types.ts +9 -0
  33. package/packs/dev-team/skills/codebase-tour/evals/files/1/src/http/handlers/shipments.ts +38 -0
  34. package/packs/dev-team/skills/codebase-tour/evals/files/1/src/http/routes.ts +7 -0
  35. package/packs/dev-team/skills/codebase-tour/evals/files/1/src/jobs/export-shipments.ts +22 -0
  36. package/packs/dev-team/skills/codebase-tour/evals/files/1/src/server.ts +18 -0
  37. package/packs/dev-team/skills/codebase-tour/evals/files/1/tests/domain/filter.test.ts +16 -0
  38. package/packs/dev-team/skills/codebase-tour/evals/files/1/tests/http/shipments.test.ts +18 -0
  39. package/packs/dev-team/skills/codebase-tour/evals/files/1/tsconfig.json +10 -0
  40. package/packs/dev-team/skills/codebase-tour/evals/files/1/vitest.config.ts +5 -0
  41. package/packs/dev-team/skills/codebase-tour/evals/files/2/.github/workflows/ci.yml +16 -0
  42. package/packs/dev-team/skills/codebase-tour/evals/files/2/README.md +16 -0
  43. package/packs/dev-team/skills/codebase-tour/evals/files/2/noxfile.py +14 -0
  44. package/packs/dev-team/skills/codebase-tour/evals/files/2/pyproject.toml +22 -0
  45. package/packs/dev-team/skills/codebase-tour/evals/files/2/src/ledgerline/__init__.py +3 -0
  46. package/packs/dev-team/skills/codebase-tour/evals/files/2/src/ledgerline/_version.py +4 -0
  47. package/packs/dev-team/skills/codebase-tour/evals/files/2/src/ledgerline/cli.py +32 -0
  48. package/packs/dev-team/skills/codebase-tour/evals/files/2/src/ledgerline/detect.py +15 -0
  49. package/packs/dev-team/skills/codebase-tour/evals/files/2/src/ledgerline/errors.py +2 -0
  50. package/packs/dev-team/skills/codebase-tour/evals/files/2/src/ledgerline/money.py +14 -0
  51. package/packs/dev-team/skills/codebase-tour/evals/files/2/src/ledgerline/parsers/__init__.py +8 -0
  52. package/packs/dev-team/skills/codebase-tour/evals/files/2/src/ledgerline/parsers/acme.py +18 -0
  53. package/packs/dev-team/skills/codebase-tour/evals/files/2/src/ledgerline/parsers/base.py +24 -0
  54. package/packs/dev-team/skills/codebase-tour/evals/files/2/src/ledgerline/parsers/harbor.py +13 -0
  55. package/packs/dev-team/skills/codebase-tour/evals/files/2/tests/fixtures/acme_sample.csv +3 -0
  56. package/packs/dev-team/skills/codebase-tour/evals/files/2/tests/test_acme.py +23 -0
  57. package/packs/dev-team/skills/codebase-tour/evals/files/2/tests/test_detect.py +13 -0
  58. package/packs/dev-team/skills/codebase-tour/evals/files/3/.github/workflows/ci.yml +28 -0
  59. package/packs/dev-team/skills/codebase-tour/evals/files/3/Makefile +16 -0
  60. package/packs/dev-team/skills/codebase-tour/evals/files/3/README.md +16 -0
  61. package/packs/dev-team/skills/codebase-tour/evals/files/3/cmd/tidepool/main.go +24 -0
  62. package/packs/dev-team/skills/codebase-tour/evals/files/3/go.mod +5 -0
  63. package/packs/dev-team/skills/codebase-tour/evals/files/3/internal/config/config.go +38 -0
  64. package/packs/dev-team/skills/codebase-tour/evals/files/3/internal/dispatch/dispatcher.go +52 -0
  65. package/packs/dev-team/skills/codebase-tour/evals/files/3/internal/dispatch/dispatcher_test.go +42 -0
  66. package/packs/dev-team/skills/codebase-tour/evals/files/3/internal/dispatch/sender.go +32 -0
  67. package/packs/dev-team/skills/codebase-tour/evals/files/3/internal/replay/replay.go +31 -0
  68. package/packs/dev-team/skills/codebase-tour/evals/files/3/internal/store/db.go +35 -0
  69. package/packs/dev-team/skills/codebase-tour/evals/files/3/internal/store/query.sql +11 -0
  70. package/packs/dev-team/skills/codebase-tour/evals/files/3/internal/store/query.sql.go +30 -0
  71. package/packs/dev-team/skills/codebase-tour/evals/files/3/internal/store/schema.sql +6 -0
  72. package/packs/dev-team/skills/codebase-tour/evals/files/3/internal/store/store_integration_test.go +21 -0
  73. package/packs/dev-team/skills/codebase-tour/evals/files/3/sqlc.yaml +9 -0
  74. package/packs/dev-team/skills/codebase-tour/evals/files/4/.env.example +4 -0
  75. package/packs/dev-team/skills/codebase-tour/evals/files/4/.github/workflows/ci.yml +16 -0
  76. package/packs/dev-team/skills/codebase-tour/evals/files/4/Makefile +16 -0
  77. package/packs/dev-team/skills/codebase-tour/evals/files/4/README.md +23 -0
  78. package/packs/dev-team/skills/codebase-tour/evals/files/4/data/rates.csv +4 -0
  79. package/packs/dev-team/skills/codebase-tour/evals/files/4/pyproject.toml +19 -0
  80. package/packs/dev-team/skills/codebase-tour/evals/files/4/scripts/gen_rates.py +29 -0
  81. package/packs/dev-team/skills/codebase-tour/evals/files/4/src/shipcalc/__init__.py +0 -0
  82. package/packs/dev-team/skills/codebase-tour/evals/files/4/src/shipcalc/api/__init__.py +0 -0
  83. package/packs/dev-team/skills/codebase-tour/evals/files/4/src/shipcalc/api/preview.py +14 -0
  84. package/packs/dev-team/skills/codebase-tour/evals/files/4/src/shipcalc/api/routes.py +36 -0
  85. package/packs/dev-team/skills/codebase-tour/evals/files/4/src/shipcalc/config.py +15 -0
  86. package/packs/dev-team/skills/codebase-tour/evals/files/4/src/shipcalc/discounts.py +8 -0
  87. package/packs/dev-team/skills/codebase-tour/evals/files/4/src/shipcalc/generated/__init__.py +0 -0
  88. package/packs/dev-team/skills/codebase-tour/evals/files/4/src/shipcalc/generated/rates_table.py +8 -0
  89. package/packs/dev-team/skills/codebase-tour/evals/files/4/src/shipcalc/main.py +8 -0
  90. package/packs/dev-team/skills/codebase-tour/evals/files/4/src/shipcalc/shipping.py +18 -0
  91. package/packs/dev-team/skills/codebase-tour/evals/files/4/src/shipcalc/tax.py +7 -0
  92. package/packs/dev-team/skills/codebase-tour/evals/files/4/tests/conftest.py +3 -0
  93. package/packs/dev-team/skills/codebase-tour/evals/files/4/tests/unit/test_discounts.py +11 -0
  94. package/packs/dev-team/skills/codebase-tour/evals/files/4/tests/unit/test_tax.py +13 -0
  95. package/packs/dev-team/skills/codebase-tour/evals/files/5/.env.example +3 -0
  96. package/packs/dev-team/skills/codebase-tour/evals/files/5/.github/workflows/ci.yml +13 -0
  97. package/packs/dev-team/skills/codebase-tour/evals/files/5/README.md +18 -0
  98. package/packs/dev-team/skills/codebase-tour/evals/files/5/package.json +13 -0
  99. package/packs/dev-team/skills/codebase-tour/evals/files/5/packages/api/openapi.json +30 -0
  100. package/packs/dev-team/skills/codebase-tour/evals/files/5/packages/api/package.json +15 -0
  101. package/packs/dev-team/skills/codebase-tour/evals/files/5/packages/api/src/db.js +19 -0
  102. package/packs/dev-team/skills/codebase-tour/evals/files/5/packages/api/src/flags.js +8 -0
  103. package/packs/dev-team/skills/codebase-tour/evals/files/5/packages/api/src/generated/schema.js +32 -0
  104. package/packs/dev-team/skills/codebase-tour/evals/files/5/packages/api/src/middleware/validate.js +20 -0
  105. package/packs/dev-team/skills/codebase-tour/evals/files/5/packages/api/src/routes/bookings.js +15 -0
  106. package/packs/dev-team/skills/codebase-tour/evals/files/5/packages/api/src/server.js +15 -0
  107. package/packs/dev-team/skills/codebase-tour/evals/files/5/packages/api/src/services/bookings.js +23 -0
  108. package/packs/dev-team/skills/codebase-tour/evals/files/5/packages/api/src/services/pricing.js +9 -0
  109. package/packs/dev-team/skills/codebase-tour/evals/files/5/packages/api/test/pricing.test.js +15 -0
  110. package/packs/dev-team/skills/codebase-tour/evals/files/5/packages/web/package.json +9 -0
  111. package/packs/dev-team/skills/codebase-tour/evals/files/5/packages/web/src/bookingForm.js +11 -0
  112. package/packs/dev-team/skills/codebase-tour/evals/files/5/packages/web/src/format.js +7 -0
  113. package/packs/dev-team/skills/codebase-tour/evals/files/5/packages/web/test/format.test.js +12 -0
  114. package/packs/dev-team/skills/codebase-tour/evals/files/5/scripts/gen-schema.mjs +27 -0
  115. package/packs/dev-team/skills/codebase-tour/examples/refund-window.md +158 -0
  116. package/packs/dev-team/skills/codebase-tour/manifest.json +47 -0
  117. package/packs/dev-team/skills/codebase-tour/scripts/repo_facts.py +403 -0
  118. package/packs/dev-team/skills/codebase-tour/templates/codebase-tour.md +85 -0
  119. package/packs/dev-team/skills/dependency-upgrade-review/SKILL.md +101 -0
  120. package/packs/dev-team/skills/dependency-upgrade-review/evals/eval_queries.json +11 -0
  121. package/packs/dev-team/skills/dependency-upgrade-review/evals/evals.json +77 -0
  122. package/packs/dev-team/skills/dependency-upgrade-review/evals/files/1/.github/workflows/ci.yml +20 -0
  123. package/packs/dev-team/skills/dependency-upgrade-review/evals/files/1/pr.diff +87 -0
  124. package/packs/dev-team/skills/dependency-upgrade-review/evals/files/1/quillmark-CHANGELOG.md +24 -0
  125. package/packs/dev-team/skills/dependency-upgrade-review/evals/files/1/src/render/comment.js +6 -0
  126. package/packs/dev-team/skills/dependency-upgrade-review/evals/files/1/src/render/note.js +8 -0
  127. package/packs/dev-team/skills/dependency-upgrade-review/evals/files/1/test/note.test.js +12 -0
  128. package/packs/dev-team/skills/dependency-upgrade-review/evals/files/2/.github/workflows/ci.yml +23 -0
  129. package/packs/dev-team/skills/dependency-upgrade-review/evals/files/2/.github/workflows/release.yml +23 -0
  130. package/packs/dev-team/skills/dependency-upgrade-review/evals/files/2/pr.diff +32 -0
  131. package/packs/dev-team/skills/dependency-upgrade-review/evals/files/2/release-notes.md +20 -0
  132. package/packs/dev-team/skills/dependency-upgrade-review/evals/files/4/.github/workflows/ci.yml +18 -0
  133. package/packs/dev-team/skills/dependency-upgrade-review/evals/files/4/Dockerfile +8 -0
  134. package/packs/dev-team/skills/dependency-upgrade-review/evals/files/4/PR.md +36 -0
  135. package/packs/dev-team/skills/dependency-upgrade-review/evals/files/4/pr.diff +47 -0
  136. package/packs/dev-team/skills/dependency-upgrade-review/evals/files/4/src/ingest/client.py +24 -0
  137. package/packs/dev-team/skills/dependency-upgrade-review/evals/files/4/src/ingest/reader.py +32 -0
  138. package/packs/dev-team/skills/dependency-upgrade-review/evals/files/4/tests/test_reader.py +15 -0
  139. package/packs/dev-team/skills/dependency-upgrade-review/evals/files/4/upstream/quillstream-CHANGELOG.md +37 -0
  140. package/packs/dev-team/skills/dependency-upgrade-review/evals/files/5/.github/workflows/ci.yml +22 -0
  141. package/packs/dev-team/skills/dependency-upgrade-review/evals/files/5/.github/workflows/release.yml +25 -0
  142. package/packs/dev-team/skills/dependency-upgrade-review/evals/files/5/.nvmrc +1 -0
  143. package/packs/dev-team/skills/dependency-upgrade-review/evals/files/5/PR-412.md +16 -0
  144. package/packs/dev-team/skills/dependency-upgrade-review/evals/files/5/PR-413.md +15 -0
  145. package/packs/dev-team/skills/dependency-upgrade-review/evals/files/5/package.json +21 -0
  146. package/packs/dev-team/skills/dependency-upgrade-review/evals/files/5/pr-412.diff +26 -0
  147. package/packs/dev-team/skills/dependency-upgrade-review/evals/files/5/pr-413.diff +84 -0
  148. package/packs/dev-team/skills/dependency-upgrade-review/evals/files/5/scripts/release.mjs +18 -0
  149. package/packs/dev-team/skills/dependency-upgrade-review/evals/files/5/upstream/release-notary-CHANGELOG.md +16 -0
  150. package/packs/dev-team/skills/dependency-upgrade-review/evals/files/5/upstream/setup-toolchain-RELEASES.md +22 -0
  151. package/packs/dev-team/skills/dependency-upgrade-review/examples/inputs/ledgerline.diff +38 -0
  152. package/packs/dev-team/skills/dependency-upgrade-review/examples/ledgerline-tidyparse.md +91 -0
  153. package/packs/dev-team/skills/dependency-upgrade-review/manifest.json +50 -0
  154. package/packs/dev-team/skills/dependency-upgrade-review/reference.md +99 -0
  155. package/packs/dev-team/skills/dependency-upgrade-review/scripts/upgrade_diff.py +718 -0
  156. package/packs/dev-team/skills/refactor-plan/SKILL.md +168 -0
  157. package/packs/dev-team/skills/refactor-plan/evals/eval_queries.json +46 -0
  158. package/packs/dev-team/skills/refactor-plan/evals/evals.json +106 -0
  159. package/packs/dev-team/skills/refactor-plan/evals/files/1/debt-register-row.md +5 -0
  160. package/packs/dev-team/skills/refactor-plan/evals/files/1/in-flight-prs.md +7 -0
  161. package/packs/dev-team/skills/refactor-plan/evals/files/1/order_service.py +74 -0
  162. package/packs/dev-team/skills/refactor-plan/evals/files/1/test-coverage.txt +14 -0
  163. package/packs/dev-team/skills/refactor-plan/evals/files/3/imports.txt +22 -0
  164. package/packs/dev-team/skills/refactor-plan/evals/files/3/register.md +3 -0
  165. package/packs/dev-team/skills/refactor-plan/evals/files/4/docs/open-prs.md +6 -0
  166. package/packs/dev-team/skills/refactor-plan/evals/files/4/docs/tech-debt-register.md +7 -0
  167. package/packs/dev-team/skills/refactor-plan/evals/files/4/reports/monthly_revenue.py +44 -0
  168. package/packs/dev-team/skills/refactor-plan/evals/files/4/src/billing/invoice.py +233 -0
  169. package/packs/dev-team/skills/refactor-plan/evals/files/4/src/partners/statement.py +29 -0
  170. package/packs/dev-team/skills/refactor-plan/evals/files/4/tests/billing/test_payments.py +13 -0
  171. package/packs/dev-team/skills/refactor-plan/evals/files/5/ci-notes.md +5 -0
  172. package/packs/dev-team/skills/refactor-plan/evals/files/5/open-prs.txt +4 -0
  173. package/packs/dev-team/skills/refactor-plan/evals/files/5/scripts/nightly-digest.js +36 -0
  174. package/packs/dev-team/skills/refactor-plan/evals/files/5/src/auth/passwordReset.ts +16 -0
  175. package/packs/dev-team/skills/refactor-plan/evals/files/5/src/notify/index.ts +2 -0
  176. package/packs/dev-team/skills/refactor-plan/evals/files/5/src/notify/legacyMailer.ts +149 -0
  177. package/packs/dev-team/skills/refactor-plan/evals/files/5/src/orders/onOrderShipped.ts +15 -0
  178. package/packs/dev-team/skills/refactor-plan/evals/files/5/tests/notify/legacyMailer.test.ts +7 -0
  179. package/packs/dev-team/skills/refactor-plan/evals/files/6/billing-service/src/main/kotlin/billing/DunningJob.kt +29 -0
  180. package/packs/dev-team/skills/refactor-plan/evals/files/6/billing-service/src/main/kotlin/billing/InvoiceService.kt +157 -0
  181. package/packs/dev-team/skills/refactor-plan/evals/files/6/billing-service/src/main/kotlin/billing/pdf/InvoicePdfRenderer.kt +36 -0
  182. package/packs/dev-team/skills/refactor-plan/evals/files/6/billing-service/src/test/kotlin/billing/InvoiceServiceTest.kt +40 -0
  183. package/packs/dev-team/skills/refactor-plan/evals/files/6/docs/adr-007-persistence.md +26 -0
  184. package/packs/dev-team/skills/refactor-plan/evals/files/6/docs/open-prs.md +9 -0
  185. package/packs/dev-team/skills/refactor-plan/evals/files/7/notes/open-work.md +19 -0
  186. package/packs/dev-team/skills/refactor-plan/evals/files/7/web/src/components/CheckoutPanel.tsx +207 -0
  187. package/packs/dev-team/skills/refactor-plan/evals/files/7/web/src/components/MiniCart.tsx +33 -0
  188. package/packs/dev-team/skills/refactor-plan/evals/files/7/web/src/components/__tests__/CheckoutPanel.test.tsx +13 -0
  189. package/packs/dev-team/skills/refactor-plan/evals/files/7/web/src/pages/OrderReview.tsx +24 -0
  190. package/packs/dev-team/skills/refactor-plan/examples/search-ranker.md +104 -0
  191. package/packs/dev-team/skills/refactor-plan/manifest.json +36 -0
  192. package/packs/dev-team/skills/refactor-plan/reference.md +149 -0
  193. package/packs/dev-team/skills/refactor-plan/templates/refactor-plan.md +82 -0
  194. package/packs/dev-team/skills/rollout-plan/SKILL.md +157 -0
  195. package/packs/dev-team/skills/rollout-plan/evals/eval_queries.json +12 -0
  196. package/packs/dev-team/skills/rollout-plan/evals/evals.json +109 -0
  197. package/packs/dev-team/skills/rollout-plan/evals/files/2/metrics-snapshot.txt +11 -0
  198. package/packs/dev-team/skills/rollout-plan/evals/files/2/pr-description.md +28 -0
  199. package/packs/dev-team/skills/rollout-plan/evals/files/4/PR_DESCRIPTION.md +24 -0
  200. package/packs/dev-team/skills/rollout-plan/evals/files/4/metrics_snapshot.txt +7 -0
  201. package/packs/dev-team/skills/rollout-plan/evals/files/5/docs/tax-engine-v2.md +24 -0
  202. package/packs/dev-team/skills/rollout-plan/evals/files/6/flags/smart_reminders_v2.yaml +14 -0
  203. package/packs/dev-team/skills/rollout-plan/evals/files/6/notes/push-pipeline.md +10 -0
  204. package/packs/dev-team/skills/rollout-plan/evals/files/7/docs/ratelimiter-v2.md +21 -0
  205. package/packs/dev-team/skills/rollout-plan/evals/files/7/ops/traffic-summary.txt +9 -0
  206. package/packs/dev-team/skills/rollout-plan/examples/sync-engine-workspaces.md +102 -0
  207. package/packs/dev-team/skills/rollout-plan/manifest.json +35 -0
  208. package/packs/dev-team/skills/rollout-plan/reference.md +100 -0
  209. package/packs/dev-team/skills/rollout-plan/scripts/canary_math.py +261 -0
  210. package/packs/dev-team/skills/rollout-plan/templates/rollout-plan.md +50 -0
  211. package/packs/product-manager/loops/discover/LOOP.md +5 -4
  212. package/packs/product-manager/loops/discover/loop.json +2 -2
  213. package/packs/product-manager/skills/feedback-synthesis/SKILL.md +182 -0
  214. package/packs/product-manager/skills/feedback-synthesis/evals/eval_queries.json +46 -0
  215. package/packs/product-manager/skills/feedback-synthesis/evals/evals.json +76 -0
  216. package/packs/product-manager/skills/feedback-synthesis/evals/files/1/interview-notes.md +47 -0
  217. package/packs/product-manager/skills/feedback-synthesis/evals/files/1/nps-verbatims.csv +13 -0
  218. package/packs/product-manager/skills/feedback-synthesis/evals/files/1/support-tickets.csv +36 -0
  219. package/packs/product-manager/skills/feedback-synthesis/evals/files/3/churn-exit-survey.csv +11 -0
  220. package/packs/product-manager/skills/feedback-synthesis/evals/files/3/sales-call-notes.md +55 -0
  221. package/packs/product-manager/skills/feedback-synthesis/evals/files/4/csat_comments_q3.csv +17 -0
  222. package/packs/product-manager/skills/feedback-synthesis/evals/files/4/support_tickets_q3.csv +49 -0
  223. package/packs/product-manager/skills/feedback-synthesis/evals/files/5/app_reviews_last_60_days.csv +32 -0
  224. package/packs/product-manager/skills/feedback-synthesis/evals/files/5/interview_notes_round1.md +48 -0
  225. package/packs/product-manager/skills/feedback-synthesis/examples/booking-app-feedback.md +139 -0
  226. package/packs/product-manager/skills/feedback-synthesis/examples/inputs/booking-app-coded.csv +17 -0
  227. package/packs/product-manager/skills/feedback-synthesis/manifest.json +53 -0
  228. package/packs/product-manager/skills/feedback-synthesis/reference.md +89 -0
  229. package/packs/product-manager/skills/feedback-synthesis/scripts/theme_counts.py +355 -0
  230. package/packs/product-manager/skills/feedback-synthesis/templates/coding-sheet.csv +6 -0
  231. package/packs/product-manager/skills/feedback-synthesis/templates/synthesis.md +42 -0
@@ -0,0 +1,168 @@
1
+ ---
2
+ name: refactor-plan
3
+ description: Plan paying down one piece of technical debt as a refactor that serves the goal it is blocking — characterisation tests on the riskiest behaviour first, then small steps each merged to main with main releasable after every one, pure refactors kept apart from the behaviour changes the goal needs, a scope fence, a stop point, a technique per step (extract, branch by abstraction, parallel change, strangler fig, mikado) and the clashes with in-flight feature work. Use when the user asks for a refactor plan, how to untangle, break up, replace or clean up a module without a rewrite, how to pay down a tech-debt item, "how do we refactor X without stopping feature work", "how do we get off X without a big bang", or wants a strangler-fig or mikado plan.
4
+ ---
5
+
6
+ # refactor-plan
7
+
8
+ You plan a refactor: work that pays down one piece of technical debt so that something the
9
+ user wants becomes safe to do — add a shipping zone without fear, retire a vendor before its
10
+ contract ends, let a batch worker import billing. The plan is a sequence of small steps, each
11
+ one PR merged to main with main releasable after it, ending at the structure that goal needs.
12
+ The team can stop after any step and the code is better than it was.
13
+
14
+ Recording and ranking debt is `tech-debt-register`'s job; this skill starts from one item.
15
+ Moving data or a schema with a backfill is `migration-plan`'s; when the payoff needs one, the
16
+ plan stops at the code seam and names the migration as separate work.
17
+
18
+ ## How to respond
19
+
20
+ **Four rules come before the steps and outrank them:**
21
+
22
+ - **Lead with the answer.** The first two lines say the approach and the first step, in plain words: ✅ *"Put an interface in front of `SmsGateway`, build the new client behind it dark, and move message types over one flag at a time. Start with characterisation tests for `SmsGateway.send` in a new test file."* — ❌ a title, a target table, or a paragraph of context first.
23
+ - **Match the size of the ask.** A question in chat ("how do we do this??", "plan how we untangle X") gets the plan in the reply: what the code shows, then a numbered sequence, one or two lines a step, each with its why. Write a full document only when the user asks for one (a file, "a proper plan", something to take to planning) or the work runs past about ten steps; then write the file and reply with the lead, the findings that change the plan, and where the file is. Never both at full length.
24
+ - **Read what you were given, and use only that.** Open every file the user names and the callers it imports or is imported by. Report what is actually there, cited as `file:function`. Never say a file is unreadable without having tried to open it; if a read really fails, say which file and plan without it. No invented modules, baselines, vendors, volumes or dates. Callers or facts not in the input are checks to run (✅ *"grep for `price_order(` and `lines=`"*), not statements.
25
+ - **Write for someone who has never heard of this skill.** No skill names, no scripts or paths from this folder, nothing about how the plan was produced. With a debt item to work on, state assumptions in a line and produce the plan; with nothing at all, ask for the debt item in one or two plain sentences.
26
+
27
+ 1. **Map the debt from the code.** The functions involved, every caller, and what each caller
28
+ relies on beyond the return value. Look for these four; they are what a plain read misses
29
+ and what breaks a refactor:
30
+ - **Contracts callers depend on:** mutation of an argument (✅ *"`exports/ledger.py:month_end` reads the rounding line back out of the `lines` list it passed to `price_order`, so `price_order` must keep appending to it"*), a return value that lies (✅ *"`SmsGateway.send` returns `True` from its `except`, so the retry in `alerts/notify.py:page_oncall` has never fired"*), ordering, side effects.
31
+ - **Copies outside the module:** duplicated constants or logic that bypass it and have drifted (a cron script with its own HTTP send, a report with its own `VAT_RATES`). Search every file in the input, scripts and reports included, for the module's constant and function names. Each goes inside or outside the fence by name; if outside, the plan does not claim the duplication is gone.
32
+ - **Test coverage as it is:** which paths existing tests reach (✅ *"the only test covers `format_message()`"*; ✅ *"`test_checkout.py` never imports `pricing.py`"*).
33
+ - **Stale signals:** a comment, TODO or open PR that says one thing while the code does another.
34
+
35
+ 2. **Start from the goal, and end at the structure it needs.** Name the user's stated goal and
36
+ the end state that delivers it — not the same code split across more files.
37
+ ✅ *"Goal: adding a shipping zone is a one-line data change. End state: zone rules live in
38
+ one table that `quote_shipping` reads; a new zone is a row plus a test."* — ❌ *"Extract
39
+ seven helpers from `quote_shipping` unchanged"* and stop. Then the target: one metric the team
40
+ can read (from the register item, or files touched per change, call sites on the old path,
41
+ forbidden imports), its baseline from the input or `not measured` with a step to measure it,
42
+ and a checkable **stop point** (✅ *"zero sends through the old gateway, the cron script
43
+ included"* — ❌ *"when the code is clean"*). Every step must move toward that end state;
44
+ a step that doesn't is cut.
45
+
46
+ 3. **Label every step `refactor` or `behaviour change`.** A refactor keeps output identical and
47
+ is proven by the pinned tests. A behaviour change the goal needs (moving rules into data
48
+ that changes a result, removing a mutation a caller relied on, surfacing errors that were
49
+ swallowed, switching traffic to a new provider) is its own step, placed after the refactor
50
+ that makes it safe, with the caller moved first and its own rollback. Behaviour changes the
51
+ goal does not need — renamed env vars, a fixed rounding bug, money as Decimal, a schema
52
+ change — go out of the plan to a ticket, a later parallel change or a migration. Bugs callers
53
+ depend on are pinned as they are: ✅ *"pin `send` returning `true` on failure; surfacing
54
+ failures is a later, deliberate step"* — ❌ *"fix it while writing the test."*
55
+
56
+ 4. **Characterisation tests first, on the riskiest behaviour.** Before any step touches
57
+ untested code, pin current output, starting with what has caused incidents or what the code
58
+ comments warn about (the register's incidents, branches with odd rules). Name each behaviour
59
+ to pin by function: inputs callers send, boundaries (empty, zero, rounding, ordering,
60
+ timezones), error paths, current bugs. An approval (golden-master) test over recorded
61
+ real-shape inputs when output is too wide to assert by hand. Pin at the narrowest seam that
62
+ runs without the current suite's infrastructure.
63
+ **An open PR on the same file never delays the tests.** Tests change no production code: put
64
+ them in a new test file so they can't conflict, and if the open PR changes the pinned
65
+ behaviour on purpose, its author updates the pinned value in that PR. ❌ *"Characterisation
66
+ tests for the zone rules after #212 merges"* leaves the incident-prone code bare for weeks.
67
+ Code already covered on the paths a step touches skips this step; say which tests cover it.
68
+
69
+ 5. **Cut steps by the one-PR rule.** One PR a reviewer reads in one sitting, merged within a
70
+ couple of days, main releasable after it. **Never mix a move with a change** (✅ *"PR A: move
71
+ `calculate_tax` to `orders/tax.py` unchanged, old name re-exports it. PR B: change it."*).
72
+ **No long-lived branch:** work that can't merge in days goes behind a seam, on main and dark.
73
+ Size the sequence against the delivery facts given (CI time and flake rate, deploys per week,
74
+ approval gates, a deadline): ✅ *"about two deploys a week leaves roughly N releases before
75
+ the deadline; each flag flip needs one, so flips start by week X"* — counted from the input,
76
+ never invented. Around a busy window (peak season, a launch), run the dark refactor steps
77
+ through it and schedule the behaviour-change steps after it.
78
+
79
+ 6. **Pick the technique per step and say why** in one clause. The catalogue, with rollback
80
+ for each, is in [`reference.md`](reference.md):
81
+
82
+ | Technique | Use when |
83
+ |---|---|
84
+ | Characterisation tests | The code a step touches has no tests on the paths callers use |
85
+ | Extract / inline / move | A cohesive piece can leave in one PR with callers unchanged |
86
+ | Branch by abstraction | A dependency many callers use must be replaced, and the swap takes more than one PR |
87
+ | Parallel change (expand–contract) | A signature, contract or data shape changes and callers move over several PRs |
88
+ | Strangler fig | A whole module is replaced piece by piece, with routing at its edge deciding old or new per call |
89
+ | Mikado method | Each attempted change breaks on prerequisites; the graph of what must change first is unknown |
90
+
91
+ 7. **Order for value early, stoppable everywhere.** After the tests, the first step is the one
92
+ that unblocks the work the user named, not the neatest one; say why it is first. Every step
93
+ leaves the code better on its own. A step that only pays off once a later one lands is merged
94
+ with it, or its pair is within the next two steps and the plan says two paths exist until then.
95
+
96
+ 8. **Coordinate with in-flight work.** For each open PR or planned feature, name the shared
97
+ `file:function` and pick one:
98
+ - **Land first** — a PR the business needs (peak season, a customer date) merges ahead; the refactor rebases.
99
+ - **Wait** — a *move or rewrite* of code an open PR edits starts after it merges. Tests and seams elsewhere carry on.
100
+ - **Fold in** — the feature PR does the extraction for its own area, as a separate commit, when one team owns both.
101
+ - **Through the seam** — a PR adding a new caller of the code being replaced is asked to call the new interface.
102
+ - **Freeze window** — hours, not weeks, announced, for a high-churn move nobody can work around.
103
+ The refactor absorbs the rebase cost, not the feature. With no in-flight list, say
104
+ `[assumption] no known clashes` and add "check open PRs on these files before each step".
105
+
106
+ 9. **Emit the plan in the shape rule two chose.**
107
+ - **Chat reply** (default): the two-line lead; "What the code shows" as three to six bullets
108
+ citing `file:function`; the numbered steps, each one or two lines — the change, the why,
109
+ `[refactor]` or `[behaviour change]`, and the clash or rollback when it matters; one line
110
+ each for target and stop point and for not touched; at most three questions under "To
111
+ confirm". Aim under about 50 lines.
112
+ - **Document** (asked for, or large): the skeleton in
113
+ [`templates/refactor-plan.md`](templates/refactor-plan.md), each step with change,
114
+ type, technique and why, verified by, rollback (the live side for any flag), risk, size,
115
+ owner role. Save it where the user asked; the reply is the lead, the findings and the path.
116
+ Either shape ends with the scope-creep rule in one line: *"Anything found during the work
117
+ that isn't in a step goes to the debt register as a new item."*
118
+
119
+ **Non-interactive runs** (subagent, CI, headless): with no debt item, module or pain named at
120
+ all, emit `BLOCKED: need the debt to pay down — the module or register item and what it costs
121
+ the team`. Everything else degrades to `[assumption]`: a missing baseline becomes a measuring
122
+ step, missing code means steps cite only the names given, a missing in-flight list becomes
123
+ "no known clashes", a missing owner becomes a role.
124
+
125
+ ## Useful references in this skill
126
+
127
+ - [`reference.md`](reference.md) — the technique catalogue: when each fits, the step shape, rollback and "which side is live", the failure mode of each
128
+ - [`templates/refactor-plan.md`](templates/refactor-plan.md) — the chat shape and the document skeleton
129
+ - [`examples/search-ranker.md`](examples/search-ranker.md) — worked example: a 600-line ranking function with a flag pile and an in-flight PR, as a chat reply and as a document
130
+
131
+ ## Quality bar
132
+
133
+ - **The first two lines give the approach and the first step.** A chat question gets a chat-sized plan; a document only when asked or large.
134
+ - **Every step serves the stated goal,** and the last step reaches the structure that goal needs.
135
+ - **Every step is labelled refactor or behaviour change;** behaviour changes stand alone, after the refactor that makes them safe.
136
+ - **Characterisation tests come first, on the incident-prone behaviour,** and no open PR leaves that behaviour unpinned.
137
+ - **Caller contracts, copies outside the module and real test coverage are reported** from the files, cited `file:function`.
138
+ - **Every in-flight clash has a decision,** and a must-land feature PR is never blocked.
139
+ - **The scope fence and stop point exist;** new findings go to the register.
140
+ - **Every file, function, metric and number is from the input.** Unknowns are checks to run.
141
+
142
+ ## When to use this skill
143
+
144
+ - ✅ Planning how to pay down one register item, or one module everyone dreads changing
145
+ - ✅ Breaking up a god class, a giant function or a tangled package without a rewrite
146
+ - ✅ Replacing an internal dependency or a vendor client used from many call sites while features keep shipping
147
+ - ✅ Making the case that a refactor can be done in small, stoppable steps instead of a quarter-long branch
148
+
149
+ ## When NOT to use this skill
150
+
151
+ - ❌ Listing or ranking the debt in the first place — `tech-debt-register`
152
+ - ❌ Moving data, changing a schema or switching platforms with a backfill — `migration-plan`
153
+ - ❌ Writing the code for one of the steps — `feature-implement-loop`
154
+ - ❌ Deciding the target architecture or recording why — `adr-generator` or `design-doc`
155
+ - ❌ Reviewing a refactor PR that already exists — `pre-merge-review`
156
+ - ❌ Splitting a feature into user stories — `user-story-splitter`
157
+
158
+ ## Anti-patterns to avoid
159
+
160
+ - ❌ **The unrequested report.** Twelve thousand characters of tables in reply to "how do we do this??". The asker wanted the sequence and the first move.
161
+ - ❌ **Same code, more files.** A plan that only moves code unchanged when the goal was "add a zone without fear". Seven helpers that each still hard-code the rules haven't made the next zone easier.
162
+ - ❌ **Tests after the open PR.** Deferring characterisation tests until a feature PR merges, so the area with the incidents stays unpinned through the riskiest weeks.
163
+ - ❌ **Quietly changing a contract.** Making a function pure, or making `send` honestly return `false`, without moving the callers that relied on the old behaviour first.
164
+ - ❌ **Deleting the module and calling it done** while a script or report still carries its own copy.
165
+ - ❌ **The rewrite in disguise.** "Step 1: build the new OrderService. Step 2: switch over."
166
+ - ❌ **Fixing bugs mid-refactor.** The first unlabelled behaviour change makes every later test failure ambiguous.
167
+ - ❌ **No stop point,** or a target nobody measured. "Easier to change" ends never.
168
+ - ❌ **Claiming a file couldn't be read** without having opened it.
@@ -0,0 +1,46 @@
1
+ [
2
+ {
3
+ "query": "This 1,800-line OrderService is where every change goes wrong; plan how we untangle it without stopping feature work",
4
+ "should_trigger": true
5
+ },
6
+ {
7
+ "query": "TD-04 got approved for this quarter. Plan the refactor as small PRs we can ship one at a time",
8
+ "should_trigger": true
9
+ },
10
+ {
11
+ "query": "How do we break billing's dependency on the web package without a big-bang rewrite?",
12
+ "should_trigger": true
13
+ },
14
+ {
15
+ "query": "Give me a strangler-fig plan for replacing our legacy report builder module piece by piece",
16
+ "should_trigger": true
17
+ },
18
+ {
19
+ "query": "We want to stop calling the SMTP client from 14 places — plan the refactor with characterisation tests first",
20
+ "should_trigger": true
21
+ },
22
+ {
23
+ "query": "Go through our TODOs and incident notes and tell me which tech debt to pay down this quarter",
24
+ "should_trigger": false
25
+ },
26
+ {
27
+ "query": "Plan the cutover from MySQL to Postgres with a backfill and no downtime",
28
+ "should_trigger": false
29
+ },
30
+ {
31
+ "query": "Implement step 2 of the refactor plan: move calculate_tax into its own module with tests",
32
+ "should_trigger": false
33
+ },
34
+ {
35
+ "query": "Write an ADR for splitting the monolith into an orders service and a billing service",
36
+ "should_trigger": false
37
+ },
38
+ {
39
+ "query": "Review this refactoring PR before I merge it",
40
+ "should_trigger": false
41
+ },
42
+ {
43
+ "query": "Split the gift-card checkout epic into user stories",
44
+ "should_trigger": false
45
+ }
46
+ ]
@@ -0,0 +1,106 @@
1
+ {
2
+ "skill_name": "refactor-plan",
3
+ "evals": [
4
+ {
5
+ "id": 1,
6
+ "prompt": "This 1,800-line OrderService is where every change goes wrong; plan how we untangle it without stopping feature work. The code is order_service.py, the register row is debt-register-row.md, coverage is in test-coverage.txt, and what's in flight is in-flight-prs.md.",
7
+ "assertions": [
8
+ "Opens with the approach and the first step in its first lines, and states the target with its baseline taken from the register row (9 of the last 12 order PRs touching order_service.py, or the 3.1-day against 1.2-day review time) and a checkable stop point",
9
+ "Characterisation tests come before any step that touches calculate_tax, refund or send_confirmation_email (0% coverage), and they pin the per-line tax rounding and the zero-price negative-quantity lines as they behave today rather than fixing them",
10
+ "No step is a rewrite or a long-lived branch: every step is one PR merged to main, and moves are separated from changes",
11
+ "Each step says what changes, why (naming the technique where it matters), which tests verify it, and whether it is a pure refactor or a behaviour change; any step that adds a flag or an abstraction says which side is live for rollback",
12
+ "Names the clash with #481 (apply_discounts, charge_payment) and #486 (send_confirmation_email) and decides how each is handled (land first, wait, fold in, route through the new seam, or a short freeze window), rather than moving that code under the open PRs; characterisation tests are not delayed by either PR",
13
+ "The scope fence keeps the confirmation-email-on-failed-payment behaviour (INC-715) and the rounding out of the refactor as behaviour changes, and says new findings go to the debt register",
14
+ "Says the plan can stop after any step with the code better than before, and gives a reason the first real step goes first (for example the planned partial refunds or the in-flight gift-card work)",
15
+ "Does not invent modules, metrics or numbers not in the input; every file and function cited exists in the four files",
16
+ "Answers at the size of the ask: the plan is in the reply as a numbered sequence of steps of a line or two each, not a long multi-section document, since no document was requested"
17
+ ]
18
+ },
19
+ {
20
+ "id": 2,
21
+ "prompt": "Our Django app reads settings straight from os.environ in about 40 places and every test has to monkeypatch the environment. I want a plan to move everything behind one settings object. While we're at it, let's rename all the env vars to a consistent APP_ prefix.",
22
+ "assertions": [
23
+ "Produces the plan without asking questions first, with its assumptions tagged [assumption] at the top",
24
+ "Pulls the env var rename out of the refactor as a behaviour change, and either sends it to its own later parallel change (read both names, then drop the old) or to a separate ticket, instead of folding it into the steps",
25
+ "Uses 'about 40 direct os.environ reads' as the baseline and does not invent other measured numbers such as test times or file counts; anything else to measure is marked not measured",
26
+ "Introduces the settings object first and moves call sites over in batches of one PR each, so main stays releasable between batches",
27
+ "Characterisation tests pin how values are parsed today (defaults when unset, how strings like 'false' or '0' are read) before call sites move",
28
+ "The stop point is zero direct os.environ reads outside the settings object, enforced by a check in CI or a test",
29
+ "Does not invent file names, module names or settings that the prompt did not mention"
30
+ ]
31
+ },
32
+ {
33
+ "id": 3,
34
+ "prompt": "We want the nightly batch worker to import billing directly, but billing imports from web all over the place. imports.txt has the import graph and register.md has the debt row (TD-11). Plan the refactor. Also, invoices.total_cents should become a decimal column, so fold that in too.",
35
+ "assertions": [
36
+ "Builds the order from imports.txt as a prerequisite graph (mikado-style or explicit dependency order): the goal, the import edges blocking it, and which edges change cleanly first, with the reason",
37
+ "States the target as the count of billing-to-web imports in imports.txt (seven) going to zero, or the four in invoices.py and dunning.py that block the worker as the first milestone, with the final stop point enforced by an import check in CI",
38
+ "Puts characterisation tests for billing/proration.py before any step that changes it, because the register row says it has no tests, and relies on tests/billing/ for invoices and dunning",
39
+ "Sequences the edges that block worker/nightly.py (in billing/invoices.py and billing/dunning.py) before the proration and tax edges the worker doesn't need, and says the plan can stop with the worker unblocked and the code better",
40
+ "Pulls the invoices.total_cents column change out of the refactor as a schema change with data to migrate, to be planned separately as a migration, and does not fold it into any step",
41
+ "Every step cites the specific import line or file from imports.txt it removes, and states its rollback",
42
+ "Does not invent modules, functions, metrics or numbers beyond imports.txt and register.md"
43
+ ]
44
+ },
45
+ {
46
+ "id": 4,
47
+ "held_out": "2026-10-20",
48
+ "prompt": "I'm tech lead on the billing squad. I want a proper refactor plan for DEBT-14 in docs/tech-debt-register.md, i.e. build_invoice in src/billing/invoice.py. Goal: adding a tax region or a pricing rule should stop being a week of fear. No rewrite, and we can't freeze feature work; open PRs are in docs/open-prs.md. Callers I know about are preview_invoice in the same file and src/partners/statement.py; the only billing tests we have are tests/billing/test_payments.py. Please write the plan to docs/refactor-plan-debt-14.md so I can take it to planning on Thursday.",
49
+ "assertions": [
50
+ "Spots that src/partners/statement.py depends on build_invoice mutating the caller-supplied line_items list (it reads the appended tax line back from its own `lines` list, not from the returned invoice), and the plan either keeps that side effect working or moves that caller off it in its own step before the mutation is removed. It does not quietly make build_invoice pure.",
51
+ "Spots that reports/monthly_revenue.py has its own copied TAX_RATES and tax logic, which has already drifted (no US-TX, no EU reverse-charge rule). The plan puts it explicitly inside or outside the scope fence, and does not claim the refactor removes duplicated tax logic if the report is left out.",
52
+ "Puts characterisation tests for build_invoice first, before any extraction, and says they must cover the existing branches (short-period proration, annual anniversary month, included seats/annual add-on discount, usage tiers, each coupon type including expiry, tax-exempt and reverse charge, non-GBP conversion, credit notes, rounding). They pin current output, float-rounding quirks included. Notes that test_payments.py gives no coverage of invoice.py.",
53
+ "Deals with the clash with PR #412 (tax block and TAX_RATES, committed to customers for 1 Nov): it sequences the tax-block work after #412 merges, or otherwise avoids conflicting edits to that block while #412 is open. It also mentions draft PR #420 touching the add-ons block.",
54
+ "Keeps DEBT-15 (float to Decimal money) and any change to rounding or tax results out of scope as separate work, and states that every step preserves behaviour, i.e. invoice output is unchanged.",
55
+ "Breaks the work into small, separately mergeable PRs, each leaving main releasable. Each step names a specific technique that fits it, e.g. extract function per section (plan, add-ons, usage, coupons, tax, currency, credit note), or parallel change / expand-contract for the line_items parameter or the signature with 9 parameters.",
56
+ "Sets a measurable target with a baseline taken from the files (e.g. build_invoice is about 165 lines with 9 parameters and 0 tests; or the register's '2 incidents in Q3' / 'most of a sprint per tax region') or marked as 'to measure'. It also defines an explicit stop point where the refactor is finished.",
57
+ "Invents no facts: no made-up cyclomatic-complexity numbers, coverage percentages, incident counts, team sizes or dates beyond those in the inputs, and no claims that callers, modules or tests exist beyond those shown. New modules it proposes are clearly labelled as proposed. Unknowns, such as other callers of build_invoice, appear as checks to run (e.g. grep for build_invoice and line_items=), not as stated facts."
58
+ ]
59
+ },
60
+ {
61
+ "id": 5,
62
+ "held_out": "2026-10-20",
63
+ "prompt": "ok so we're moving off the old smtp relay to the new email provider, relay contract ends end of Q1. LegacyMailer has to die but i really dont want a big bang, peak season is coming and #88 needs to land. how do we do this?? stuff is in src/notify, see open-prs.txt and ci-notes.md. we'll write the new provider client ourselves, its just an HTTP API",
64
+ "assertions": [
65
+ "Flags that LegacyMailer.send returns true even when sending fails (the catch block returns true), and that src/orders/onOrderShipped.ts has a retryQueue branch on !sent that has never fired as a result. Warns that a new client which honestly returns false (or throws) would change behaviour, with possible retries or duplicate emails. Keeps the current semantics at the seam and treats surfacing errors as a separate, deliberate change.",
66
+ "Finds that scripts/nightly-digest.js has its own copy of the SMTP send and renderer, bypasses LegacyMailer, and skips the suppression list. Brings it into the plan, since the relay can't be switched off while it exists, and does not treat deleting legacyMailer.ts as finishing the migration.",
67
+ "Proposes an incremental migration through a seam (branch by abstraction and/or strangler fig): an interface the callers depend on, the new HTTP-provider implementation behind it, a per-template or per-caller switch via flag/config with a quick rollback, and legacy code deleted only once traffic on it is zero.",
68
+ "Lists the LegacyMailer behaviours that need characterisation tests before the swap: comma-splitting and lowercasing recipients, suppression-file filtering (and returning true when all recipients are suppressed), bccOps, per-template subjects including the 'there' fallback, missing vars rendering as empty, sendBulk's sequential 200ms pacing. Notes the existing test only covers render().",
69
+ "Handles each in-flight PR that matters: #88 (adds a template and subject inside legacyMailer.ts; must land for peak) is allowed to land first or is accommodated, not blocked. #91 (rewrites loadTemplate/templates to MJML) is flagged as a clash with the template-rendering step. #93 (new mailer.send caller for magic_link) is told to go through the new seam.",
70
+ "Sizes and sequences the steps against ci-notes.md (about 38 min e2e that flakes about 1 in 6, manual prod approval, about 2 deploys a week) and the end-of-Q1 deadline. Each step is its own small PR leaving main releasable. Includes a measurable target and stop point, e.g. templates or call sites still on the relay going from a baseline counted in the files to 0, or relay send volume reaching 0, before the contract ends.",
71
+ "Invents no facts: no named email vendor or specific API endpoints, no email volumes, error rates, team size or calendar dates beyond 'end of Q1', and no claim of call sites beyond those in the files. Unknowns (other callers of mailer, the provider's suppression handling, which templates are highest volume) are raised as questions or checks.",
72
+ "Delivers a usable plan in this reply despite the vague request, asking at most a few targeted questions, and recommends neither a big-bang cutover nor a feature freeze."
73
+ ]
74
+ },
75
+ {
76
+ "id": 6,
77
+ "held_out": "2026-10-20",
78
+ "prompt": "I need a refactor plan for InvoiceService in billing-service (billing-service/src/main/kotlin/billing/InvoiceService.kt). It's become a god class: JPA queries, native SQL and all our invoice rules (totals, tax, late fees, payment state) in one place. ADR-007 (docs/adr-007-persistence.md) commits us to moving off Hibernate to jOOQ in Q1, and I want the business rules pulled out before that starts so the ORM swap really is persistence-only. We can't freeze feature work on this service; what's open right now is in docs/open-prs.md. The existing tests are in billing-service/src/test/kotlin/billing/. Please write the plan as a sequence of PRs I can hand to the team.",
79
+ "assertions": [
80
+ "Flags that DunningJob.run() mutates invoices returned by findOverdue (status, dunningLevel, lastDunnedOn) without saving them, relying on Hibernate dirty checking inside @Transactional, and makes the plan handle it (an explicit save step or a test pinning that the changes persist) before findOverdue moves behind a non-JPA interface or the ORM changes.",
81
+ "Notices that InvoicePdfRenderer.taxFor is a copy of computeLineTax but rounds HALF_UP instead of HALF_EVEN, and does not silently merge the two as a behaviour-preserving step: it either fences the renderer out of scope or raises the mismatch as a decision for finance/the team (ADR-007 says tax rules are not to change).",
82
+ "Puts characterisation tests ahead of moving the untested logic, naming at least applyLateFees (grace days, exemption, the daysLate/30 multiplier, only raising the fee) and per-line tax rounding, since the existing tests only cover createInvoice; and says these tests should not need a database once the rules are extracted, or explains how they move from @SpringBootTest to plain unit tests.",
83
+ "Sequences around PR #412: does not start moving applyLateFees until #412 (approved, expected to merge this week) has landed, or explicitly rebases onto it; and names PR #415 (credit notes, changes void, about 3 weeks out) as a clash to coordinate with, e.g. by agreeing where issueCreditNote should live.",
84
+ "Sets a target taken from ADR-007's precondition, such as: invoice rule code has no jakarta.persistence imports and no EntityManager dependency, with the baseline read from the current file and a stop point at that precondition; the jOOQ migration itself is explicitly outside the scope fence, as are tax and late-fee rule changes.",
85
+ "Each step is a separate, small PR that leaves main releasable and behaviour unchanged, and each names a concrete technique that fits it (e.g. extract a pure InvoiceCalculator / late-fee policy, introduce a repository port in front of the EntityManager queries as branch by abstraction, parallel change for the findOverdue callers). It does not propose a rewrite or a long-lived branch.",
86
+ "Treats markPaid's native customer balance update (Customer.balance is mapped read-only) as persistence that has to stay atomic with the invoice save, and keeps it on the persistence side rather than moving it into the rules code.",
87
+ "Invents no modules, files, metrics, dates or numbers that are not in the provided files (no made-up coverage percentages, line counts, team sizes or PR numbers); anything new it proposes, such as a class name, is clearly labelled as a proposal."
88
+ ]
89
+ },
90
+ {
91
+ "id": 7,
92
+ "held_out": "2026-10-20",
93
+ "prompt": "web/src/components/CheckoutPanel.tsx is a nightmare. what's the plan? we ship nov 3 btw, open stuff is in notes/open-work.md",
94
+ "assertions": [
95
+ "The answer is short and easy to scan, matching a casual one-line question: a brief plan of a handful of steps, not a long formal document with many sections, and it does not ask a string of clarifying questions before giving a plan.",
96
+ "Respects the release: nothing beyond adding tests or trivial, zero-risk changes goes into CheckoutPanel before the 30 Oct code freeze / 3 Nov v4.8 release, and the structural refactor starts after release (or the plan explains why a specific step is safe to land before freeze).",
97
+ "Puts gift cards PR #231 (in v4.8, rewrites the totals block and submit(), expected merge by 28 Oct) ahead of any refactor of the same code, so the refactor does not conflict with it; and mentions #236 (address autocomplete, parked until after release) as touching the address section the plan would move.",
98
+ "Spots that MiniCart.tsx duplicates the totals maths but differs from CheckoutPanel (it rounds the discount per line instead of on the subtotal, and its free-shipping test is >= 5000 where CheckoutPanel uses > 5000), links this to WEB-512's 1p mismatch, and treats unifying them as a deliberate behaviour change needing a decision on which is correct, not a behaviour-preserving step.",
99
+ "Spots that OrderReview.tsx reads the 'checkout_draft' localStorage entry that CheckoutPanel writes (including total), and keeps that contract unchanged (or pins it with a test) when draft or totals logic is moved into a hook or module.",
100
+ "Asks for characterisation tests before moving logic, because the only existing test is a single snapshot: at least totals with a promo and the free-shipping threshold, and the draft write, as plain tests on behaviour rather than more snapshots.",
101
+ "Gives a target and stop point tied to something real in the input, for example WEB-530 (pay-by-bank in Q1 needs the payment step to stop assuming card fields) or pulling the totals and validation into pure functions, and says what is out of scope (e.g. not a redesign of the checkout flow).",
102
+ "Invents no files, metrics, dates or numbers not in the provided files; any count it quotes (e.g. how many useState calls there are or the file's length) matches the actual file, and proposed new hooks or modules are clearly labelled as proposals."
103
+ ]
104
+ }
105
+ ]
106
+ }
@@ -0,0 +1,5 @@
1
+ # Tech debt register — Checkout team (fictional), extract
2
+
3
+ | ID | Item | Where | Cost today (evidence) | Fix size | Decision | Trigger | Owner |
4
+ |---|---|---|---|---|---|---|---|
5
+ | TD-04 | `OrderService` does pricing, tax, stock, payment, email, cancel, refund and finance export in one 1,812-line class; every order change edits it | `orders/order_service.py` | 9 of the last 12 merged order PRs touched `order_service.py`; median review time for those PRs 3.1 days against a team median of 1.2 days; INC-702 (tax totals wrong after a discount change, Aug) and INC-715 (confirmation emails sent for failed payments, Sep) | L | Pay now | A third incident traced to `order_service.py` | Checkout team |
@@ -0,0 +1,7 @@
1
+ # Open PRs touching orders/ (fictional)
2
+
3
+ - **#481 Gift cards at checkout** (Checkout team) — edits `OrderService.apply_discounts` and `OrderService.charge_payment`. Expected to merge next week.
4
+ - **#486 Send order mail through the notifications service** (Platform team) — replaces `smtp_client.send` in `OrderService.send_confirmation_email`; adds `notifications/client.py`. In review.
5
+ - **#490 Finance export: add VAT number column** (Finance tooling) — edits `OrderService.export_for_finance`. Draft, no date.
6
+
7
+ Planned next quarter (not started): partial refunds, which needs changes to `OrderService.refund`.
@@ -0,0 +1,74 @@
1
+ # orders/order_service.py (Lantern Outfitters web shop, fictional)
2
+ # Excerpt: the full file is 1,812 lines. Signatures are complete; bodies shown only where
3
+ # they matter. Elided bodies are marked "...".
4
+
5
+ from decimal import Decimal, ROUND_HALF_UP
6
+
7
+ from orders.models import Order, OrderLine
8
+ from infra import stock_db # direct DB client
9
+ from infra import payments_gateway # third-party SDK wrapper
10
+ from infra import smtp_client # sends mail synchronously
11
+ from finance.csv_writer import write_row
12
+
13
+
14
+ class OrderService:
15
+ """Creates, prices, pays for, fulfils, cancels, refunds and exports orders."""
16
+
17
+ def create_order(self, customer, cart, promo_code=None):
18
+ order = Order(customer=customer)
19
+ for item in cart.items:
20
+ order.lines.append(OrderLine(sku=item.sku, qty=item.qty, unit_price=item.price))
21
+ self.apply_discounts(order, promo_code)
22
+ self.calculate_tax(order)
23
+ self.reserve_stock(order)
24
+ result = self.charge_payment(order)
25
+ self.send_confirmation_email(order) # sent even when result.ok is False
26
+ order.status = "paid" if result.ok else "payment_failed"
27
+ order.save()
28
+ return order
29
+
30
+ def apply_discounts(self, order, promo_code):
31
+ for line in order.lines:
32
+ if line.qty <= 0:
33
+ # Returns flow builds negative-qty lines and relies on them pricing at zero.
34
+ line.price = Decimal("0")
35
+ continue
36
+ line.price = line.unit_price * line.qty
37
+ if promo_code:
38
+ ... # 140 lines: percentage, fixed-amount, buy-x-get-y, staff discount
39
+
40
+ def calculate_tax(self, order):
41
+ rate = Decimal("0.20")
42
+ total_tax = Decimal("0")
43
+ for line in order.lines:
44
+ # Rounds per line, not per order. Invoices issued since 2021 depend on this.
45
+ line_tax = (line.price * rate).quantize(Decimal("0.01"), rounding=ROUND_HALF_UP)
46
+ line.tax = line_tax
47
+ total_tax += line_tax
48
+ order.tax = total_tax
49
+
50
+ def reserve_stock(self, order):
51
+ for line in order.lines:
52
+ stock_db.execute("UPDATE stock SET reserved = reserved + %s WHERE sku = %s",
53
+ (line.qty, line.sku))
54
+
55
+ def charge_payment(self, order):
56
+ ... # 210 lines: retries, 3-D Secure, gateway error mapping
57
+ return payments_gateway.charge(order.customer.payment_token, order.total())
58
+
59
+ def send_confirmation_email(self, order):
60
+ body = self._render_confirmation(order)
61
+ smtp_client.send(to=order.customer.email, subject="Your order", body=body)
62
+
63
+ def _render_confirmation(self, order):
64
+ ... # 180 lines of string templating
65
+
66
+ def cancel_order(self, order):
67
+ ... # calls stock_db directly, then payments_gateway.void, then smtp_client.send
68
+
69
+ def refund(self, order, lines):
70
+ ... # builds negative-qty lines, calls apply_discounts and calculate_tax, then
71
+ # payments_gateway.refund and smtp_client.send
72
+
73
+ def export_for_finance(self, orders, path):
74
+ ... # 260 lines: one CSV row per order line via finance.csv_writer.write_row
@@ -0,0 +1,14 @@
1
+ Coverage for orders/order_service.py (from tests/orders/test_order_service.py)
2
+
3
+ OrderService.create_order 71%
4
+ OrderService.apply_discounts 38%
5
+ OrderService.calculate_tax 0%
6
+ OrderService.reserve_stock 55%
7
+ OrderService.charge_payment 64%
8
+ OrderService.send_confirmation_email 0%
9
+ OrderService._render_confirmation 0%
10
+ OrderService.cancel_order 22%
11
+ OrderService.refund 0%
12
+ OrderService.export_for_finance 80%
13
+
14
+ tests/orders/test_order_service.py: 46 tests, 6m40s in CI (starts a real stock database container)
@@ -0,0 +1,22 @@
1
+ # Imports from billing/ into web/ (fictional Quillrow app), generated by grep on main
2
+ # Goal: billing/ imports nothing from web/, so the nightly batch worker (worker/nightly.py)
3
+ # can import billing without starting the web framework.
4
+
5
+ billing/invoices.py:4 from web.request_context import current_tenant
6
+ billing/invoices.py:5 from web.templates import render_pdf
7
+ billing/proration.py:3 from web.settings import BILLING_ROUNDING
8
+ billing/proration.py:9 from web.request_context import current_tenant
9
+ billing/dunning.py:2 from web.mailer import send_mail
10
+ billing/dunning.py:3 from web.templates import render_email
11
+ billing/tax.py:6 from web.settings import TAX_REGION
12
+
13
+ # Imports inside web/ that the above pull in
14
+ web/request_context.py imports web.app (starts the framework on import)
15
+ web/templates.py imports web.app
16
+ web/mailer.py imports web.templates
17
+ web/settings.py no web imports
18
+
19
+ # Callers of billing from outside billing/
20
+ web/views/invoices.py billing.invoices.issue_invoice
21
+ web/views/plans.py billing.proration.prorate
22
+ worker/nightly.py (wants) billing.dunning.run_dunning, billing.invoices.issue_invoice
@@ -0,0 +1,3 @@
1
+ | ID | Item | Where | Cost today (evidence) | Fix size | Decision | Trigger | Owner |
2
+ |---|---|---|---|---|---|---|---|
3
+ | TD-11 | `billing` imports from `web`, so anything that imports billing starts the web app | `billing/` | The nightly dunning job runs as an HTTP call to a hidden admin endpoint because the batch worker can't import billing; that endpoint timed out on 3 nights in September and dunning emails went out a day late. `billing/proration.py` has no tests; `billing/invoices.py` and `billing/dunning.py` are covered by `tests/billing/`. | M | Pay now | Another missed dunning run | Billing team |
@@ -0,0 +1,6 @@
1
+ # Open PRs (billing repo) — snapshot 2026-10-19
2
+
3
+ - #412 "EU B2B reverse charge: check seller country, add NL/ES/IT rates" — author: dev-b — touches `src/billing/invoice.py` (tax block + TAX_RATES), +88/-14. Target: merge this sprint, customer commitment for 1 Nov.
4
+ - #415 "Bump pytest to 8.x" — touches `requirements-dev.txt` only.
5
+ - #418 "Partner statements: add CSV download" — touches `src/partners/statement.py`, `src/partners/views.py`.
6
+ - #420 "Annual plans: support mid-year seat true-ups" — draft — touches `src/billing/invoice.py` (add-ons block).
@@ -0,0 +1,7 @@
1
+ # Tech debt register — billing squad
2
+
3
+ | ID | Item | Pain | Owner | Raised |
4
+ |----|------|------|-------|--------|
5
+ | DEBT-09 | Payment retries use sleep() in request thread | Slow checkout under load | payments | 2025-11 |
6
+ | DEBT-14 | `src/billing/invoice.py::build_invoice` is one giant function with 9 keyword args; no unit tests | Every pricing change touches it; 2 billing incidents in Q3 traced to edits here (wrong proration, coupon applied after tax). Adding a new tax region took a dev most of a sprint. | billing | 2026-07 |
7
+ | DEBT-15 | Float money arithmetic throughout billing | Cent-level mismatches finance fixes by hand monthly | billing | 2026-07 |
@@ -0,0 +1,44 @@
1
+ """Finance's monthly revenue report. Run by cron on the 2nd of each month.
2
+
3
+ Recomputes tax per invoice so the report can split revenue by region.
4
+ """
5
+
6
+ import csv
7
+ import sys
8
+
9
+ from billing.db import invoices_between, fetch_customer
10
+
11
+ # copied from billing/invoice.py so the report doesn't need the whole
12
+ # billing import chain (2023)
13
+ TAX_RATES = {
14
+ "GB": 0.20,
15
+ "DE": 0.19,
16
+ "FR": 0.20,
17
+ "IE": 0.23,
18
+ "US-CA": 0.0725,
19
+ "US-NY": 0.04,
20
+ }
21
+
22
+
23
+ def tax_for(customer, subtotal):
24
+ if customer["country"] == "US":
25
+ region = "US-" + (customer.get("state") or "")
26
+ else:
27
+ region = customer["country"]
28
+ rate = TAX_RATES.get(region, 0.0)
29
+ if customer.get("tax_exempt"):
30
+ rate = 0.0
31
+ return region, round(subtotal * rate, 2)
32
+
33
+
34
+ def main(start, end, out=sys.stdout):
35
+ writer = csv.writer(out)
36
+ writer.writerow(["invoice_id", "region", "subtotal", "tax"])
37
+ for inv in invoices_between(start, end):
38
+ customer = fetch_customer(inv["customer_id"])
39
+ region, tax = tax_for(customer, inv["subtotal"])
40
+ writer.writerow([inv["id"], region, inv["subtotal"], tax])
41
+
42
+
43
+ if __name__ == "__main__":
44
+ main(sys.argv[1], sys.argv[2])