skilldrop-cli 1.3.0 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (200) hide show
  1. package/README.md +10 -10
  2. package/bin/skilldrop.js +15 -10
  3. package/catalogue.json +8 -4
  4. package/contracts/catalogue.schema.json +2 -1
  5. package/model-routing.json +70 -78
  6. package/package.json +1 -1
  7. package/packs/ai-engineering/skills/agent-adoption-stage/SKILL.md +0 -1
  8. package/packs/ai-engineering/skills/agent-adoption-stage/manifest.json +3 -4
  9. package/packs/ai-engineering/skills/ai-adoption-rollout/SKILL.md +0 -1
  10. package/packs/ai-engineering/skills/ai-adoption-rollout/manifest.json +3 -4
  11. package/packs/ai-engineering/skills/ai-readiness-assessment/SKILL.md +0 -1
  12. package/packs/ai-engineering/skills/ai-readiness-assessment/manifest.json +1 -2
  13. package/packs/ai-engineering/skills/ai-use-case-triage/manifest.json +3 -3
  14. package/packs/ai-engineering/skills/judge-audit/SKILL.md +29 -13
  15. package/packs/ai-engineering/skills/judge-audit/evals/evals.json +30 -0
  16. package/packs/ai-engineering/skills/judge-audit/evals/files/12/judge_runs.csv +241 -0
  17. package/packs/ai-engineering/skills/judge-audit/evals/files/12/rosa_labels.csv +33 -0
  18. package/packs/ai-engineering/skills/judge-audit/evals/files/13/moderation_eval.csv +601 -0
  19. package/packs/ai-engineering/skills/judge-audit/manifest.json +2 -3
  20. package/packs/ai-engineering/skills/llm-eval-harness/SKILL.md +1 -2
  21. package/packs/ai-engineering/skills/llm-eval-harness/manifest.json +3 -3
  22. package/packs/ai-engineering/skills/mcp-pairing/manifest.json +1 -1
  23. package/packs/ai-engineering/skills/mcp-pairing/reference.md +4 -3
  24. package/packs/ai-engineering/skills/subagent-design/SKILL.md +1 -0
  25. package/packs/ai-engineering/skills/subagent-design/examples/help-center-localization.md +146 -0
  26. package/packs/ai-engineering/skills/subagent-design/manifest.json +2 -2
  27. package/packs/api-builder/skills/prompt-caching-advisor/SKILL.md +4 -0
  28. package/packs/api-builder/skills/prompt-caching-advisor/examples/support-bot-block-order.md +120 -0
  29. package/packs/api-builder/skills/prompt-caching-advisor/manifest.json +2 -2
  30. package/packs/api-builder/skills/tool-use-schema-writer/manifest.json +2 -2
  31. package/packs/career/skills/application-form-answers/manifest.json +3 -3
  32. package/packs/career/skills/cover-letter/manifest.json +3 -3
  33. package/packs/career/skills/resume-tailor/manifest.json +3 -3
  34. package/packs/converters/skills/md-to-docx/manifest.json +3 -3
  35. package/packs/core/loops/ship-a-draft/evals/evals.json +28 -0
  36. package/packs/core/loops/ship-a-draft/evals/files/ship-a-draft-adr-from-thread/slack-thread.txt +47 -0
  37. package/packs/core/loops/ship-a-draft/evals/files/ship-a-draft-announcement-unsettled/ticket-PRD-412.md +44 -0
  38. package/packs/core/skills/output-hygiene/manifest.json +3 -3
  39. package/packs/data-analytics/loops/measure/evals/evals.json +30 -0
  40. package/packs/data-analytics/loops/measure/evals/files/measure-active-customers-dispute/schema.md +43 -0
  41. package/packs/data-analytics/loops/measure/evals/files/measure-active-customers-dispute/sql/finance_active_customers.sql +7 -0
  42. package/packs/data-analytics/loops/measure/evals/files/measure-active-customers-dispute/sql/growth_active_customers.sql +9 -0
  43. package/packs/data-analytics/loops/measure/evals/files/measure-active-customers-dispute/thread.md +30 -0
  44. package/packs/data-analytics/loops/measure/evals/files/measure-retention-dashboard/request.md +34 -0
  45. package/packs/data-analytics/loops/measure/evals/files/measure-retention-dashboard/schema.md +58 -0
  46. package/packs/data-analytics/loops/measure/evals/files/measure-retention-dashboard/sql/active_subscribers.sql +17 -0
  47. package/packs/data-analytics/loops/measure/evals/files/measure-retention-dashboard/sql/net_revenue_by_plan.sql +15 -0
  48. package/packs/data-analytics/loops/measure/evals/files/measure-retention-dashboard/sql/weekly_churn_rate.sql +34 -0
  49. package/packs/data-analytics/skills/dashboard-spec/SKILL.md +0 -1
  50. package/packs/data-analytics/skills/dashboard-spec/manifest.json +1 -2
  51. package/packs/design/skills/slide-outliner/manifest.json +2 -2
  52. package/packs/dev-team/loops/build/evals/evals.json +28 -0
  53. package/packs/dev-team/loops/build/evals/files/build-promo-codes/.github/workflows/ci.yml.fixture +19 -0
  54. package/packs/dev-team/loops/build/evals/files/build-promo-codes/cartcalc/__init__.py +4 -0
  55. package/packs/dev-team/loops/build/evals/files/build-promo-codes/cartcalc/pricing.py +47 -0
  56. package/packs/dev-team/loops/build/evals/files/build-promo-codes/pyproject.toml.fixture +9 -0
  57. package/packs/dev-team/loops/build/evals/files/build-promo-codes/story.md +61 -0
  58. package/packs/dev-team/loops/build/evals/files/build-promo-codes/tests/__init__.py +0 -0
  59. package/packs/dev-team/loops/build/evals/files/build-promo-codes/tests/test_pricing.py +49 -0
  60. package/packs/dev-team/loops/build/evals/files/build-rate-limiter-not-ready/.github/workflows/ci.yml.fixture +19 -0
  61. package/packs/dev-team/loops/build/evals/files/build-rate-limiter-not-ready/bug-report.md +25 -0
  62. package/packs/dev-team/loops/build/evals/files/build-rate-limiter-not-ready/pr-description.md +42 -0
  63. package/packs/dev-team/loops/build/evals/files/build-rate-limiter-not-ready/pyproject.toml.fixture +9 -0
  64. package/packs/dev-team/loops/build/evals/files/build-rate-limiter-not-ready/ratelimit/__init__.py +4 -0
  65. package/packs/dev-team/loops/build/evals/files/build-rate-limiter-not-ready/ratelimit/bucket.py +49 -0
  66. package/packs/dev-team/loops/build/evals/files/build-rate-limiter-not-ready/tests/__init__.py +0 -0
  67. package/packs/dev-team/loops/build/evals/files/build-rate-limiter-not-ready/tests/test_bucket.py +63 -0
  68. package/packs/dev-team/loops/modernize/evals/evals.json +30 -0
  69. package/packs/dev-team/loops/modernize/evals/files/modernize-late-fee-batch-port/.github/workflows/ci.yml.fixture +11 -0
  70. package/packs/dev-team/loops/modernize/evals/files/modernize-late-fee-batch-port/README.md +17 -0
  71. package/packs/dev-team/loops/modernize/evals/files/modernize-late-fee-batch-port/docs/adr/0004-late-fee-calculation.md +25 -0
  72. package/packs/dev-team/loops/modernize/evals/files/modernize-late-fee-batch-port/legacy/feecalc.py +74 -0
  73. package/packs/dev-team/loops/modernize/evals/files/modernize-late-fee-batch-port/samples/accounts_2026-10.txt +10 -0
  74. package/packs/dev-team/loops/modernize/evals/files/modernize-late-fee-batch-port/samples/accounts_edge.txt +11 -0
  75. package/packs/dev-team/loops/modernize/evals/files/modernize-late-fee-batch-port/tests/test_feecalc_smoke.py +21 -0
  76. package/packs/dev-team/loops/modernize/evals/files/modernize-late-fee-rewrite-not-equivalent/.github/workflows/ci.yml.fixture +11 -0
  77. package/packs/dev-team/loops/modernize/evals/files/modernize-late-fee-rewrite-not-equivalent/PR-412.md +24 -0
  78. package/packs/dev-team/loops/modernize/evals/files/modernize-late-fee-rewrite-not-equivalent/README.md +17 -0
  79. package/packs/dev-team/loops/modernize/evals/files/modernize-late-fee-rewrite-not-equivalent/docs/adr/0004-late-fee-calculation.md +25 -0
  80. package/packs/dev-team/loops/modernize/evals/files/modernize-late-fee-rewrite-not-equivalent/hartwell_fees/__init__.py +1 -0
  81. package/packs/dev-team/loops/modernize/evals/files/modernize-late-fee-rewrite-not-equivalent/hartwell_fees/cli.py +33 -0
  82. package/packs/dev-team/loops/modernize/evals/files/modernize-late-fee-rewrite-not-equivalent/hartwell_fees/extract.py +33 -0
  83. package/packs/dev-team/loops/modernize/evals/files/modernize-late-fee-rewrite-not-equivalent/hartwell_fees/rules.py +39 -0
  84. package/packs/dev-team/loops/modernize/evals/files/modernize-late-fee-rewrite-not-equivalent/legacy/feecalc.py +74 -0
  85. package/packs/dev-team/loops/modernize/evals/files/modernize-late-fee-rewrite-not-equivalent/samples/accounts_2026-10.txt +10 -0
  86. package/packs/dev-team/loops/modernize/evals/files/modernize-late-fee-rewrite-not-equivalent/samples/accounts_edge.txt +11 -0
  87. package/packs/dev-team/loops/modernize/evals/files/modernize-late-fee-rewrite-not-equivalent/tests/test_feecalc_smoke.py +21 -0
  88. package/packs/dev-team/loops/release/LOOP.md +4 -4
  89. package/packs/dev-team/loops/release/evals/eval_queries.json +10 -0
  90. package/packs/dev-team/loops/release/evals/evals.json +34 -0
  91. package/packs/dev-team/loops/release/evals/files/release-irreversible-tomorrow/0061_switch_to_haldrin_tokens.sql +22 -0
  92. package/packs/dev-team/loops/release/evals/files/release-irreversible-tomorrow/eng-notes.md +40 -0
  93. package/packs/dev-team/loops/release/evals/files/release-irreversible-tomorrow/merged-prs.md +13 -0
  94. package/packs/dev-team/loops/release/evals/files/release-saved-searches/merged-prs.md +13 -0
  95. package/packs/dev-team/loops/release/evals/files/release-saved-searches/readiness-evidence.md +27 -0
  96. package/packs/dev-team/loops/release/evals/files/release-saved-searches/traffic-baseline.md +23 -0
  97. package/packs/dev-team/loops/release/loop.json +1 -1
  98. package/packs/dev-team/skills/bug-triage/SKILL.md +1 -0
  99. package/packs/dev-team/skills/bug-triage/examples/export-row-limit-escalation.md +126 -0
  100. package/packs/dev-team/skills/bug-triage/manifest.json +2 -2
  101. package/packs/dev-team/skills/feature-implement-loop/manifest.json +2 -2
  102. package/packs/dev-team/skills/pr-description-writer/manifest.json +3 -3
  103. package/packs/dev-team/skills/pre-merge-review/manifest.json +2 -2
  104. package/packs/dev-team/skills/release-notes/manifest.json +2 -2
  105. package/packs/dev-team/skills/test-plan-generator/manifest.json +2 -2
  106. package/packs/experience-design/skills/service-blueprint/manifest.json +3 -3
  107. package/packs/experience-design/skills/ux-writing/manifest.json +3 -3
  108. package/packs/grc/loops/comply/evals/evals.json +29 -0
  109. package/packs/grc/loops/comply/evals/files/comply-chat-vendor-launch/vendor-brief.md +72 -0
  110. package/packs/grc/loops/comply/evals/files/comply-loyalty-app-unowned-risks/launch-note.md +31 -0
  111. package/packs/grc/loops/comply/evals/files/comply-loyalty-app-unowned-risks/risk-register.csv +6 -0
  112. package/packs/infra-as-code/skills/terraform-module/manifest.json +3 -3
  113. package/packs/product-manager/skills/prd-draft/SKILL.md +1 -0
  114. package/packs/product-manager/skills/prd-draft/examples/approver-delay-not-slack.md +142 -0
  115. package/packs/product-manager/skills/prd-draft/manifest.json +2 -2
  116. package/packs/product-manager/skills/user-journey-map/manifest.json +2 -2
  117. package/packs/research/loops/research/evals/evals.json +28 -0
  118. package/packs/research/loops/research/evals/files/research-ticket-spike-confounded/sources/t1-ticket-volume.md +15 -0
  119. package/packs/research/loops/research/evals/files/research-ticket-spike-confounded/sources/t2-release-notes-4-2.md +11 -0
  120. package/packs/research/loops/research/evals/files/research-ticket-spike-confounded/sources/t3-billing-migration.md +12 -0
  121. package/packs/research/loops/research/evals/files/research-ticket-spike-confounded/sources/t4-agent-poll.md +11 -0
  122. package/packs/research/loops/research/evals/files/research-ticket-spike-confounded/sources/t5-app-store-reviews.md +12 -0
  123. package/packs/research/loops/research/evals/files/research-trial-conversion-drop/sources/s1-analytics-export.md +29 -0
  124. package/packs/research/loops/research/evals/files/research-trial-conversion-drop/sources/s2-email-delivery-report.md +25 -0
  125. package/packs/research/loops/research/evals/files/research-trial-conversion-drop/sources/s3-pricing-changelog.md +9 -0
  126. package/packs/research/loops/research/evals/files/research-trial-conversion-drop/sources/s4-trial-interview-notes.md +21 -0
  127. package/packs/research/loops/research/evals/files/research-trial-conversion-drop/sources/s5-competitor-note.md +9 -0
  128. package/packs/research/loops/research/evals/files/research-trial-conversion-drop/sources/s6-weekly-growth-recap.md +7 -0
  129. package/packs/research/skills/research-plan/manifest.json +3 -3
  130. package/packs/research/skills/source-synthesis/manifest.json +3 -3
  131. package/packs/skill-engineering/skills/skill-author/manifest.json +3 -3
  132. package/packs/solution-architect/loops/design/evals/evals.json +28 -0
  133. package/packs/solution-architect/loops/design/evals/files/design-bulk-export/adr/0001-postgres-system-of-record.md +23 -0
  134. package/packs/solution-architect/loops/design/evals/files/design-bulk-export/adr/0002-background-work-via-job-queue.md +23 -0
  135. package/packs/solution-architect/loops/design/evals/files/design-bulk-export/adr/0003-tenant-isolation-row-level.md +25 -0
  136. package/packs/solution-architect/loops/design/evals/files/design-bulk-export/prd-bulk-export.md +50 -0
  137. package/packs/solution-architect/loops/design/evals/files/design-draft-prd-webhooks/adr/0002-background-work-via-job-queue.md +23 -0
  138. package/packs/solution-architect/loops/design/evals/files/design-draft-prd-webhooks/adr/0004-new-stateful-infra-needs-platform-signoff.md +22 -0
  139. package/packs/solution-architect/loops/design/evals/files/design-draft-prd-webhooks/prd-partner-webhooks.md +36 -0
  140. package/packs/solution-architect/skills/api-contract-draft/manifest.json +3 -3
  141. package/packs/solution-architect/skills/architecture-diagrams/manifest.json +3 -3
  142. package/packs/solution-architect/skills/design-doc/manifest.json +2 -2
  143. package/packs/solution-architect/skills/figma-diagrams/manifest.json +3 -3
  144. package/packs/sre-oncall/skills/cloud-cost-review/SKILL.md +0 -1
  145. package/packs/sre-oncall/skills/cloud-cost-review/manifest.json +1 -2
  146. package/packs/sre-oncall/skills/delivery-metrics-report/SKILL.md +0 -1
  147. package/packs/sre-oncall/skills/delivery-metrics-report/manifest.json +1 -2
  148. package/packs/sre-oncall/skills/incident-comms/manifest.json +2 -2
  149. package/packs/sre-oncall/skills/postmortem-generator/manifest.json +2 -2
  150. package/packs/stakeholder-comms/skills/decision-log/manifest.json +2 -2
  151. package/packs/trackers/skills/team-status-report/manifest.json +3 -3
  152. package/profiles.json +1 -1
  153. package/packs/ai-engineering/loops/adopt/LOOP.md +0 -120
  154. package/packs/ai-engineering/loops/adopt/evals/eval_queries.json +0 -10
  155. package/packs/ai-engineering/loops/adopt/evals/evals.json +0 -69
  156. package/packs/ai-engineering/loops/adopt/evals/files/adopt-first-wave-to-sign-off/ai-ideas.md +0 -15
  157. package/packs/ai-engineering/loops/adopt/evals/files/adopt-first-wave-to-sign-off/org-context.md +0 -27
  158. package/packs/ai-engineering/loops/adopt/evals/files/adopt-first-wave-to-sign-off/staff-survey.csv +0 -11
  159. package/packs/ai-engineering/loops/adopt/evals/files/adopt-sponsor-slice-blocked-by-client-data/ai-ideas.md +0 -13
  160. package/packs/ai-engineering/loops/adopt/evals/files/adopt-sponsor-slice-blocked-by-client-data/firm-context.md +0 -23
  161. package/packs/ai-engineering/loops/adopt/loop.json +0 -53
  162. package/packs/ai-engineering/skills/ai-usage-report/SKILL.md +0 -111
  163. package/packs/ai-engineering/skills/ai-usage-report/evals/eval_queries.json +0 -30
  164. package/packs/ai-engineering/skills/ai-usage-report/evals/evals.json +0 -46
  165. package/packs/ai-engineering/skills/ai-usage-report/evals/files/1/northwind-ai-events.csv +0 -4213
  166. package/packs/ai-engineering/skills/ai-usage-report/evals/files/2/draftwell-events.csv +0 -249
  167. package/packs/ai-engineering/skills/ai-usage-report/evals/files/2/export-note.md +0 -9
  168. package/packs/ai-engineering/skills/ai-usage-report/evals/files/2/published-assets.csv +0 -44
  169. package/packs/ai-engineering/skills/ai-usage-report/evals/files/2/team-roster.md +0 -10
  170. package/packs/ai-engineering/skills/ai-usage-report/examples/weekly-engineering-team.md +0 -133
  171. package/packs/ai-engineering/skills/ai-usage-report/manifest.json +0 -12
  172. package/packs/ai-engineering/skills/ai-usage-report/scripts/build_report.py +0 -453
  173. package/packs/ai-engineering/skills/ai-usage-report/templates/report-per-user.md +0 -42
  174. package/packs/ai-engineering/skills/ai-usage-report/templates/report-team-rollup.md +0 -49
  175. package/packs/ai-engineering/skills/ai-usage-report/templates/usage-event-schema.md +0 -76
  176. package/packs/career/loops/apply/LOOP.md +0 -97
  177. package/packs/career/loops/apply/evals/eval_queries.json +0 -9
  178. package/packs/career/loops/apply/evals/evals.json +0 -27
  179. package/packs/career/loops/apply/loop.json +0 -52
  180. package/packs/product-manager/loops/discover/LOOP.md +0 -82
  181. package/packs/product-manager/loops/discover/evals/eval_queries.json +0 -30
  182. package/packs/product-manager/loops/discover/evals/evals.json +0 -26
  183. package/packs/product-manager/loops/discover/loop.json +0 -41
  184. package/packs/product-manager/skills/lean-canvas/SKILL.md +0 -162
  185. package/packs/product-manager/skills/lean-canvas/evals/eval_queries.json +0 -42
  186. package/packs/product-manager/skills/lean-canvas/evals/evals.json +0 -82
  187. package/packs/product-manager/skills/lean-canvas/evals/files/1/pawpass-notes.md +0 -54
  188. package/packs/product-manager/skills/lean-canvas/evals/files/3/lunchloop-pitch.md +0 -30
  189. package/packs/product-manager/skills/lean-canvas/evals/files/4/clubkit-notes.md +0 -44
  190. package/packs/product-manager/skills/lean-canvas/evals/files/5/cost-model.csv +0 -5
  191. package/packs/product-manager/skills/lean-canvas/evals/files/5/proposal.md +0 -40
  192. package/packs/product-manager/skills/lean-canvas/evals/files/5/stakeholder-emails.txt +0 -18
  193. package/packs/product-manager/skills/lean-canvas/examples/bike-repair.md +0 -72
  194. package/packs/product-manager/skills/lean-canvas/manifest.json +0 -34
  195. package/packs/product-manager/skills/lean-canvas/reference.md +0 -102
  196. package/packs/product-manager/skills/lean-canvas/scripts/unit_economics.py +0 -211
  197. package/packs/sre-oncall/loops/operate/LOOP.md +0 -84
  198. package/packs/sre-oncall/loops/operate/evals/eval_queries.json +0 -30
  199. package/packs/sre-oncall/loops/operate/evals/evals.json +0 -25
  200. package/packs/sre-oncall/loops/operate/loop.json +0 -41
