skilldrop-cli 0.16.44 → 0.16.46

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 (255) hide show
  1. package/catalogue.json +1 -0
  2. package/model-routing.json +1 -0
  3. package/package.json +1 -1
  4. package/packs/converters/skills/md-to-html/manifest.json +1 -1
  5. package/packs/converters/skills/md-to-html/scripts/md_to_html.py +3 -0
  6. package/packs/design/skills/brand-kit/manifest.json +1 -1
  7. package/packs/design/skills/brand-kit/scripts/check_brand.py +11 -1
  8. package/packs/dev-team/skills/behaviour-equivalence/manifest.json +1 -1
  9. package/packs/dev-team/skills/behaviour-equivalence/scripts/golden_diff.py +11 -1
  10. package/packs/frontend/skills/bundle-size-review/manifest.json +1 -1
  11. package/packs/frontend/skills/bundle-size-review/scripts/bundle_stats.py +13 -1
  12. package/packs/frontend/skills/web-performance-review/manifest.json +1 -1
  13. package/packs/frontend/skills/web-performance-review/scripts/lh_summary.py +12 -2
  14. package/packs/grc/skills/risk-register/manifest.json +1 -1
  15. package/packs/grc/skills/risk-register/scripts/check_register.py +9 -1
  16. package/packs/infra-as-code/skills/terraform-plan-review/manifest.json +1 -1
  17. package/packs/infra-as-code/skills/terraform-plan-review/scripts/plan_summary.py +12 -2
  18. package/packs/product-manager/skills/feedback-synthesis/manifest.json +1 -1
  19. package/packs/product-manager/skills/feedback-synthesis/scripts/theme_counts.py +6 -1
  20. package/packs/research/skills/hypothesis-comparison/manifest.json +1 -1
  21. package/packs/research/skills/hypothesis-comparison/scripts/ach_matrix.py +10 -2
  22. package/packs/solution-architect/skills/context-graph/SKILL.md +119 -0
  23. package/packs/solution-architect/skills/context-graph/evals/eval_queries.json +11 -0
  24. package/packs/solution-architect/skills/context-graph/evals/evals.json +225 -0
  25. package/packs/solution-architect/skills/context-graph/evals/files/1/docs/adr/0001-record-architecture-decisions.md +19 -0
  26. package/packs/solution-architect/skills/context-graph/evals/files/1/docs/adr/0002-postgres-as-primary-database.md +16 -0
  27. package/packs/solution-architect/skills/context-graph/evals/files/1/docs/adr/0003-use-redis-for-session-storage.md +24 -0
  28. package/packs/solution-architect/skills/context-graph/evals/files/1/docs/adr/0004-structured-json-logging.md +15 -0
  29. package/packs/solution-architect/skills/context-graph/evals/files/1/docs/adr/0005-session-lifetime-eight-hours.md +22 -0
  30. package/packs/solution-architect/skills/context-graph/evals/files/1/docs/adr/0006-move-sessions-to-postgres.md +16 -0
  31. package/packs/solution-architect/skills/context-graph/evals/files/1/docs/adr/0007-stateless-signed-session-cookies.md +35 -0
  32. package/packs/solution-architect/skills/context-graph/evals/files/1/services/api/__init__.py +0 -0
  33. package/packs/solution-architect/skills/context-graph/evals/files/1/services/api/db.py +0 -0
  34. package/packs/solution-architect/skills/context-graph/evals/files/1/services/api/logging_setup.py +0 -0
  35. package/packs/solution-architect/skills/context-graph/evals/files/1/services/auth/__init__.py +0 -0
  36. package/packs/solution-architect/skills/context-graph/evals/files/1/services/auth/cookie_sessions.py +7 -0
  37. package/packs/solution-architect/skills/context-graph/evals/files/1/services/auth/redis_sessions.py +7 -0
  38. package/packs/solution-architect/skills/context-graph/evals/files/10/README.md +8 -0
  39. package/packs/solution-architect/skills/context-graph/evals/files/10/docs/adr/0001-record-architecture-decisions.md +10 -0
  40. package/packs/solution-architect/skills/context-graph/evals/files/10/docs/adr/0004-service-auth-hmac-shared-keys.md +21 -0
  41. package/packs/solution-architect/skills/context-graph/evals/files/10/docs/adr/0007-secrets-from-vault-agent.md +19 -0
  42. package/packs/solution-architect/skills/context-graph/evals/files/10/docs/adr/0011-service-auth-mtls.md +23 -0
  43. package/packs/solution-architect/skills/context-graph/evals/files/10/services/billing-api/app.py +16 -0
  44. package/packs/solution-architect/skills/context-graph/evals/files/10/services/billing-api/auth/__init__.py +4 -0
  45. package/packs/solution-architect/skills/context-graph/evals/files/10/services/billing-api/auth/hmac_verify.py +31 -0
  46. package/packs/solution-architect/skills/context-graph/evals/files/10/services/billing-api/config/settings.py +14 -0
  47. package/packs/solution-architect/skills/context-graph/evals/files/10/services/ledger-worker/auth/__init__.py +4 -0
  48. package/packs/solution-architect/skills/context-graph/evals/files/10/services/ledger-worker/auth/mtls.py +18 -0
  49. package/packs/solution-architect/skills/context-graph/evals/files/11/README.md +4 -0
  50. package/packs/solution-architect/skills/context-graph/evals/files/11/app/models/base.py +3 -0
  51. package/packs/solution-architect/skills/context-graph/evals/files/11/app/models/line_item.py +14 -0
  52. package/packs/solution-architect/skills/context-graph/evals/files/11/app/models/order.py +24 -0
  53. package/packs/solution-architect/skills/context-graph/evals/files/11/eng/decisions/DR-003-money-as-minor-units.md +10 -0
  54. package/packs/solution-architect/skills/context-graph/evals/files/11/eng/decisions/DR-005-orders-typed-columns-only.md +10 -0
  55. package/packs/solution-architect/skills/context-graph/evals/files/11/eng/decisions/DR-009-line-items-immutable.md +11 -0
  56. package/packs/solution-architect/skills/context-graph/evals/files/11/eng/decisions/DR-014-optional-order-attributes-json.md +16 -0
  57. package/packs/solution-architect/skills/context-graph/evals/files/11/eng/decisions/DR-016-order-attributes-table.md +11 -0
  58. package/packs/solution-architect/skills/context-graph/evals/files/11/migrations/0007_create_orders.sql +11 -0
  59. package/packs/solution-architect/skills/context-graph/evals/files/11/migrations/0012_add_order_attributes.sql +2 -0
  60. package/packs/solution-architect/skills/context-graph/evals/files/12/README.md +8 -0
  61. package/packs/solution-architect/skills/context-graph/evals/files/12/config/delivery.yaml +4 -0
  62. package/packs/solution-architect/skills/context-graph/evals/files/12/config/export.yaml +2 -0
  63. package/packs/solution-architect/skills/context-graph/evals/files/12/config/partners.yaml +10 -0
  64. package/packs/solution-architect/skills/context-graph/evals/files/12/docs/adr/0001-record-architecture-decisions.md +10 -0
  65. package/packs/solution-architect/skills/context-graph/evals/files/12/docs/adr/0002-postgres-delivery-log.md +15 -0
  66. package/packs/solution-architect/skills/context-graph/evals/files/12/docs/adr/0003-redis-idempotency-keys.md +15 -0
  67. package/packs/solution-architect/skills/context-graph/evals/files/12/docs/adr/0004-retry-policy.md +16 -0
  68. package/packs/solution-architect/skills/context-graph/evals/files/12/docs/adr/0005-hmac-signed-payloads.md +10 -0
  69. package/packs/solution-architect/skills/context-graph/evals/files/12/docs/adr/0006-postgres-idempotency.md +15 -0
  70. package/packs/solution-architect/skills/context-graph/evals/files/12/docs/adr/0007-sftp-batch-export.md +12 -0
  71. package/packs/solution-architect/skills/context-graph/evals/files/12/docs/adr/0008-partner-config-in-yaml.md +12 -0
  72. package/packs/solution-architect/skills/context-graph/evals/files/12/docs/adr/0009-partner-config-admin-api.md +13 -0
  73. package/packs/solution-architect/skills/context-graph/evals/files/12/migrations/003_idempotency_keys.sql +6 -0
  74. package/packs/solution-architect/skills/context-graph/evals/files/12/migrations/004_partners.sql +7 -0
  75. package/packs/solution-architect/skills/context-graph/evals/files/12/src/delivery/dispatcher.py +29 -0
  76. package/packs/solution-architect/skills/context-graph/evals/files/12/src/delivery/retry.py +15 -0
  77. package/packs/solution-architect/skills/context-graph/evals/files/12/src/delivery/signing.py +13 -0
  78. package/packs/solution-architect/skills/context-graph/evals/files/12/src/export/s3_export.py +24 -0
  79. package/packs/solution-architect/skills/context-graph/evals/files/12/src/idempotency/pg_store.py +11 -0
  80. package/packs/solution-architect/skills/context-graph/evals/files/12/src/idempotency/redis_store.py +8 -0
  81. package/packs/solution-architect/skills/context-graph/evals/files/12/src/partners/loader.py +15 -0
  82. package/packs/solution-architect/skills/context-graph/evals/files/12/src/store/delivery_log.py +18 -0
  83. package/packs/solution-architect/skills/context-graph/evals/files/13/README.md +3 -0
  84. package/packs/solution-architect/skills/context-graph/evals/files/13/config/cache.yaml +2 -0
  85. package/packs/solution-architect/skills/context-graph/evals/files/13/config/settings.yaml +2 -0
  86. package/packs/solution-architect/skills/context-graph/evals/files/13/docs/decisions/RFC-008-pricing-from-erp.md +10 -0
  87. package/packs/solution-architect/skills/context-graph/evals/files/13/docs/decisions/RFC-012-pricing-cache.md +23 -0
  88. package/packs/solution-architect/skills/context-graph/evals/files/13/docs/decisions/RFC-015-checkout-price-lock.md +15 -0
  89. package/packs/solution-architect/skills/context-graph/evals/files/13/docs/decisions/RFC-019-pricing-service.md +17 -0
  90. package/packs/solution-architect/skills/context-graph/evals/files/13/shopfront/catalog/views.py +8 -0
  91. package/packs/solution-architect/skills/context-graph/evals/files/13/shopfront/checkout/lock.py +14 -0
  92. package/packs/solution-architect/skills/context-graph/evals/files/13/shopfront/pricing/cache.py +22 -0
  93. package/packs/solution-architect/skills/context-graph/evals/files/13/shopfront/pricing/client.py +10 -0
  94. package/packs/solution-architect/skills/context-graph/evals/files/14/docs/rfcs/0001-idempotency-keys.md +33 -0
  95. package/packs/solution-architect/skills/context-graph/evals/files/14/docs/rfcs/0002-processor-retry-policy.md +26 -0
  96. package/packs/solution-architect/skills/context-graph/evals/files/14/docs/rfcs/0003-money-in-minor-units.md +31 -0
  97. package/packs/solution-architect/skills/context-graph/evals/files/14/docs/rfcs/0004-webhook-signing.md +27 -0
  98. package/packs/solution-architect/skills/context-graph/evals/files/14/docs/rfcs/0005-retry-with-jitter.md +30 -0
  99. package/packs/solution-architect/skills/context-graph/evals/files/14/docs/rfcs/0006-idempotency-at-gateway.md +28 -0
  100. package/packs/solution-architect/skills/context-graph/evals/files/14/pr-summary.md +27 -0
  101. package/packs/solution-architect/skills/context-graph/evals/files/14/src/payments/amount.ts +10 -0
  102. package/packs/solution-architect/skills/context-graph/evals/files/14/src/payments/charge.ts +28 -0
  103. package/packs/solution-architect/skills/context-graph/evals/files/14/src/payments/idempotency.ts +4 -0
  104. package/packs/solution-architect/skills/context-graph/evals/files/14/src/payments/retry.ts +21 -0
  105. package/packs/solution-architect/skills/context-graph/evals/files/14/src/webhooks/send.ts +9 -0
  106. package/packs/solution-architect/skills/context-graph/evals/files/14/src/webhooks/sign.ts +6 -0
  107. package/packs/solution-architect/skills/context-graph/evals/files/15/deploy/services.yaml +14 -0
  108. package/packs/solution-architect/skills/context-graph/evals/files/15/doc/adr/0001-record-architecture-decisions.md +20 -0
  109. package/packs/solution-architect/skills/context-graph/evals/files/15/doc/adr/0002-use-postgresql-for-shipment-data.md +23 -0
  110. package/packs/solution-architect/skills/context-graph/evals/files/15/doc/adr/0003-store-vehicle-telemetry-in-cassandra.md +22 -0
  111. package/packs/solution-architect/skills/context-graph/evals/files/15/doc/adr/0004-use-redis-for-session-storage.md +22 -0
  112. package/packs/solution-architect/skills/context-graph/evals/files/15/doc/adr/0005-use-react-for-dispatcher-dashboard.md +19 -0
  113. package/packs/solution-architect/skills/context-graph/evals/files/15/doc/adr/0006-archive-delivered-shipments-to-object-storage.md +23 -0
  114. package/packs/solution-architect/skills/context-graph/evals/files/15/doc/adr/0007-move-shipment-records-to-mongodb.md +23 -0
  115. package/packs/solution-architect/skills/context-graph/evals/files/15/doc/adr/0008-store-vehicle-telemetry-in-timescaledb.md +28 -0
  116. package/packs/solution-architect/skills/context-graph/evals/files/15/doc/adr/0009-stateless-sessions-with-signed-cookies.md +24 -0
  117. package/packs/solution-architect/skills/context-graph/evals/files/15/services/api/session.py +17 -0
  118. package/packs/solution-architect/skills/context-graph/evals/files/15/services/shipments/db.py +30 -0
  119. package/packs/solution-architect/skills/context-graph/evals/files/15/services/shipments/migrations.sql +3 -0
  120. package/packs/solution-architect/skills/context-graph/evals/files/15/services/telemetry/store.py +23 -0
  121. package/packs/solution-architect/skills/context-graph/evals/files/15/web/dashboard/App.jsx +3 -0
  122. package/packs/solution-architect/skills/context-graph/evals/files/2/docs/adr/0001-record-architecture-decisions.md +11 -0
  123. package/packs/solution-architect/skills/context-graph/evals/files/2/docs/adr/0002-rabbitmq-for-background-jobs.md +15 -0
  124. package/packs/solution-architect/skills/context-graph/evals/files/2/docs/adr/0003-thumbnail-service.md +20 -0
  125. package/packs/solution-architect/skills/context-graph/evals/files/2/docs/adr/0004-money-as-integer-minor-units.md +17 -0
  126. package/packs/solution-architect/skills/context-graph/evals/files/2/docs/adr/0005-worker-retries.md +12 -0
  127. package/packs/solution-architect/skills/context-graph/evals/files/2/docs/adr/0006-fx-amounts-with-four-decimals.md +18 -0
  128. package/packs/solution-architect/skills/context-graph/evals/files/2/docs/adr/0007-feature-flags.md +16 -0
  129. package/packs/solution-architect/skills/context-graph/evals/files/2/docs/adr/0008-retire-legacy-search.md +14 -0
  130. package/packs/solution-architect/skills/context-graph/evals/files/2/libs/money/__init__.py +0 -0
  131. package/packs/solution-architect/skills/context-graph/evals/files/2/libs/money/amount.py +9 -0
  132. package/packs/solution-architect/skills/context-graph/evals/files/2/services/checkout/flags.py +1 -0
  133. package/packs/solution-architect/skills/context-graph/evals/files/2/services/checkout/publish.py +0 -0
  134. package/packs/solution-architect/skills/context-graph/evals/files/2/services/media/resize.py +5 -0
  135. package/packs/solution-architect/skills/context-graph/evals/files/2/services/worker/consumer.py +0 -0
  136. package/packs/solution-architect/skills/context-graph/evals/files/3/docs/rfcs/0001-how-we-write-rfcs.md +9 -0
  137. package/packs/solution-architect/skills/context-graph/evals/files/3/docs/rfcs/0002-payments-service-boundary.md +16 -0
  138. package/packs/solution-architect/skills/context-graph/evals/files/3/docs/rfcs/0003-webhook-retries-fixed-interval.md +19 -0
  139. package/packs/solution-architect/skills/context-graph/evals/files/3/docs/rfcs/0004-webhook-idempotency-keys.md +16 -0
  140. package/packs/solution-architect/skills/context-graph/evals/files/3/docs/rfcs/0005-notification-retries.md +16 -0
  141. package/packs/solution-architect/skills/context-graph/evals/files/3/docs/rfcs/0006-webhook-retry-backoff.md +26 -0
  142. package/packs/solution-architect/skills/context-graph/evals/files/3/docs/rfcs/0007-webhooks-on-the-job-queue.md +12 -0
  143. package/packs/solution-architect/skills/context-graph/evals/files/3/services/notifications/__init__.py +0 -0
  144. package/packs/solution-architect/skills/context-graph/evals/files/3/services/notifications/retry.py +4 -0
  145. package/packs/solution-architect/skills/context-graph/evals/files/3/services/payments/__init__.py +0 -0
  146. package/packs/solution-architect/skills/context-graph/evals/files/3/services/payments/webhooks/__init__.py +0 -0
  147. package/packs/solution-architect/skills/context-graph/evals/files/3/services/payments/webhooks/retry.py +15 -0
  148. package/packs/solution-architect/skills/context-graph/evals/files/3/services/payments/webhooks/send.py +0 -0
  149. package/packs/solution-architect/skills/context-graph/evals/files/4/docs/adr/0001-record-architecture-decisions.md +19 -0
  150. package/packs/solution-architect/skills/context-graph/evals/files/4/docs/adr/0002-postgres-as-system-of-record.md +18 -0
  151. package/packs/solution-architect/skills/context-graph/evals/files/4/docs/adr/0004-redis-locks-for-scheduler.md +19 -0
  152. package/packs/solution-architect/skills/context-graph/evals/files/4/docs/adr/0007-remove-redis.md +27 -0
  153. package/packs/solution-architect/skills/context-graph/evals/files/4/docs/adr/0009-money-as-integer-minor-units.md +19 -0
  154. package/packs/solution-architect/skills/context-graph/evals/files/4/docs/onboarding.md +12 -0
  155. package/packs/solution-architect/skills/context-graph/evals/files/4/src/ledgerline/__init__.py +0 -0
  156. package/packs/solution-architect/skills/context-graph/evals/files/4/src/ledgerline/db/__init__.py +22 -0
  157. package/packs/solution-architect/skills/context-graph/evals/files/4/src/ledgerline/invoicing.py +9 -0
  158. package/packs/solution-architect/skills/context-graph/evals/files/4/src/ledgerline/money.py +10 -0
  159. package/packs/solution-architect/skills/context-graph/evals/files/4/src/ledgerline/scheduler/__init__.py +0 -0
  160. package/packs/solution-architect/skills/context-graph/evals/files/4/src/ledgerline/scheduler/lock.py +23 -0
  161. package/packs/solution-architect/skills/context-graph/evals/files/4/src/ledgerline/scheduler/runner.py +17 -0
  162. package/packs/solution-architect/skills/context-graph/evals/files/5/lib/types/index.ts +13 -0
  163. package/packs/solution-architect/skills/context-graph/evals/files/5/rfcs/0001-service-layout.md +20 -0
  164. package/packs/solution-architect/skills/context-graph/evals/files/5/rfcs/0003-ingest-retry-policy.md +26 -0
  165. package/packs/solution-architect/skills/context-graph/evals/files/5/rfcs/0006-shared-retry-library.md +24 -0
  166. package/packs/solution-architect/skills/context-graph/evals/files/5/rfcs/0008-outbound-http-client.md +22 -0
  167. package/packs/solution-architect/skills/context-graph/evals/files/5/services/export/client.ts +15 -0
  168. package/packs/solution-architect/skills/context-graph/evals/files/5/services/export/retry.ts +12 -0
  169. package/packs/solution-architect/skills/context-graph/evals/files/5/services/ingest/client.ts +14 -0
  170. package/packs/solution-architect/skills/context-graph/evals/files/5/services/ingest/retry.ts +20 -0
  171. package/packs/solution-architect/skills/context-graph/evals/files/6/README.md +4 -0
  172. package/packs/solution-architect/skills/context-graph/evals/files/6/docs/adr/0001-monorepo-layout.md +10 -0
  173. package/packs/solution-architect/skills/context-graph/evals/files/6/docs/adr/0002-billing-on-postgres.md +13 -0
  174. package/packs/solution-architect/skills/context-graph/evals/files/6/docs/adr/0004-ledger-sync-nightly-batch.md +9 -0
  175. package/packs/solution-architect/skills/context-graph/evals/files/6/docs/rfcs/RFC-0003-invoice-ids.md +9 -0
  176. package/packs/solution-architect/skills/context-graph/evals/files/6/docs/rfcs/RFC-0007-invoice-ids-ulid.md +11 -0
  177. package/packs/solution-architect/skills/context-graph/evals/files/6/docs/rfcs/RFC-0009-billing-event-store.md +15 -0
  178. package/packs/solution-architect/skills/context-graph/evals/files/6/docs/rfcs/RFC-0010-revenue-service.md +13 -0
  179. package/packs/solution-architect/skills/context-graph/evals/files/6/libs/money/ulid.py +8 -0
  180. package/packs/solution-architect/skills/context-graph/evals/files/6/services/billing/src/events.py +10 -0
  181. package/packs/solution-architect/skills/context-graph/evals/files/6/services/billing/src/store.py +10 -0
  182. package/packs/solution-architect/skills/context-graph/evals/files/6/services/invoicing/ids/generator.py +7 -0
  183. package/packs/solution-architect/skills/context-graph/evals/files/6/services/ledger/README.md +4 -0
  184. package/packs/solution-architect/skills/context-graph/evals/files/6/services/ledger/sync.py +3 -0
  185. package/packs/solution-architect/skills/context-graph/evals/files/7/README.md +4 -0
  186. package/packs/solution-architect/skills/context-graph/evals/files/7/db/queries/orders.sql +2 -0
  187. package/packs/solution-architect/skills/context-graph/evals/files/7/db/sharding/router.py +9 -0
  188. package/packs/solution-architect/skills/context-graph/evals/files/7/design/decisions/DD-001-postgres-primary.md +8 -0
  189. package/packs/solution-architect/skills/context-graph/evals/files/7/design/decisions/DD-002-kafka-event-bus.md +20 -0
  190. package/packs/solution-architect/skills/context-graph/evals/files/7/design/decisions/DD-003-no-orm.md +9 -0
  191. package/packs/solution-architect/skills/context-graph/evals/files/7/design/decisions/DD-006-tenant-sharding.md +9 -0
  192. package/packs/solution-architect/skills/context-graph/evals/files/7/design/decisions/DD-011-orm-for-admin-console.md +11 -0
  193. package/packs/solution-architect/skills/context-graph/evals/files/7/pipelines/fanout/router.py +4 -0
  194. package/packs/solution-architect/skills/context-graph/evals/files/7/pipelines/ingest/consumer.py +5 -0
  195. package/packs/solution-architect/skills/context-graph/evals/files/7/pipelines/ingest/topics.yaml +8 -0
  196. package/packs/solution-architect/skills/context-graph/evals/files/7/rfcs/0004-region-sharding.md +15 -0
  197. package/packs/solution-architect/skills/context-graph/evals/files/7/services/admin-console/models.py +11 -0
  198. package/packs/solution-architect/skills/context-graph/evals/files/7/services/orders/repo.py +9 -0
  199. package/packs/solution-architect/skills/context-graph/evals/files/8/README.md +11 -0
  200. package/packs/solution-architect/skills/context-graph/evals/files/8/api/ledger.proto +30 -0
  201. package/packs/solution-architect/skills/context-graph/evals/files/8/config/http.yaml +10 -0
  202. package/packs/solution-architect/skills/context-graph/evals/files/8/config/messaging.yaml +8 -0
  203. package/packs/solution-architect/skills/context-graph/evals/files/8/docs/adr/0001-record-architecture-decisions.md +20 -0
  204. package/packs/solution-architect/skills/context-graph/evals/files/8/docs/adr/0002-postgresql-for-ledger.md +22 -0
  205. package/packs/solution-architect/skills/context-graph/evals/files/8/docs/adr/0003-rabbitmq-domain-events.md +22 -0
  206. package/packs/solution-architect/skills/context-graph/evals/files/8/docs/adr/0004-integer-minor-units.md +32 -0
  207. package/packs/solution-architect/skills/context-graph/evals/files/8/docs/adr/0005-provider-http-retries.md +26 -0
  208. package/packs/solution-architect/skills/context-graph/evals/files/8/docs/adr/0006-openapi-generated-clients.md +20 -0
  209. package/packs/solution-architect/skills/context-graph/evals/files/8/docs/adr/0007-kafka-domain-events.md +25 -0
  210. package/packs/solution-architect/skills/context-graph/evals/files/8/docs/adr/0008-structured-logging.md +18 -0
  211. package/packs/solution-architect/skills/context-graph/evals/files/8/docs/rfcs/RFC-012-idempotency-keys.md +19 -0
  212. package/packs/solution-architect/skills/context-graph/evals/files/8/services/ledger/api/idempotency.py +19 -0
  213. package/packs/solution-architect/skills/context-graph/evals/files/8/services/ledger/api/server.py +35 -0
  214. package/packs/solution-architect/skills/context-graph/evals/files/8/services/ledger/clients/provider.py +33 -0
  215. package/packs/solution-architect/skills/context-graph/evals/files/8/services/ledger/db/migrations/0001_init.sql +14 -0
  216. package/packs/solution-architect/skills/context-graph/evals/files/8/services/ledger/db/migrations/0008_idempotency_keys.sql +5 -0
  217. package/packs/solution-architect/skills/context-graph/evals/files/8/services/ledger/db/migrations/0009_fx_entries.sql +4 -0
  218. package/packs/solution-architect/skills/context-graph/evals/files/8/services/ledger/events/publisher.py +28 -0
  219. package/packs/solution-architect/skills/context-graph/evals/files/8/services/ledger/logging.py +25 -0
  220. package/packs/solution-architect/skills/context-graph/evals/files/8/services/ledger/models/entry.py +16 -0
  221. package/packs/solution-architect/skills/context-graph/evals/files/9/.env.example +3 -0
  222. package/packs/solution-architect/skills/context-graph/evals/files/9/README.md +5 -0
  223. package/packs/solution-architect/skills/context-graph/evals/files/9/docker-compose.yml +18 -0
  224. package/packs/solution-architect/skills/context-graph/evals/files/9/docs/decisions/001-session-storage.md +21 -0
  225. package/packs/solution-architect/skills/context-graph/evals/files/9/docs/decisions/002-api-rate-limiting.md +32 -0
  226. package/packs/solution-architect/skills/context-graph/evals/files/9/docs/decisions/003-jwt-sessions.md +29 -0
  227. package/packs/solution-architect/skills/context-graph/evals/files/9/docs/decisions/004-catalog-cache.md +19 -0
  228. package/packs/solution-architect/skills/context-graph/evals/files/9/src/catalog/cache.ts +13 -0
  229. package/packs/solution-architect/skills/context-graph/evals/files/9/src/catalog/repository.ts +13 -0
  230. package/packs/solution-architect/skills/context-graph/evals/files/9/src/jobs/email.ts +3 -0
  231. package/packs/solution-architect/skills/context-graph/evals/files/9/src/jobs/export.ts +3 -0
  232. package/packs/solution-architect/skills/context-graph/evals/files/9/src/jobs/queue.ts +10 -0
  233. package/packs/solution-architect/skills/context-graph/evals/files/9/src/jobs/worker.ts +8 -0
  234. package/packs/solution-architect/skills/context-graph/evals/files/9/src/lib/db.ts +10 -0
  235. package/packs/solution-architect/skills/context-graph/evals/files/9/src/lib/redis.ts +8 -0
  236. package/packs/solution-architect/skills/context-graph/evals/files/9/src/middleware/rateLimit.ts +26 -0
  237. package/packs/solution-architect/skills/context-graph/evals/files/9/src/server.ts +17 -0
  238. package/packs/solution-architect/skills/context-graph/evals/files/9/src/session/store.ts +10 -0
  239. package/packs/solution-architect/skills/context-graph/examples/inputs/order-events/docs/adr/0004-kafka-for-order-events.md +19 -0
  240. package/packs/solution-architect/skills/context-graph/examples/inputs/order-events/docs/adr/0006-email-receipts.md +7 -0
  241. package/packs/solution-architect/skills/context-graph/examples/inputs/order-events/docs/adr/0009-sqs-for-order-events.md +19 -0
  242. package/packs/solution-architect/skills/context-graph/examples/inputs/order-events/services/notifications/receipts.py +0 -0
  243. package/packs/solution-architect/skills/context-graph/examples/inputs/order-events/services/orders/publisher.py +0 -0
  244. package/packs/solution-architect/skills/context-graph/examples/order-events.md +63 -0
  245. package/packs/solution-architect/skills/context-graph/manifest.json +33 -0
  246. package/packs/solution-architect/skills/context-graph/reference.md +104 -0
  247. package/packs/solution-architect/skills/context-graph/scripts/context_graph.py +634 -0
  248. package/packs/solution-architect/skills/event-contract-draft/manifest.json +1 -1
  249. package/packs/solution-architect/skills/event-contract-draft/scripts/check_asyncapi.py +14 -2
  250. package/packs/sre-oncall/skills/cloud-cost-review/manifest.json +1 -1
  251. package/packs/sre-oncall/skills/cloud-cost-review/scripts/cost_summary.py +2 -0
  252. package/packs/trackers/skills/backlog-triage/manifest.json +1 -1
  253. package/packs/trackers/skills/backlog-triage/scripts/triage_backlog.py +4 -1
  254. package/packs/trackers/skills/team-status-report/manifest.json +1 -1
  255. package/packs/trackers/skills/team-status-report/scripts/status_counts.py +4 -1