@@ -0,0 +1,146 @@
1
+ # Worked example: a seven-agent localization plan that grades its own work
2
+
3
+ Input → output oracle for `subagent-design`. Shows a fan-out that is justified but drawn
4
+ wrong: a translator that checks itself, a middle layer with no job, a summary agent standing
5
+ in for a merge, and auto-publishing. It also shows the one barrier the work actually needs.
6
+
7
+ ## Input
8
+
9
+ > We're localizing our help center with AI agents. My team lead drew up the plan below. Is
10
+ > this the right setup? Our docs lead has to sign off, so keep it short.
11
+ >
12
+ > **Plan:**
13
+ > 1. A Coordinator agent spawns three Language Leads (de, fr, ja).
14
+ > 2. Each Language Lead spawns one Translator agent per article. The Translator translates
15
+ > the article, then reviews its own translation against the glossary and fixes anything it
16
+ > finds.
17
+ > 3. When all 960 translations are done, a Summary agent reads them all and writes a report of
18
+ > quality issues for the docs lead.
19
+ > 4. The Coordinator publishes everything to the help center.
20
+ > 5. We want 7+ agents because more agents run faster.
21
+ >
22
+ > **Facts:**
23
+ > - 320 English articles, about 1,500 words each, into three languages.
24
+ > - A glossary of 410 product terms with an approved translation per language. Style rule: UI
25
+ > labels stay in English.
26
+ > - Pilot of 40 articles into Japanese: the Translator's self-review marked all 40 "pass". Our
27
+ > Japanese linguist found 11 with UI labels translated, and 4 where the same term was
28
+ > translated two different ways in different articles.
29
+ > - Today a linguist per language approves each article before it goes live.
30
+
31
+ ## Output
32
+
33
+ **Not as drawn. Splitting the work per article is right, but four parts of the plan need to
34
+ change.** Keep one translation job per article and language. A separate agent should check
35
+ each translation instead of the translator checking itself. Code should build the report, not
36
+ a summary agent. Drop the Language Lead layer. And nothing goes live without the linguist,
37
+ who stays the final approval. That leaves two agent roles, each run once per article and
38
+ language, plus code around them. The pilot is the reason: the translator passed its own work
39
+ 40 times out of 40, and the linguist found problems in at least 11 of those articles.
40
+
41
+ Assumed: each article can be translated on its own, given the glossary.
42
+
43
+ # Orchestration plan: help-center localization (320 articles × de, fr, ja)
44
+
45
+ > Fan-out justification: **context separation**, because about 480,000 words of source won't
46
+ > fit one context well and each article stands alone. **Role conflict** for checking, because
47
+ > the agent that wrote a translation can't honestly grade it, as the 40/40 pilot showed.
48
+ > Speed comes with the per-article split. It is not a reason on its own.
49
+
50
+ ## Role cards
51
+
52
+ ### orchestrator (code, not an agent)
53
+ - **Mission:** Move each article-language pair through translation, checking and the linguist queue.
54
+ - **Inputs:** article list, glossary, UI-label rule
55
+ - **Denied (isolation):** it doesn't judge translation quality. It acts only on the fields each step returns
56
+ - **Output contract:** `{ article_id: string, lang: "de"|"fr"|"ja", status: "translated"|"checked"|"flagged"|"invalid"|"queued_for_linguist" }[]`
57
+ - **Tools/permissions:** read source articles, write to the linguist's review queue. **No publish permission**
58
+ - **On failure:** marks the item `invalid` with the reason and carries on with the rest
59
+ - **Model:** none needed: routing and merging are code
60
+
61
+ ### translator ×960
62
+ - **Mission:** Translate one article into one language using the glossary.
63
+ - **Inputs:** one source article, that language's glossary, the UI-label rule
64
+ - **Denied (isolation):** other articles' translations, so each job stays small. Consistency across articles comes from the glossary and the term check below
65
+ - **Output contract:** `{ article_id: string, lang: string, body: string, terms: [{ source: string, target: string, in_glossary: bool }], status: "ok"|"blocked", blocked_reason: string|null }`
66
+ - **Tools/permissions:** none beyond its inputs. No write access anywhere
67
+ - **On failure:** `status: "blocked"` with a reason. It never sends a partial translation as `ok`
68
+ - **Model:** mid-size: translating with a fixed glossary doesn't need the largest model
69
+
70
+ ### checker ×960
71
+ - **Mission:** Find every place the translation departs from the source, the glossary or the UI-label rule.
72
+ - **Inputs:** source article, translated `body`, glossary, UI-label rule
73
+ - **Denied (isolation):** the translator's `terms` list and status, so it checks the text instead of confirming the translator's claims about it
74
+ - **Output contract:** `{ article_id: string, lang: string, verdict: "pass"|"fail"|"blocked", findings: [{ type: "glossary"|"ui_label"|"meaning"|"omission", source_excerpt: string, target_excerpt: string, expected: string }] }`
75
+ - **Tools/permissions:** read-only
76
+ - **On failure:** `verdict: "blocked"`, handled exactly like `fail`. It is never counted as a pass
77
+ - **Model:** mid-size: a different agent from the translator, which is what makes the check independent
78
+
79
+ ## Topology
80
+
81
+ **Shape:** a pipeline per article (translate, then check), **because** no article's
82
+ translation depends on another's. **One barrier per language**, **because** the pilot's 4
83
+ inconsistent terms only show up across articles. Code groups every translator's `terms` by
84
+ source term and flags any term with more than one translation. That check needs all of a
85
+ language's articles, and nothing else in the plan does.
86
+ **Depth:** one level. The Language Leads would only pass articles along, and the orchestrator
87
+ already does that.
88
+
89
+ ```mermaid
90
+ flowchart LR
91
+ O[orchestrator] --> T["translator ×960"]
92
+ T --> C["checker ×960"]
93
+ T -. terms .-> X{"term check per language"}
94
+ C --> X
95
+ X --> L["linguist approval"]
96
+ L -->|approved| P[help center]
97
+ T -->|invalid output| F[failure list]
98
+ C -->|invalid output| F
99
+ ```
100
+
101
+ ## Aggregation & verification
102
+
103
+ - **Aggregation:** code. The term check groups by `(lang, source term)`. The report for the
104
+ docs lead is counts of `findings` by type and language, plus the list of `fail` and
105
+ `invalid` articles. Output that doesn't match its schema goes to the failure list and never
106
+ into the report.
107
+ - **Verification:** the checker's job is to find faults, and it never sees the translator's
108
+ own assessment. A failed article goes to the linguist with its findings attached.
109
+ - **Vote rule:** a single checker. An article is flagged if it has any finding. The linguist's
110
+ approval gates publishing, as it does today.
111
+
112
+ ## Budget line
113
+
114
+ No spending cap was given, so none is set here. Model sizes are on each card.
115
+
116
+ **Left out of the plan:** the Coordinator publishing everything. Every article still goes
117
+ through linguist approval before it goes live. The pilot shows why: the self-check missed all
118
+ 11 UI-label errors.
119
+
120
+ **To confirm**
121
+ - The spending cap for a full run.
122
+ - Whether source articles mark UI labels in a way code can detect. If they do, that check
123
+ can move from the checker to code.
124
+ - Who reworks a failed article: the linguist, or a second translation pass with the findings.
125
+
126
+ Next step: design the run around this, meaning retries, the stop conditions and what happens
127
+ when the cap is hit, before the first full batch.
128
+
129
+ ## Why this is the oracle
130
+
131
+ - **The verifier is separate and adversarial, never the generator.** The plan's step 2 has
132
+ the translator check itself, and the pilot's 40/40 self-pass is the evidence that this
133
+ fails. The checker is denied the translator's own claims, so it can't just agree with them.
134
+ - **Aggregation is code, and a barrier needs a named dependency across items.** The summary
135
+ agent becomes a group-by over typed `findings`. The plan's "when all 960 are done"
136
+ synchronisation shrinks to one barrier per language, earned by cross-article term
137
+ consistency, the 4 inconsistent terms no per-article check can see.
138
+ - **Fan out only for a named reason, keep depth to one level, and take budget numbers only
139
+ from the user.** "More agents run faster" is rejected as a reason. The Language Lead layer
140
+ is cut. No cap is invented, and auto-publishing is removed and named as removed.
141
+
142
+ A cheaper pass approves the seven-agent plan and tunes the prompts. It keeps self-review,
143
+ lets a summary agent paraphrase 960 translations, and leaves the Coordinator publishing. That
144
+ ships the pilot's UI-label errors across three languages straight to the live help center. The
145
+ opposite cheap answer, "use one agent", fails too: 480,000 words don't fit one context, and it
146
+ leaves the role conflict in place.
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "subagent-design",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "description": "Decompose a task into an orchestrator and subagents — one-mission role cards with typed output contracts, a topology (pipeline / parallel / judge panel) chosen with a reason, context-isolation rationale, and a verification stage that is never a generator. Use when the user wants to fan work out across multiple agents, design a multi-agent workflow or orchestration, or asks \"should this be one agent or several\".",
5
5
  "entrypoint": "SKILL.md",
6
6
  "deps": { "npm": [], "pip": [] },
7
7
  "env": { "required": [], "optional": [] },
8
8
  "related": ["agent-budget", "agent-loop-design", "council-review", "devils-advocate", "feature-implement-loop"],
9
9
  "tags": ["subagents", "orchestration", "multi-agent", "fan-out", "agentic", "role-cards"],
10
- "model": { "tier": "standard", "rationale": "Design synthesis against a fixed topology catalog and role-card template. The fan-out-or-refuse judgment is bounded by the three-reasons rule; escalate via ambiguous-input on genuinely novel workflow shapes." }
10
+ "model": { "tier": "heavy", "rationale": "Measured (RFC-0073): on its held-out evals the heavy tier met 100% and 100% of checks in two tier trials, against standard's 86% and light's 85% and 86%, more than 5 points ahead both times; moved once its examples/ oracle shipped (RFC-0074)." }
11
11
  }
@@ -75,6 +75,10 @@ Produce:
75
75
  1. **Annotated structure** — show the messages array with `cache_control` placements marked and commented
76
76
  2. **Cache strategy summary** — one paragraph: what is cached, why, expected hit rate, and the dominant cost driver before and after
77
77
 
78
+ ## Useful references in this skill
79
+
80
+ - [`examples/support-bot-block-order.md`](examples/support-bot-block-order.md) — worked example: a breakpoint under the minimum size and a per-customer block splitting a shared cache, fixed by reordering
81
+
78
82
  ## Anti-patterns