@@ -0,0 +1,17 @@
1
+ ---
2
+ status: accepted
3
+ date: 2024-01-15
4
+ deciders: payments guild
5
+ ---
6
+
7
+ # Money as integer minor units
8
+
9
+ ## Context and Problem Statement
10
+
11
+ A float rounding error put a basket total out by 1p, and the refund did not match the charge.
12
+
13
+ ## Decision Outcome
14
+
15
+ Chosen option: "integer minor units". Every amount in `libs/money/` is stored and computed as an
16
+ integer count of minor units (pence, cents). "No floats and no Decimal anywhere in money code:
17
+ one representation, exact by construction."
@@ -0,0 +1,12 @@
1
+ # 5. Retry failed background jobs
2
+
3
+ Date: 2024-03-04
4
+
5
+ ## Status
6
+
7
+ Accepted
8
+
9
+ ## Decision
10
+
11
+ A failed job in `services/worker/` is retried three times, then moved to a dead-letter queue.
12
+ Retry timing follows the backoff policy in ADR-0011.
@@ -0,0 +1,18 @@
1
+ ---
2
+ status: accepted
3
+ date: 2025-02-27
4
+ deciders: marketplace team
5
+ ---
6
+
7
+ # FX amounts with four decimal places
8
+
9
+ ## Context and Problem Statement
10
+
11
+ Marketplace sellers are paid in their own currency. Converting a price in whole minor units and
12
+ then converting back loses sub-penny amounts, and seller statements drift.
13
+
14
+ ## Decision Outcome
15
+
16
+ This builds on ADR-0004. Amounts in `libs/money/` are held as `Decimal` with four decimal places,
17
+ "so that a conversion and its reverse return the original amount", and are rounded to minor units
18
+ only when charged or paid out.
@@ -0,0 +1,16 @@
1
+ # 7. Feature flags in config, not a flag service
2
+
3
+ Date: 2025-06-09
4
+
5
+ ## Context
6
+
7
+ We want to ship checkout changes dark and turn them on per region.
8
+
9
+ ## Decision
10
+
11
+ Flags live in `services/checkout/flags.py` as a plain dictionary read at start-up. No external flag
12
+ service for now.
13
+
14
+ ## Consequences
15
+
16
+ Turning a flag on needs a deploy.
@@ -0,0 +1,14 @@
1
+ ---
2
+ status: deprecated
3
+ date: 2023-06-30
4
+ ---
5
+
6
+ # Legacy search on the database
7
+
8
+ ## Context and Problem Statement
9
+
10
+ Product search runs as SQL `LIKE` queries from `services/legacy-search/`.
11
+
12
+ ## Decision Outcome
13
+
14
+ Deprecated in 2024 when search moved to the hosted search provider. Kept for history.
@@ -0,0 +1,9 @@
1
+ from decimal import Decimal
2
+
3
+ PLACES = Decimal("0.0001")
4
+
5
+
6
+ class Amount:
7
+ def __init__(self, value, currency):
8
+ self.value = Decimal(value).quantize(PLACES)
9
+ self.currency = currency
@@ -0,0 +1,5 @@
1
+ """Resize product images on upload. Consumes 'resize' jobs."""
2
+
3
+
4
+ def handle_resize(job):
5
+ raise NotImplementedError
@@ -0,0 +1,9 @@
1
+ ---
2
+ id: RFC-0001
3
+ title: How we write RFCs
4
+ status: accepted
5
+ date: 2024-01-08
6
+ ---
7
+
8
+ Design decisions are written as RFCs in `docs/rfcs/`. An RFC starts as Proposed and becomes
9
+ Accepted or Rejected after review. A replaced RFC is marked "Superseded by" its successor.
@@ -0,0 +1,16 @@
1
+ ---
2
+ id: RFC-0002
3
+ title: Payments service boundary
4
+ status: accepted
5
+ date: 2024-06-10
6
+ authors: payments team
7
+ ---
8
+
9
+ ## Decision
10
+
11
+ Everything under `services/payments/` is owned by the payments team. Changes there need a review
12
+ from a payments engineer, and no other service imports from it directly; they call its API.
13
+
14
+ ## Why
15
+
16
+ "Card data and payment state stay behind one boundary, so the audit scope stays one service wide."
@@ -0,0 +1,19 @@
1
+ # RFC-0003: Fixed-interval webhook retries
2
+
3
+ Date: 2024-08-01
4
+
5
+ ## Status
6
+
7
+ Superseded by [RFC-0006](0006-webhook-retry-backoff.md)
8
+
9
+ ## Context
10
+
11
+ Merchants miss payment events when their endpoint is briefly down.
12
+
13
+ ## Decision
14
+
15
+ `services/payments/webhooks/retry.py` retries a failed webhook every 30 seconds, up to 100 times.
16
+
17
+ ## Consequences
18
+
19
+ Simple to reason about. A merchant endpoint that is down gets a request every 30 seconds.
@@ -0,0 +1,16 @@
1
+ ---
2
+ id: RFC-0004
3
+ title: Idempotency keys on every webhook
4
+ status: accepted
5
+ date: 2024-11-19
6
+ ---
7
+
8
+ ## Decision
9
+
10
+ Every webhook sent from `services/payments/webhooks/` carries an `Idempotency-Key` header. The key
11
+ is generated once per event and "every retry of that event resends the same key, so a merchant who
12
+ receives it twice can drop the duplicate".
13
+
14
+ ## Consequences
15
+
16
+ Merchants are told in the integration guide to deduplicate on the key.
@@ -0,0 +1,16 @@
1
+ # RFC-0005: Retrying customer notifications
2
+
3
+ Date: 2025-01-27
4
+
5
+ ## Status
6
+
7
+ Accepted
8
+
9
+ ## Decision
10
+
11
+ Email and SMS sends that fail are retried by `services/notifications/retry.py` every 5 minutes for
12
+ up to 24 hours, then dropped and logged.
13
+
14
+ ## Consequences
15
+
16
+ A customer may get a receipt email up to a day late.
@@ -0,0 +1,26 @@
1
+ ---
2
+ id: RFC-0006
3
+ title: Exponential backoff for webhook retries
4
+ status: accepted
5
+ date: 2025-04-22
6
+ supersedes: RFC-0003
7
+ authors: payments team
8
+ ---
9
+
10
+ ## Context and Problem Statement
11
+
12
+ During the 2025-03-06 incident a large merchant's endpoint was down for an hour. Fixed 30-second
13
+ retries from every queued event "hammered their endpoint as it came back and knocked it over
14
+ again".
15
+
16
+ ## Decision Outcome
17
+
18
+ `services/payments/webhooks/retry.py` retries with exponential backoff: the first retry after 2
19
+ seconds, doubling each time, capped at 10 minutes between attempts, and at most 8 attempts in all.
20
+ A 4xx response is not retried, except 429. After the last attempt the event is marked failed and
21
+ the merchant can replay it from the dashboard.
22
+
23
+ ### Consequences
24
+
25
+ * Good, because a recovering endpoint gets a trickle, not a flood.
26
+ * Bad, because an event can now be given up on within about four minutes.
@@ -0,0 +1,12 @@
1
+ ---
2
+ id: RFC-0007
3
+ title: Send webhooks from the job queue
4
+ status: proposed
5
+ date: 2025-09-15
6
+ ---
7
+
8
+ ## Proposal
9
+
10
+ Move delivery out of `services/payments/webhooks/` and onto the shared job queue, so retries
11
+ survive a deploy. Open questions: who owns the queue, and whether payment events may leave the
12
+ payments boundary in RFC-0002.
@@ -0,0 +1,4 @@
1
+ """Retry failed email and SMS sends (RFC-0005)."""
2
+
3
+ INTERVAL_S = 300
4
+ GIVE_UP_AFTER_S = 24 * 60 * 60
@@ -0,0 +1,15 @@
1
+ """Webhook retry policy (RFC-0006)."""
2
+
3
+ FIRST_DELAY_S = 2
4
+ MAX_DELAY_S = 600
5
+ MAX_ATTEMPTS = 8
6
+
7
+
8
+ def should_retry(status_code, attempt):
9
+ if attempt >= MAX_ATTEMPTS:
10
+ return False
11
+ return status_code == 429 or status_code >= 500
12
+
13
+
14
+ def delay_for(attempt):
15
+ return min(FIRST_DELAY_S * 2 ** attempt, MAX_DELAY_S)
@@ -0,0 +1,19 @@
1
+ # ADR-0001: Record architecture decisions
2
+
3
+ - Status: Accepted
4
+ - Date: 2023-01-09
5
+ - Deciders: Priya Okafor, Tomasz Lind
6
+
7
+ ## Context
8
+
9
+ Ledgerline is growing past the two people who wrote it. Decisions live in Slack threads.
10
+
11
+ ## Decision
12
+
13
+ We will record significant architecture decisions as numbered Markdown files in `docs/adr/`.
14
+ A decision that replaces an earlier one says so in a `Supersedes:` line, and the earlier
15
+ record's status is changed to `Superseded by ADR-NNNN`.
16
+
17
+ ## Consequences
18
+
19
+ Every new ADR is reviewed in the same PR as the code it governs.
@@ -0,0 +1,18 @@
1
+ # ADR-0002: Postgres is the system of record
2
+
3
+ - Status: Accepted
4
+ - Date: 2023-01-16
5
+ - Deciders: Priya Okafor
6
+
7
+ ## Context
8
+
9
+ Invoices, line items and payment allocations need transactional integrity.
10
+
11
+ ## Decision
12
+
13
+ All persistent state lives in a single Postgres 15 database. Access goes through
14
+ `src/ledgerline/db/`.
15
+
16
+ ## Consequences
17
+
18
+ No second datastore is introduced without a new ADR.
@@ -0,0 +1,19 @@
1
+ # ADR-0004: Redis locks for the nightly invoice run
2
+
3
+ - Status: Accepted
4
+ - Date: 2023-05-02
5
+ - Deciders: Tomasz Lind
6
+
7
+ ## Context
8
+
9
+ We run three worker replicas. On 2023-04-27 two replicas both started the nightly invoice
10
+ run and 41 customers were billed twice.
11
+
12
+ ## Decision
13
+
14
+ The nightly run takes a Redis `SET NX PX` lock (key `ledgerline:invoice-run`, 15 minute TTL,
15
+ renewed every 60 s) before it starts. Implemented in `src/ledgerline/scheduler/redis_lock.py`.
16
+
17
+ ## Consequences
18
+
19
+ Adds Redis to the stack. This is an exception to ADR-0002, accepted for speed.
@@ -0,0 +1,27 @@
1
+ # ADR-0007: Remove Redis; use Postgres advisory locks for the invoice run
2
+
3
+ - Status: Accepted
4
+ - Date: 2024-02-20
5
+ - Deciders: Priya Okafor, Tomasz Lind, Wen Haddad
6
+ - Supersedes: ADR-0004
7
+ - Relates to: ADR-0002
8
+
9
+ ## Context
10
+
11
+ Redis is used for exactly one thing: the invoice-run lock from ADR-0004. Running it costs a
12
+ managed instance and an on-call runbook. In January 2024 a Redis failover dropped the lock
13
+ mid-run and a second replica started; the idempotency check on `invoice_runs` caught it, but
14
+ only just.
15
+
16
+ ## Decision
17
+
18
+ Remove Redis. The nightly run takes a session-level Postgres advisory lock with
19
+ `pg_try_advisory_lock(727001)` on a dedicated connection and holds it for the whole run.
20
+ Implemented in `src/ledgerline/scheduler/lock.py`.
21
+
22
+ ## Consequences
23
+
24
+ - Back to one datastore, as ADR-0002 intended.
25
+ - Advisory locks are session-scoped: the lock connection must NOT go through PgBouncer in
26
+ transaction-pooling mode, or the lock is released as soon as the transaction ends.
27
+ - If the lock-holding process dies, Postgres releases the lock when the session closes.
@@ -0,0 +1,19 @@
1
+ # ADR-0009: Store money as integer minor units
2
+
3
+ - Status: Accepted
4
+ - Date: 2024-06-03
5
+ - Deciders: Wen Haddad
6
+
7
+ ## Context
8
+
9
+ Float rounding produced one-cent mismatches between invoice totals and line-item sums.
10
+
11
+ ## Decision
12
+
13
+ Amounts are stored and computed as integers in the currency's minor unit (cents for USD/EUR).
14
+ Rounding rules for currencies with three minor digits follow ADR-0005.
15
+ Implemented in `src/ledgerline/money.py`.
16
+
17
+ ## Consequences
18
+
19
+ All API payloads carry `amount_minor` plus `currency`.
@@ -0,0 +1,12 @@
1
+ # Welcome to Ledgerline
2
+
3
+ Ledgerline generates and sends customer invoices.
4
+
5
+ ## Architecture in one page
6
+
7
+ - Postgres holds everything (ADR-0002).
8
+ - The nightly invoice run is scheduled by `src/ledgerline/scheduler/runner.py`. Only one
9
+ worker may run it at a time, so it takes a Redis lock first (see ADR-0004).
10
+ - Money is integer cents (ADR-0009).
11
+
12
+ Last reviewed: 2023-09-14
@@ -0,0 +1,22 @@
1
+ """Postgres access (ADR-0002)."""
2
+
3
+ import os
4
+ from contextlib import contextmanager
5
+
6
+ import psycopg
7
+
8
+ # DATABASE_URL goes through PgBouncer (transaction pooling); DATABASE_DIRECT_URL does not.
9
+ POOLED_URL = os.environ.get("DATABASE_URL", "postgresql://app:CHANGE_ME@pgbouncer:6432/ledgerline")
10
+ DIRECT_URL = os.environ.get("DATABASE_DIRECT_URL", "postgresql://app:CHANGE_ME@db:5432/ledgerline")
11
+
12
+
13
+ @contextmanager
14
+ def pooled_connection():
15
+ with psycopg.connect(POOLED_URL) as conn:
16
+ yield conn
17
+
18
+
19
+ @contextmanager
20
+ def direct_connection():
21
+ with psycopg.connect(DIRECT_URL, autocommit=True) as conn:
22
+ yield conn
@@ -0,0 +1,9 @@
1
+ """Invoice generation."""
2
+
3
+
4
+ def run_nightly():
5
+ """Generate invoices for every customer whose billing date is today.
6
+
7
+ Guarded by an idempotency row in `invoice_runs` keyed on the run date.
8
+ """
9
+ raise NotImplementedError("fixture")
@@ -0,0 +1,10 @@
1
+ """Money as integer minor units (ADR-0009)."""
2
+
3
+ MINOR_DIGITS = {"USD": 2, "EUR": 2, "JPY": 0}
4
+
5
+
6
+ def to_minor(amount_str: str, currency: str) -> int:
7
+ digits = MINOR_DIGITS[currency]
8
+ whole, _, frac = amount_str.partition(".")
9
+ frac = (frac + "0" * digits)[:digits]
10
+ return int(whole) * 10**digits + (int(frac) if digits else 0)
@@ -0,0 +1,23 @@
1
+ """Single-runner guard for the nightly invoice run (ADR-0007)."""
2
+
3
+ from contextlib import contextmanager
4
+
5
+ INVOICE_RUN_LOCK_KEY = 727001
6
+
7
+
8
+ @contextmanager
9
+ def invoice_run_lock(conn):
10
+ """Hold a session-level advisory lock for the duration of the run.
11
+
12
+ `conn` must be a dedicated, direct connection (not via PgBouncer transaction pooling).
13
+ Yields True if this process holds the lock, False if another worker already does.
14
+ """
15
+ with conn.cursor() as cur:
16
+ cur.execute("SELECT pg_try_advisory_lock(%s)", (INVOICE_RUN_LOCK_KEY,))
17
+ acquired = cur.fetchone()[0]
18
+ try:
19
+ yield acquired
20
+ finally:
21
+ if acquired:
22
+ with conn.cursor() as cur:
23
+ cur.execute("SELECT pg_advisory_unlock(%s)", (INVOICE_RUN_LOCK_KEY,))
@@ -0,0 +1,17 @@
1
+ """Nightly invoice run entry point."""
2
+
3
+ from ledgerline.db import direct_connection
4
+ from ledgerline.scheduler.lock import invoice_run_lock
5
+ from ledgerline.invoicing import run_nightly
6
+
7
+
8
+ def main():
9
+ with direct_connection() as conn, invoice_run_lock(conn) as acquired:
10
+ if not acquired:
11
+ print("another worker holds the invoice-run lock; exiting")
12
+ return
13
+ run_nightly()
14
+
15
+
16
+ if __name__ == "__main__":
17
+ main()
@@ -0,0 +1,13 @@
1
+ export interface FeedEvent {
2
+ id: string;
3
+ kind: string;
4
+ occurredAt: string;
5
+ payload: Record<string, unknown>;
6
+ }
7
+
8
+ export interface Invoice {
9
+ id: string;
10
+ customerId: string;
11
+ amountMinor: number;
12
+ currency: string;
13
+ }
@@ -0,0 +1,20 @@
1
+ ---
2
+ id: RFC-0001
3
+ title: Service layout and shared code
4
+ status: accepted
5
+ date: 2024-11-02
6
+ authors: [Dara Mensah]
7
+ governs: [services/, lib/]
8
+ ---
9
+
10
+ # RFC-0001: Service layout and shared code
11
+
12
+ ## Decision
13
+
14
+ Each service under `services/<name>/` owns its own client, retry and error-handling code.
15
+ The only shared package is `lib/types/` (pure type definitions, no runtime behaviour).
16
+
17
+ ## Why
18
+
19
+ Ingest and export talk to different partners with different idempotency guarantees. A shared
20
+ runtime library would couple their failure behaviour and force lock-step releases.
@@ -0,0 +1,26 @@
1
+ ---
2
+ id: RFC-0003
3
+ title: Retry policy for the ingest service
4
+ status: accepted
5
+ date: 2025-03-11
6
+ authors: [Oskar Byrne]
7
+ governs: [services/ingest/retry.ts]
8
+ cites: [RFC-0001]
9
+ ---
10
+
11
+ # RFC-0003: Retry policy for the ingest service
12
+
13
+ ## Context
14
+
15
+ INC-114 (2025-02-27): the partner feed API returned 503 for four minutes. Every ingest worker
16
+ retried on a fixed 1 s interval, the retries stayed synchronised, and the partner rate-limited
17
+ our whole account for two hours after recovery.
18
+
19
+ ## Decision
20
+
21
+ Ingest retries use exponential backoff with full jitter: base 200 ms, cap 30 s, at most
22
+ 6 attempts. Fixed-interval retries are not allowed in ingest.
23
+
24
+ ## Consequences
25
+
26
+ Worst-case retry window is about 60 s per request. Dashboards alert on attempt 5+.
@@ -0,0 +1,24 @@
1
+ ---
2
+ id: RFC-0006
3
+ title: Extract a shared lib/retry package
4
+ status: rejected
5
+ date: 2025-06-19
6
+ authors: [Lucía Ferraro]
7
+ cites: [RFC-0001, RFC-0003]
8
+ ---
9
+
10
+ # RFC-0006: Extract a shared lib/retry package
11
+
12
+ ## Proposal
13
+
14
+ Move `services/ingest/retry.ts` and `services/export/retry.ts` into one `lib/retry/` package
15
+ with a single default policy.
16
+
17
+ ## Outcome: rejected
18
+
19
+ - Export calls to the partner billing API are NOT idempotent; export only retries on 503
20
+ responses that carry an `Idempotency-Key` echo. A single default policy would retry
21
+ unsafe calls.
22
+ - RFC-0001 keeps runtime code per service.
23
+
24
+ Revisit only if RFC-0001 is superseded.
@@ -0,0 +1,22 @@
1
+ ---
2
+ id: RFC-0008
3
+ title: One outbound HTTP client
4
+ status: accepted
5
+ date: 2025-09-30
6
+ authors: [Dara Mensah]
7
+ governs: [lib/http/, services/ingest/client.ts, services/export/client.ts]
8
+ amends: [RFC-0001]
9
+ cites: [RFC-0007]
10
+ ---
11
+
12
+ # RFC-0008: One outbound HTTP client
13
+
14
+ ## Decision
15
+
16
+ All outbound HTTP calls go through `lib/http/client.ts`. It sets timeouts, tracing headers and
17
+ applies retries uniformly (fixed 500 ms, 3 attempts). This amends RFC-0001 to allow `lib/http/`
18
+ as a second shared package. Idempotency keys are handled as described in RFC-0007.
19
+
20
+ ## Rollout
21
+
22
+ Services migrate their `client.ts` to `lib/http/` during Q4 2025.
@@ -0,0 +1,15 @@
1
+ import { retryIfSafe } from "./retry";
2
+ import type { Invoice } from "../../lib/types";
3
+
4
+ const BILLING_URL = process.env.PARTNER_BILLING_URL ?? "https://billing.partner.example/v1/invoices";
5
+
6
+ export async function pushInvoice(inv: Invoice, idempotencyKey: string): Promise<Response> {
7
+ return retryIfSafe(() =>
8
+ fetch(BILLING_URL, {
9
+ method: "POST",
10
+ headers: { "Content-Type": "application/json", "Idempotency-Key": idempotencyKey },
11
+ body: JSON.stringify(inv),
12
+ signal: AbortSignal.timeout(15_000),
13
+ }),
14
+ );
15
+ }
@@ -0,0 +1,12 @@
1
+ // Export retries are deliberately narrow: the billing API is not idempotent (RFC-0006).
2
+ const DELAY_MS = 1_000;
3
+ const MAX_ATTEMPTS = 3;
4
+
5
+ export async function retryIfSafe(fn: () => Promise<Response>): Promise<Response> {
6
+ for (let attempt = 1; ; attempt++) {
7
+ const res = await fn();
8
+ const safe = res.status === 503 && res.headers.has("Idempotency-Key");
9
+ if (!safe || attempt >= MAX_ATTEMPTS) return res;
10
+ await new Promise((r) => setTimeout(r, DELAY_MS));
11
+ }
12
+ }
@@ -0,0 +1,14 @@
1
+ import { withRetry } from "./retry";
2
+ import type { FeedEvent } from "../../lib/types";
3
+
4
+ const FEED_URL = process.env.PARTNER_FEED_URL ?? "https://feed.partner.example/v2/events";
5
+
6
+ export async function fetchEvents(cursor: string): Promise<FeedEvent[]> {
7
+ return withRetry(async () => {
8
+ const res = await fetch(`${FEED_URL}?cursor=${encodeURIComponent(cursor)}`, {
9
+ signal: AbortSignal.timeout(10_000),
10
+ });
11
+ if (!res.ok) throw new Error(`feed ${res.status}`);
12
+ return (await res.json()) as FeedEvent[];
13
+ });
14
+ }