79
83
 
80
84
  - **Caching dynamic content.** If a user query, timestamp, or per-request database result is inside a cached block, every request is a cache miss — the structure defeats itself. Identify and flag before recommending.
@@ -0,0 +1,120 @@
1
+ # Worked example: a cache breakpoint that "didn't move the bill"
2
+
3
+ Input → output oracle for `prompt-caching-advisor`. Shows a breakpoint that never caches
4
+ because its prefix is under the minimum size, a per-customer block that splits a shared cache
5
+ in two, and the fix as a reordering, not an extra marker.
6
+
7
+ ## Input
8
+
9
+ > Our support bot runs on Haiku. We added `cache_control` to the instructions block because
10
+ > that's the static part, and the input bill didn't move. Should we add another one on the
11
+ > catalogue? Numbers: about 150,000 requests a day, conversations average 6 turns with a turn
12
+ > every 1–2 minutes, busy all day and quiet overnight. The catalogue is rebuilt once a night
13
+ > at 02:00.
14
+
15
+ ```python
16
+ def build_request(customer, session, question):
17
+ system = [
18
+ {"type": "text", "text": INSTRUCTIONS, # ~3,200 characters
19
+ "cache_control": {"type": "ephemeral"}},
20
+ {"type": "text", "text": render_profile(customer)}, # ~1,400 characters, per customer
21
+ {"type": "text", "text": load_catalogue()}, # ~62,000 characters
22
+ ]
23
+ messages = session.history + [{"role": "user", "content": question}]
24
+ return {"model": MODEL, "max_tokens": 800, "system": system, "messages": messages}
25
+ ```
26
+
27
+ ## Output
28
+
29
+ **Don't add a second marker yet. Fix the order first.** Two things are wrong, and a marker on
30
+ the catalogue in its current position would only fix half of it.
31
+
32
+ 1. **Your current breakpoint caches nothing.** A breakpoint caches the whole prefix up to and
33
+ including its block. Here that is the instructions alone: 3,200 characters, or about 800
34
+ tokens. Haiku's minimum is about 1,024 tokens, so the API processes it as normal input,
35
+ with no error and no saving. That is why the bill didn't move.
36
+ 2. **The customer profile sits in front of the catalogue.** The profile differs for every
37
+ customer, so any breakpoint after it makes a separate cache entry per customer. A marker
38
+ on the catalogue where it sits now would cache instructions + profile + catalogue together.
39
+ It would hit only on later turns of the same conversation, and every customer's first turn
40
+ would pay to write 16,600 tokens to the cache.
41
+
42
+ Move the catalogue above the profile, put one breakpoint at the end of the catalogue, and put
43
+ a second, moving breakpoint on the latest user turn.
44
+
45
+ ### Annotated structure
46
+
47
+ | Block | Size (~4 chars/token) | Class | Cached by |
48
+ |---|---|---|---|
49
+ | Instructions | 3,200 ch ≈ 800 tok | Static | BP1 (as part of its prefix) |
50
+ | Catalogue | 62,000 ch ≈ 15,500 tok | Static until the 02:00 rebuild | BP1 |
51
+ | Customer profile | 1,400 ch ≈ 350 tok | Quasi-static (per conversation) | BP2 |
52
+ | Earlier turns | grows each turn | Fixed once sent | BP2 |
53
+ | Latest question | varies | Dynamic | written by BP2 for the *next* turn to read |
54
+
55
+ ```python
56
+ def build_request(customer, session, question):
57
+ system = [
58
+ # Static. ~800 tokens: under Haiku's ~1,024 minimum, so no marker of its own.
59
+ # It is still cached, because BP1's prefix includes it.
60
+ {"type": "text", "text": INSTRUCTIONS},
61
+ # Static until the 02:00 rebuild. ~15,500 tokens.
62
+ {"type": "text", "text": load_catalogue(),
63
+ "cache_control": {"type": "ephemeral"}}, # BP1: instructions + catalogue, ~16,300 tokens,
64
+ # identical for every customer
65
+ # Quasi-static: the same for a whole conversation, different per customer.
66
+ # It sits after BP1 so it can't split the shared entry.
67
+ {"type": "text", "text": render_profile(customer)},
68
+ ]
69
+ latest = {
70
+ "role": "user",
71
+ "content": [{"type": "text", "text": question,
72
+ "cache_control": {"type": "ephemeral"}}], # BP2: profile + all earlier turns + this one
73
+ }
74
+ # session.history stores turns WITHOUT cache_control. If the marker is saved into history,
75
+ # markers pile up turn after turn, and from turn 4 the request carries more than the API's
76
+ # limit of four breakpoints.
77
+ messages = session.history + [latest]
78
+ return {"model": MODEL, "max_tokens": 800, "system": system, "messages": messages}
79
+ ```
80
+
81
+ BP2 is not caching dynamic content. It writes the conversation so far, so that the next turn,
82
+ where this question is now fixed history, can read it. Nothing that changes between requests
83
+ sits in front of BP1, and that is the prefix every customer shares.
84
+
85
+ Two breakpoints, one per logical boundary: content shared by everyone, then content shared
86
+ within a conversation. A third marker on the instructions would add nothing, because they are
87
+ under the minimum and already inside BP1's prefix.
88
+
89
+ ### Cache strategy summary
90
+
91
+ BP1 caches the instructions and catalogue, about 16,300 tokens, as one entry shared by every
92
+ customer. At about 150,000 requests a day it stays warm all through the busy hours, so its hit
93
+ rate is near 100%. It misses on the first request after the 02:00 rebuild and after any
94
+ overnight gap longer than the cache's five-minute lifetime. BP2 caches each conversation's
95
+ profile and earlier turns. With turns 1–2 minutes apart, turns 2–6 read it, about 5 of 6
96
+ requests. Before the change, about 16,600 tokens of instructions, profile and catalogue were
97
+ processed at full price on every request, roughly 2.5 billion input tokens a day. After it,
98
+ almost all of that is a cache read, billed at about a tenth of the normal input price. The
99
+ largest costs left are output tokens and each turn's new question. This answer gives no dollar
100
+ figure: the request didn't include prices, and the hit rates above are estimates until the
101
+ response `usage` fields (`cache_read_input_tokens`, `cache_creation_input_tokens`) confirm
102
+ them.
103
+
104
+ ## Why this is the oracle
105
+
106
+ - **The last block caches everything before it.** The user's mental model, "mark the static
107
+ block", is per block. A breakpoint marks a prefix. That one rule explains why the
108
+ instructions marker did nothing (its prefix is 800 tokens) and why a catalogue marker in
109
+ the current order would be keyed per customer (its prefix includes the profile).
110
+ - **Check the size before recommending a cache.** The fix removes the user's existing marker,
111
+ because it is under Haiku's minimum. The instructions still get cached, inside BP1.
112
+ - **Never let dynamic or per-user content split a shared prefix.** The profile is not dynamic
113
+ per request, but it is per customer. Moving it below BP1 turns a per-conversation cache into
114
+ a shared one. The comment on `session.history` heads off the four-breakpoint error that a
115
+ moving breakpoint causes when it is stored.
116
+
117
+ A cheaper pass answers the question as asked: "Yes, add `cache_control` to the catalogue
118
+ block." That gives each conversation a cache entry of its own, a premium cache write on every
119
+ first turn, and a bill that drops far less than expected. The team blames caching, and the
120
+ dead instructions marker stays in the code.
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "prompt-caching-advisor",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "Analyzes a prompt or conversation structure and identifies the highest-value cache breakpoints — where to insert cache_control checkpoints to minimize repeated token processing. Use when designing or optimizing a Claude API integration that uses prompt caching, when input costs are dominated by a static system prompt or few-shot block, or when a multi-turn conversation reprocesses the same context on every turn.",
5
5
  "entrypoint": "SKILL.md",
6
6
  "deps": { "npm": [], "pip": [] },
7
7
  "env": { "required": [], "optional": [] },
8
8
  "related": ["token-budget-estimator"],
9
9
  "tags": ["claude-api", "caching", "cost", "optimization", "prompt-engineering"],
10
- "model": { "tier": "standard", "rationale": "Structural analysis of a prompt with threshold arithmetic and pattern matching — above light (needs API-specific judgment), no need for heavy." }
10
+ "model": { "tier": "heavy", "rationale": "Measured (RFC-0073): on its held-out evals the heavy tier met 90% and 90% of checks in two tier trials, against standard's 63% and light's 83% and 73%, more than 5 points ahead both times; moved once its examples/ oracle shipped (RFC-0074)." }
11
11
  }
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "tool-use-schema-writer",
3
- "version": "1.0.2",
3
+ "version": "1.0.3",
4
4
  "description": "Takes a function description (name, purpose, parameters) and writes a valid Anthropic tool_use JSON schema, with type annotations, required fields, and a description for each parameter. Adds a Python client.messages.create(tools=[...]) snippet only when asked. Use when wiring a Claude agent to a function or API, designing tools for a multi-agent system, or documenting an existing tool for agent consumption.",
5
5
  "entrypoint": "SKILL.md",
6
6
  "deps": { "npm": [], "pip": [] },
7
7
  "env": { "required": [], "optional": [] },
8
8
  "related": ["agent-loop-design", "subagent-design"],
9
9
  "tags": ["claude-api", "tool-use", "json-schema", "sdk", "agents"],
10
- "model": { "tier": "standard", "rationale": "Schema generation from a natural-language description — structured output task that fits standard tier." }
10
+ "model": { "tier": "light", "rationale": "Measured (RFC-0073): on its held-out evals the light tier met 94% and 94% of checks in two tier trials, against standard's 96% and heavy's 97% and 97%, within 5 points of the best both times." }
11
11
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "application-form-answers",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Prepare the answers to a job application form from the person's resume: each question in the form's own order with either a drafted answer and the resume line behind it, or a note that it is theirs alone to answer (right to work, sponsorship, salary, declarations, monitoring questions), plus the questions that could screen the application out and the ones their resume already bears on. Works for any occupation. Use when the user says \"help me fill in this application form\", \"answer these application questions\", \"what do I put for these screening questions\", or pastes the questions from an online job application.",
5
5
  "entrypoint": "SKILL.md",
6
6
  "deps": {
@@ -25,7 +25,7 @@
25
25
  "job-search"
26
26
  ],
27
27
  "model": {
28
- "tier": "standard",
29
- "rationale": "Sorts questions into three kinds and drafts short answers from the resume. The hard rule is mechanical: a declaration is never answered for the person."
28
+ "tier": "light",
29
+ "rationale": "Measured (RFC-0073): on its held-out evals the light tier met 88% and 94% of checks in two tier trials, against standard's 88% and heavy's 81% and 81%, within 5 points of the best both times."
30
30
  }
31
31
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cover-letter",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "Write a cover letter for one job from the person's resume and the posting: under a page, opening on the strongest evidence for the role, two or three specifics matched to what the employer asked for, and no enthusiasm, company facts or experience the person didn't supply. A script lists every figure and name in the letter that their own material doesn't contain. Works for any occupation and level. Use when the user says \"write a cover letter for this job\", \"draft a covering letter\", \"write the cover note for my application\", or needs an application email to go with a resume.",
5
5
  "entrypoint": "SKILL.md",
6
6
  "deps": {
@@ -31,7 +31,7 @@
31
31
  "notes": "Reads the draft, source and posting files it is given. Writes nothing."
32
32
  },
33
33
  "model": {
34
- "tier": "standard",
35
- "rationale": "A short letter from supplied facts, with a script listing every figure and name the person's material doesn't contain."
34
+ "tier": "light",
35
+ "rationale": "Measured (RFC-0073): on its held-out evals the light tier met 100% and 100% of checks in two tier trials, against standard's 100% and heavy's 100% and 100%, within 5 points of the best both times."
36
36
  }
37
37
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "resume-tailor",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "Tailor a resume or CV to one job posting without adding anything that isn't true: reorder, cut and reword what the person has already done so the posting's needs are answered first, keep every employer, title, date and number as it was, and report what the posting asks for that the resume cannot claim. A script lists every figure and name in the draft that the original doesn't contain. Works for any occupation and level. Use when the user says \"tailor my resume for this job\", \"adapt my CV to this posting\", \"rewrite my resume for this role\", or pastes a job description next to their resume.",
5
5
  "entrypoint": "SKILL.md",
6
6
  "deps": {
@@ -31,7 +31,7 @@
31
31
  "notes": "Reads the draft, source and posting files it is given. Writes nothing."
32
32
  },
33
33
  "model": {
34
- "tier": "standard",
35
- "rationale": "Reorders and rewords existing content against a posting, with a script listing every figure and name the original doesn't contain."
34
+ "tier": "light",
35
+ "rationale": "Measured (RFC-0073): on its held-out evals the light tier met 100% and 100% of checks in two tier trials, against standard's 100% and heavy's 100% and 100%, within 5 points of the best both times."
36
36
  }
37
37
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "md-to-docx",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "description": "Turn a Markdown draft into a Word document (.docx) that someone outside engineering can open, edit and track changes in, with real Heading styles so the navigation pane and table of contents work, live hyperlinks, numbered and bulleted lists, tables, code blocks, embedded images and optional brand fonts and colours, and a warning for everything that could not be carried over. Use when the user wants a Markdown file as a Word doc, says \"send this as a .docx\", \"convert to Word\", \"legal needs it in Word\", or \"make this editable for the client\".",
5
5
  "entrypoint": "SKILL.md",
6
6
  "deps": {
@@ -39,7 +39,7 @@
39
39
  ],
40
40
  "permissions": {"network": [], "commands": [], "files": "named-paths"},
41
41
  "model": {
42
- "tier": "standard",
43
- "rationale": "A stdlib script does the conversion; the model restructures the Markdown so it converts cleanly (heading levels, footnotes, HTML), reads each warning and explains the losses. Bounded editing, no open-ended generation."
42
+ "tier": "heavy",
43
+ "rationale": "Measured (RFC-0073): on its held-out evals the heavy tier met 79% and 93% of checks in two tier trials, against standard's 64% and light's 71% and 79%, more than 5 points ahead both times."
44
44
  }
45
45
  }
@@ -20,6 +20,34 @@
20
20
  "Explains that re-drafting from the same incomplete brief reproduces the same gaps",
21
21
  "Does not run output-hygiene on a draft that is about to be rewritten"
22
22
  ]
23
+ },
24
+ {
25
+ "id": "ship-a-draft-adr-from-thread",
26
+ "held_out": "2026-11-04",
27
+ "prompt": "Can you turn this Slack thread (slack-thread.txt) into a proper ADR for moving the nightly statements job? It goes to the architecture review group on Thursday and I want it finished and ready to send, not a first draft I have to fix up.",
28
+ "assertions": [
29
+ "Produces a structured intake brief from the thread before drafting, and the ADR is written from that brief",
30
+ "The month-end queue capacity question Marta raised is carried into the brief and the ADR as open or a risk, not as settled",
31
+ "The ADR records option B as the decision, option A as rejected because it does not fix the 3h40m runtime, and option C as rejected for lack of approved budget, and keeps the unchanged statement file format and idempotent retries as requirements",
32
+ "After drafting, the ADR is critiqued with findings tied to sections and a G5 verdict in the critique's own terms (such as SHIP IT, SHIP WITH CHANGES or MAJOR REWRITE)",
33
+ "Any changes the critique calls for are made before the polish pass, and the polish pass runs last, on the revised ADR",
34
+ "The final ADR carries none of the thread's machine residue: no 'Generated with ExampleAI' or 'Summarised by ExampleBot' line and no 'Hope this helps' closer",
35
+ "Does not invent figures, dates or owners not in the thread, such as a queue capacity number, a migration date or a deadline for Marta's check"
36
+ ]
37
+ },
38
+ {
39
+ "id": "ship-a-draft-announcement-unsettled",
40
+ "held_out": "2026-11-04",
41
+ "prompt": "Need the customer announcement email for scheduled exports, to go to Team plan admins. Everything's in the ticket (ticket-PRD-412.md). Marketing want final copy today so please give me something finished I can just hand over.",
42
+ "assertions": [
43
+ "The intake brief, written before any drafting, flags the launch date, the price, and whether SFTP delivery is in as unsettled in the ticket",
44
+ "The draft leaves those three as clearly marked placeholders or open items instead of picking a value",
45
+ "The critique gate does not return SHIP IT; its verdict holds the email back (such as SHIP WITH CHANGES or MAJOR REWRITE) and names the unsettled date, price and scope as the reason",
46
+ "Tells the user plainly that the email is not ready to go to customers, and who in the ticket has to settle each item: Hannah and Oskar on SFTP scope and date, finance (Gwen, after the 10 November pricing committee) on price",
47
+ "If the polish pass runs, the placeholders survive it, and the reply does not present the email as final copy",
48
+ "Does not stop to ask questions; the brief, draft and critique are delivered in this run",
49
+ "Does not invent a launch date, price or feature scope the ticket has not settled, such as stating 12 November, 26 November or $15 a month as fact"
50
+ ]
23
51
  }
24
52
  ]
25
53
  }
@@ -0,0 +1,47 @@
1
+ #reporting-eng — thread started by Priya Raman, Mon 2 Nov 2026
2
+
3
+ Priya Raman 10:12
4
+ ok we need to stop pretending the nightly statements job is fine. it failed again on the 22nd. that's twice in October (9th and 22nd), both times because reports-01 got rebooted for patching mid-run and the cron job just died. ~1,900 customers didn't get their statement until we reran it by hand at 10am
5
+
6
+ Tom Lindqvist 10:15
7
+ and it's getting slower. last night's run was 3h40m. the window is 01:00 to 06:00 UTC because the finance team pulls the statements at 06:30. at the rate customer count is growing we're going to blow the window sometime next year
8
+
9
+ Marta Szabo 10:21
10
+ options I can see:
11
+ A) keep cron on reports-01, add a second box with a lock so only one runs, plus retry on failure
12
+ B) move it onto the job queue we already run for the email service. one job per customer, workers pick them up, failed ones retry on their own
13
+ C) use the cloud provider's managed scheduler + functions
14
+
15
+ Tom Lindqvist 10:24
16
+ C is nice but there's no budget approved for it this year, I asked in September and got a no. not worth writing up as anything but rejected
17
+
18
+ Priya Raman 10:26
19
+ A fixes the reboot problem but not the runtime. it's still one process doing 3h40m of work
20
+
21
+ Tom Lindqvist 10:30
22
+ B gets us both. per-customer jobs means a reboot only kills the in-flight ones and they get retried, and we can add workers if we're getting close to the window
23
+
24
+ Marta Szabo 10:33
25
+ I'm fine with B in principle. my worry is month-end. the email service hammers the same queue on the 1st of every month (invoice emails). if statements and invoice emails land on the same night, does the queue keep up? I don't actually know its capacity
26
+
27
+ Priya Raman 10:35
28
+ can you find out?
29
+
30
+ Marta Szabo 10:36
31
+ I'll check with platform
32
+
33
+ Jonas Weber 10:52
34
+ dropping in the AI summary of the thread from the meeting notes bot, in case it helps:
35
+
36
+ > **Summary:** The team is evaluating three options for the nightly statements job. Option B (job queue) is the leading candidate because it addresses both reliability and runtime. Option C is rejected due to budget. Open question: queue capacity at month-end.
37
+ > Hope this helps! Let me know if you need anything else.
38
+ > — Summarised by ExampleBot · Generated with ExampleAI (notes.example.com)
39
+
40
+ Priya Raman 11:02
41
+ so B, pending Marta's capacity check. we also need to keep the output the same, finance's import breaks if the statement file format changes. I'll write it up as an ADR for architecture review
42
+
43
+ Tom Lindqvist 11:04
44
+ 👍 one more thing for the ADR: we'd need idempotent jobs, because a retried job must not send the same customer two statements
45
+
46
+ Priya Raman 11:05
47
+ yep
@@ -0,0 +1,44 @@
1
+ # PRD-412 — Launch: scheduled exports for Team plan
2
+
3
+ **Type:** Launch **Status:** In progress **Reporter:** Hannah Ito (Product) **Assignee:** Hannah Ito
4
+ **Labels:** team-plan, exports, launch-q4
5
+
6
+ ## Description
7
+
8
+ Team plan customers can set up a recurring export (daily, weekly or monthly) of their project data as CSV, delivered by email link or to an SFTP server they own. Replaces the manual "Export all" button that customers currently run by hand.
9
+
10
+ Top customer asks (from the feedback board):
11
+ - "We export every Monday for our finance team. Please let us schedule it." — 41 upvotes
12
+ - "Need this to land on our SFTP so our BI tool can pick it up" — 17 upvotes
13
+ - "Weekly CSV by email would be enough for us" — 12 upvotes
14
+
15
+ What's in:
16
+ - Daily / weekly / monthly schedules, choose the time of day
17
+ - CSV format, same columns as today's manual export
18
+ - Delivery by emailed download link (link valid 7 days)
19
+ - Delivery to customer SFTP — see comments
20
+
21
+ Help-centre article: help.example.com/scheduled-exports (draft, not published)
22
+
23
+ ## Comments
24
+
25
+ **Hannah Ito (Product)** — 20 Oct
26
+ Target launch Nov 12. Marketing will send the announcement email to Team plan admins on launch day.
27
+
28
+ **Oskar Brenner (Eng lead)** — 22 Oct
29
+ Nov 12 isn't realistic with SFTP in. Email delivery is done and in QA. SFTP destination still needs key management and the retry logic. If we cut SFTP from v1, earliest is Nov 26. With SFTP, I can't give a date yet.
30
+
31
+ **Hannah Ito (Product)** — 23 Oct
32
+ SFTP is the thing the bigger customers asked for, I don't want to launch without it. Let's discuss.
33
+
34
+ **Oskar Brenner (Eng lead)** — 23 Oct
35
+ Fine, but then the date moves. Need a decision from you.
36
+
37
+ **Hannah Ito (Product)** — 27 Oct
38
+ Pricing proposal: add-on at $15/month per workspace. Sent to finance.
39
+
40
+ **Gwen Adeyemi (Finance)** — 29 Oct
41
+ Not approved yet. We're also looking at including it in Team at no extra charge as a retention play. Will have an answer after the pricing committee on Nov 10.
42
+
43
+ **Hannah Ito (Product)** — 3 Nov
44
+ Marketing is asking for final copy for the announcement. Can someone draft it?
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "output-hygiene",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "description": "Review agent-written text before it ships and strip the machine artifacts it carries — invisible Unicode, non-breaking spaces, homoglyphs, harness-added provenance trailers, trailing chat closers — then flag the prose tells a script cannot catch, each quoted with a rewrite. Use when the user wants to clean up AI-generated output, asks to remove a Claude watermark, attribution footer, or \"Generated with\" line, wants a draft that does not read as machine-written, or needs a pre-ship hygiene pass on a document, commit message, or pasted chat answer.",
5
5
  "entrypoint": "SKILL.md",
6
6
  "deps": {
@@ -28,7 +28,7 @@
28
28
  ],
29
29
  "permissions": {"network": [], "commands": [], "files": "named-paths", "notes": "Writes only with --fix, which rewrites the files it was given, in place."},
30
30
  "model": {
31
- "tier": "standard",
32
- "rationale": "The mechanical pass is a deterministic script; the model does bounded rewriting of flagged prose against a fixed tell list, not adversarial review."
31
+ "tier": "light",
32
+ "rationale": "Measured (RFC-0073): on its held-out evals the light tier met 100% and 100% of checks in two tier trials, against standard's 92% and heavy's 100% and 92%, within 5 points of the best both times."
33
33
  }
34
34
  }
@@ -23,6 +23,36 @@
23
23
  "Reviews the changed query again at G9 before returning to sign-off",
24
24
  "Counts the return under the cap of 3"
25
25
  ]
26
+ },
27
+ {
28
+ "id": "measure-retention-dashboard",
29
+ "held_out": "2026-11-04",
30
+ "prompt": "we need a dashboard for the Monday win-back meeting. Anouk's request is in request.md, the queries we use now are in sql/ and the schema's in schema.md. Can you make sure these numbers are right before Sana's team builds it? Postgres.",
31
+ "assertions": [
32
+ "Runs the stages in order: the dashboard spec first, then the SQL review, then a sign-off request to the metric owners. Fixes from the review go back into the metric definitions in the spec",
33
+ "The spec says which decision the dashboard serves (which plan cohorts get this week's win-back offer), gives one chart for each of Anouk's four questions in her order, and leaves page views and app downloads off, saying why",
34
+ "Catches that net_revenue_by_plan.sql inflates revenue: joining order_items repeats each order total once per line item, and the refunds join repeats it again for orders with more than one refund",
35
+ "Catches that NOT IN (SELECT subscriber_id FROM subs.test_accounts) returns no rows because test_accounts.subscriber_id can be NULL. Gives a NOT EXISTS (or equivalent) fix",
36
+ "Catches the integer division in weekly_churn_rate.sql (cancelled / active_start), which rounds churn down to 0 for nearly every week. Gives corrected SQL",
37
+ "Flags that date_trunc('week', ...) on UTC timestamps does not match the Monday-to-Sunday UK-time week Anouk asked for, and gives a fix that sets the time zone to Europe/London",
38
+ "Asks the named owners to sign off each metric definition (Anouk for churn and active subscribers, Callum Reyes for net revenue) and does not record any definition as signed off",
39
+ "Does not invent figures, targets, alert thresholds or metric owners that are not in the request"
40
+ ]
41
+ },
42
+ {
43
+ "id": "measure-active-customers-dispute",
44
+ "held_out": "2026-11-04",
45
+ "prompt": "The exec dashboard gets built next week and it's got one 'Active customers' tile. Finance says 41,200 for October and Growth says 58,900, and the thread in thread.md is getting heated. Both queries are in sql/, schema notes in schema.md. Just pick whichever one is right and write the spec so BI can start Monday please.",
46
+ "assertions": [
47
+ "Runs the spec and the SQL review, then stops at sign-off. It does not hand BI a finished spec with the Active customers definition marked as agreed",
48
+ "Separates the difference in definition (Finance counts customers with a successful payment in October; Growth counts non-trial customers with a login) from the defects in the SQL",
49
+ "Catches that growth_active_customers.sql uses COUNT(*) over the users join, so it counts staff seats rather than customers, and gives a COUNT(DISTINCT c.customer_id) fix",
50
+ "Catches that last_login_at is overwritten on every login, so a customer whose users logged in during October and again since is dropped. Points to app.login_events instead",
51
+ "Catches that last_login_at < '2026-10-31' leaves out 31 October",
52
+ "Does not settle the definition on its own authority. It may recommend one, but presents the choice for a named person to sign off, and points out that the thread names no metric owner and that 'happy for analytics to call it' is not a sign-off",
53
+ "Reports that the sign-off gate has not passed, so the Active customers tile is not ready to build, and says who needs to decide what",
54
+ "Does not invent a metric owner, a reconciled Active customers figure, or a split of the 17,700 gap by cause, none of which are in the input"
55
+ ]
26
56
  }
27
57
  ]
28
58
  }