skilldrop-cli 0.16.45 → 1.0.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 (230) hide show
  1. package/README.md +1 -1
  2. package/catalogue.json +1 -0
  3. package/model-routing.json +1 -0
  4. package/package.json +1 -1
  5. package/packs/solution-architect/skills/context-graph/SKILL.md +119 -0
  6. package/packs/solution-architect/skills/context-graph/evals/eval_queries.json +11 -0
  7. package/packs/solution-architect/skills/context-graph/evals/evals.json +225 -0
  8. package/packs/solution-architect/skills/context-graph/evals/files/1/docs/adr/0001-record-architecture-decisions.md +19 -0
  9. package/packs/solution-architect/skills/context-graph/evals/files/1/docs/adr/0002-postgres-as-primary-database.md +16 -0
  10. package/packs/solution-architect/skills/context-graph/evals/files/1/docs/adr/0003-use-redis-for-session-storage.md +24 -0
  11. package/packs/solution-architect/skills/context-graph/evals/files/1/docs/adr/0004-structured-json-logging.md +15 -0
  12. package/packs/solution-architect/skills/context-graph/evals/files/1/docs/adr/0005-session-lifetime-eight-hours.md +22 -0
  13. package/packs/solution-architect/skills/context-graph/evals/files/1/docs/adr/0006-move-sessions-to-postgres.md +16 -0
  14. package/packs/solution-architect/skills/context-graph/evals/files/1/docs/adr/0007-stateless-signed-session-cookies.md +35 -0
  15. package/packs/solution-architect/skills/context-graph/evals/files/1/services/api/__init__.py +0 -0
  16. package/packs/solution-architect/skills/context-graph/evals/files/1/services/api/db.py +0 -0
  17. package/packs/solution-architect/skills/context-graph/evals/files/1/services/api/logging_setup.py +0 -0
  18. package/packs/solution-architect/skills/context-graph/evals/files/1/services/auth/__init__.py +0 -0
  19. package/packs/solution-architect/skills/context-graph/evals/files/1/services/auth/cookie_sessions.py +7 -0
  20. package/packs/solution-architect/skills/context-graph/evals/files/1/services/auth/redis_sessions.py +7 -0
  21. package/packs/solution-architect/skills/context-graph/evals/files/10/README.md +8 -0
  22. package/packs/solution-architect/skills/context-graph/evals/files/10/docs/adr/0001-record-architecture-decisions.md +10 -0
  23. package/packs/solution-architect/skills/context-graph/evals/files/10/docs/adr/0004-service-auth-hmac-shared-keys.md +21 -0
  24. package/packs/solution-architect/skills/context-graph/evals/files/10/docs/adr/0007-secrets-from-vault-agent.md +19 -0
  25. package/packs/solution-architect/skills/context-graph/evals/files/10/docs/adr/0011-service-auth-mtls.md +23 -0
  26. package/packs/solution-architect/skills/context-graph/evals/files/10/services/billing-api/app.py +16 -0
  27. package/packs/solution-architect/skills/context-graph/evals/files/10/services/billing-api/auth/__init__.py +4 -0
  28. package/packs/solution-architect/skills/context-graph/evals/files/10/services/billing-api/auth/hmac_verify.py +31 -0
  29. package/packs/solution-architect/skills/context-graph/evals/files/10/services/billing-api/config/settings.py +14 -0
  30. package/packs/solution-architect/skills/context-graph/evals/files/10/services/ledger-worker/auth/__init__.py +4 -0
  31. package/packs/solution-architect/skills/context-graph/evals/files/10/services/ledger-worker/auth/mtls.py +18 -0
  32. package/packs/solution-architect/skills/context-graph/evals/files/11/README.md +4 -0
  33. package/packs/solution-architect/skills/context-graph/evals/files/11/app/models/base.py +3 -0
  34. package/packs/solution-architect/skills/context-graph/evals/files/11/app/models/line_item.py +14 -0
  35. package/packs/solution-architect/skills/context-graph/evals/files/11/app/models/order.py +24 -0
  36. package/packs/solution-architect/skills/context-graph/evals/files/11/eng/decisions/DR-003-money-as-minor-units.md +10 -0
  37. package/packs/solution-architect/skills/context-graph/evals/files/11/eng/decisions/DR-005-orders-typed-columns-only.md +10 -0
  38. package/packs/solution-architect/skills/context-graph/evals/files/11/eng/decisions/DR-009-line-items-immutable.md +11 -0
  39. package/packs/solution-architect/skills/context-graph/evals/files/11/eng/decisions/DR-014-optional-order-attributes-json.md +16 -0
  40. package/packs/solution-architect/skills/context-graph/evals/files/11/eng/decisions/DR-016-order-attributes-table.md +11 -0
  41. package/packs/solution-architect/skills/context-graph/evals/files/11/migrations/0007_create_orders.sql +11 -0
  42. package/packs/solution-architect/skills/context-graph/evals/files/11/migrations/0012_add_order_attributes.sql +2 -0
  43. package/packs/solution-architect/skills/context-graph/evals/files/12/README.md +8 -0
  44. package/packs/solution-architect/skills/context-graph/evals/files/12/config/delivery.yaml +4 -0
  45. package/packs/solution-architect/skills/context-graph/evals/files/12/config/export.yaml +2 -0
  46. package/packs/solution-architect/skills/context-graph/evals/files/12/config/partners.yaml +10 -0
  47. package/packs/solution-architect/skills/context-graph/evals/files/12/docs/adr/0001-record-architecture-decisions.md +10 -0
  48. package/packs/solution-architect/skills/context-graph/evals/files/12/docs/adr/0002-postgres-delivery-log.md +15 -0
  49. package/packs/solution-architect/skills/context-graph/evals/files/12/docs/adr/0003-redis-idempotency-keys.md +15 -0
  50. package/packs/solution-architect/skills/context-graph/evals/files/12/docs/adr/0004-retry-policy.md +16 -0
  51. package/packs/solution-architect/skills/context-graph/evals/files/12/docs/adr/0005-hmac-signed-payloads.md +10 -0
  52. package/packs/solution-architect/skills/context-graph/evals/files/12/docs/adr/0006-postgres-idempotency.md +15 -0
  53. package/packs/solution-architect/skills/context-graph/evals/files/12/docs/adr/0007-sftp-batch-export.md +12 -0
  54. package/packs/solution-architect/skills/context-graph/evals/files/12/docs/adr/0008-partner-config-in-yaml.md +12 -0
  55. package/packs/solution-architect/skills/context-graph/evals/files/12/docs/adr/0009-partner-config-admin-api.md +13 -0
  56. package/packs/solution-architect/skills/context-graph/evals/files/12/migrations/003_idempotency_keys.sql +6 -0
  57. package/packs/solution-architect/skills/context-graph/evals/files/12/migrations/004_partners.sql +7 -0
  58. package/packs/solution-architect/skills/context-graph/evals/files/12/src/delivery/dispatcher.py +29 -0
  59. package/packs/solution-architect/skills/context-graph/evals/files/12/src/delivery/retry.py +15 -0
  60. package/packs/solution-architect/skills/context-graph/evals/files/12/src/delivery/signing.py +13 -0
  61. package/packs/solution-architect/skills/context-graph/evals/files/12/src/export/s3_export.py +24 -0
  62. package/packs/solution-architect/skills/context-graph/evals/files/12/src/idempotency/pg_store.py +11 -0
  63. package/packs/solution-architect/skills/context-graph/evals/files/12/src/idempotency/redis_store.py +8 -0
  64. package/packs/solution-architect/skills/context-graph/evals/files/12/src/partners/loader.py +15 -0
  65. package/packs/solution-architect/skills/context-graph/evals/files/12/src/store/delivery_log.py +18 -0
  66. package/packs/solution-architect/skills/context-graph/evals/files/13/README.md +3 -0
  67. package/packs/solution-architect/skills/context-graph/evals/files/13/config/cache.yaml +2 -0
  68. package/packs/solution-architect/skills/context-graph/evals/files/13/config/settings.yaml +2 -0
  69. package/packs/solution-architect/skills/context-graph/evals/files/13/docs/decisions/RFC-008-pricing-from-erp.md +10 -0
  70. package/packs/solution-architect/skills/context-graph/evals/files/13/docs/decisions/RFC-012-pricing-cache.md +23 -0
  71. package/packs/solution-architect/skills/context-graph/evals/files/13/docs/decisions/RFC-015-checkout-price-lock.md +15 -0
  72. package/packs/solution-architect/skills/context-graph/evals/files/13/docs/decisions/RFC-019-pricing-service.md +17 -0
  73. package/packs/solution-architect/skills/context-graph/evals/files/13/shopfront/catalog/views.py +8 -0
  74. package/packs/solution-architect/skills/context-graph/evals/files/13/shopfront/checkout/lock.py +14 -0
  75. package/packs/solution-architect/skills/context-graph/evals/files/13/shopfront/pricing/cache.py +22 -0
  76. package/packs/solution-architect/skills/context-graph/evals/files/13/shopfront/pricing/client.py +10 -0
  77. package/packs/solution-architect/skills/context-graph/evals/files/14/docs/rfcs/0001-idempotency-keys.md +33 -0
  78. package/packs/solution-architect/skills/context-graph/evals/files/14/docs/rfcs/0002-processor-retry-policy.md +26 -0
  79. package/packs/solution-architect/skills/context-graph/evals/files/14/docs/rfcs/0003-money-in-minor-units.md +31 -0
  80. package/packs/solution-architect/skills/context-graph/evals/files/14/docs/rfcs/0004-webhook-signing.md +27 -0
  81. package/packs/solution-architect/skills/context-graph/evals/files/14/docs/rfcs/0005-retry-with-jitter.md +30 -0
  82. package/packs/solution-architect/skills/context-graph/evals/files/14/docs/rfcs/0006-idempotency-at-gateway.md +28 -0
  83. package/packs/solution-architect/skills/context-graph/evals/files/14/pr-summary.md +27 -0
  84. package/packs/solution-architect/skills/context-graph/evals/files/14/src/payments/amount.ts +10 -0
  85. package/packs/solution-architect/skills/context-graph/evals/files/14/src/payments/charge.ts +28 -0
  86. package/packs/solution-architect/skills/context-graph/evals/files/14/src/payments/idempotency.ts +4 -0
  87. package/packs/solution-architect/skills/context-graph/evals/files/14/src/payments/retry.ts +21 -0
  88. package/packs/solution-architect/skills/context-graph/evals/files/14/src/webhooks/send.ts +9 -0
  89. package/packs/solution-architect/skills/context-graph/evals/files/14/src/webhooks/sign.ts +6 -0
  90. package/packs/solution-architect/skills/context-graph/evals/files/15/deploy/services.yaml +14 -0
  91. package/packs/solution-architect/skills/context-graph/evals/files/15/doc/adr/0001-record-architecture-decisions.md +20 -0
  92. package/packs/solution-architect/skills/context-graph/evals/files/15/doc/adr/0002-use-postgresql-for-shipment-data.md +23 -0
  93. package/packs/solution-architect/skills/context-graph/evals/files/15/doc/adr/0003-store-vehicle-telemetry-in-cassandra.md +22 -0
  94. package/packs/solution-architect/skills/context-graph/evals/files/15/doc/adr/0004-use-redis-for-session-storage.md +22 -0
  95. package/packs/solution-architect/skills/context-graph/evals/files/15/doc/adr/0005-use-react-for-dispatcher-dashboard.md +19 -0
  96. package/packs/solution-architect/skills/context-graph/evals/files/15/doc/adr/0006-archive-delivered-shipments-to-object-storage.md +23 -0
  97. package/packs/solution-architect/skills/context-graph/evals/files/15/doc/adr/0007-move-shipment-records-to-mongodb.md +23 -0
  98. package/packs/solution-architect/skills/context-graph/evals/files/15/doc/adr/0008-store-vehicle-telemetry-in-timescaledb.md +28 -0
  99. package/packs/solution-architect/skills/context-graph/evals/files/15/doc/adr/0009-stateless-sessions-with-signed-cookies.md +24 -0
  100. package/packs/solution-architect/skills/context-graph/evals/files/15/services/api/session.py +17 -0
  101. package/packs/solution-architect/skills/context-graph/evals/files/15/services/shipments/db.py +30 -0
  102. package/packs/solution-architect/skills/context-graph/evals/files/15/services/shipments/migrations.sql +3 -0
  103. package/packs/solution-architect/skills/context-graph/evals/files/15/services/telemetry/store.py +23 -0
  104. package/packs/solution-architect/skills/context-graph/evals/files/15/web/dashboard/App.jsx +3 -0
  105. package/packs/solution-architect/skills/context-graph/evals/files/2/docs/adr/0001-record-architecture-decisions.md +11 -0
  106. package/packs/solution-architect/skills/context-graph/evals/files/2/docs/adr/0002-rabbitmq-for-background-jobs.md +15 -0
  107. package/packs/solution-architect/skills/context-graph/evals/files/2/docs/adr/0003-thumbnail-service.md +20 -0
  108. package/packs/solution-architect/skills/context-graph/evals/files/2/docs/adr/0004-money-as-integer-minor-units.md +17 -0
  109. package/packs/solution-architect/skills/context-graph/evals/files/2/docs/adr/0005-worker-retries.md +12 -0
  110. package/packs/solution-architect/skills/context-graph/evals/files/2/docs/adr/0006-fx-amounts-with-four-decimals.md +18 -0
  111. package/packs/solution-architect/skills/context-graph/evals/files/2/docs/adr/0007-feature-flags.md +16 -0
  112. package/packs/solution-architect/skills/context-graph/evals/files/2/docs/adr/0008-retire-legacy-search.md +14 -0
  113. package/packs/solution-architect/skills/context-graph/evals/files/2/libs/money/__init__.py +0 -0
  114. package/packs/solution-architect/skills/context-graph/evals/files/2/libs/money/amount.py +9 -0
  115. package/packs/solution-architect/skills/context-graph/evals/files/2/services/checkout/flags.py +1 -0
  116. package/packs/solution-architect/skills/context-graph/evals/files/2/services/checkout/publish.py +0 -0
  117. package/packs/solution-architect/skills/context-graph/evals/files/2/services/media/resize.py +5 -0
  118. package/packs/solution-architect/skills/context-graph/evals/files/2/services/worker/consumer.py +0 -0
  119. package/packs/solution-architect/skills/context-graph/evals/files/3/docs/rfcs/0001-how-we-write-rfcs.md +9 -0
  120. package/packs/solution-architect/skills/context-graph/evals/files/3/docs/rfcs/0002-payments-service-boundary.md +16 -0
  121. package/packs/solution-architect/skills/context-graph/evals/files/3/docs/rfcs/0003-webhook-retries-fixed-interval.md +19 -0
  122. package/packs/solution-architect/skills/context-graph/evals/files/3/docs/rfcs/0004-webhook-idempotency-keys.md +16 -0
  123. package/packs/solution-architect/skills/context-graph/evals/files/3/docs/rfcs/0005-notification-retries.md +16 -0
  124. package/packs/solution-architect/skills/context-graph/evals/files/3/docs/rfcs/0006-webhook-retry-backoff.md +26 -0
  125. package/packs/solution-architect/skills/context-graph/evals/files/3/docs/rfcs/0007-webhooks-on-the-job-queue.md +12 -0
  126. package/packs/solution-architect/skills/context-graph/evals/files/3/services/notifications/__init__.py +0 -0
  127. package/packs/solution-architect/skills/context-graph/evals/files/3/services/notifications/retry.py +4 -0
  128. package/packs/solution-architect/skills/context-graph/evals/files/3/services/payments/__init__.py +0 -0
  129. package/packs/solution-architect/skills/context-graph/evals/files/3/services/payments/webhooks/__init__.py +0 -0
  130. package/packs/solution-architect/skills/context-graph/evals/files/3/services/payments/webhooks/retry.py +15 -0
  131. package/packs/solution-architect/skills/context-graph/evals/files/3/services/payments/webhooks/send.py +0 -0
  132. package/packs/solution-architect/skills/context-graph/evals/files/4/docs/adr/0001-record-architecture-decisions.md +19 -0
  133. package/packs/solution-architect/skills/context-graph/evals/files/4/docs/adr/0002-postgres-as-system-of-record.md +18 -0
  134. package/packs/solution-architect/skills/context-graph/evals/files/4/docs/adr/0004-redis-locks-for-scheduler.md +19 -0
  135. package/packs/solution-architect/skills/context-graph/evals/files/4/docs/adr/0007-remove-redis.md +27 -0
  136. package/packs/solution-architect/skills/context-graph/evals/files/4/docs/adr/0009-money-as-integer-minor-units.md +19 -0
  137. package/packs/solution-architect/skills/context-graph/evals/files/4/docs/onboarding.md +12 -0
  138. package/packs/solution-architect/skills/context-graph/evals/files/4/src/ledgerline/__init__.py +0 -0
  139. package/packs/solution-architect/skills/context-graph/evals/files/4/src/ledgerline/db/__init__.py +22 -0
  140. package/packs/solution-architect/skills/context-graph/evals/files/4/src/ledgerline/invoicing.py +9 -0
  141. package/packs/solution-architect/skills/context-graph/evals/files/4/src/ledgerline/money.py +10 -0
  142. package/packs/solution-architect/skills/context-graph/evals/files/4/src/ledgerline/scheduler/__init__.py +0 -0
  143. package/packs/solution-architect/skills/context-graph/evals/files/4/src/ledgerline/scheduler/lock.py +23 -0
  144. package/packs/solution-architect/skills/context-graph/evals/files/4/src/ledgerline/scheduler/runner.py +17 -0
  145. package/packs/solution-architect/skills/context-graph/evals/files/5/lib/types/index.ts +13 -0
  146. package/packs/solution-architect/skills/context-graph/evals/files/5/rfcs/0001-service-layout.md +20 -0
  147. package/packs/solution-architect/skills/context-graph/evals/files/5/rfcs/0003-ingest-retry-policy.md +26 -0
  148. package/packs/solution-architect/skills/context-graph/evals/files/5/rfcs/0006-shared-retry-library.md +24 -0
  149. package/packs/solution-architect/skills/context-graph/evals/files/5/rfcs/0008-outbound-http-client.md +22 -0
  150. package/packs/solution-architect/skills/context-graph/evals/files/5/services/export/client.ts +15 -0
  151. package/packs/solution-architect/skills/context-graph/evals/files/5/services/export/retry.ts +12 -0
  152. package/packs/solution-architect/skills/context-graph/evals/files/5/services/ingest/client.ts +14 -0
  153. package/packs/solution-architect/skills/context-graph/evals/files/5/services/ingest/retry.ts +20 -0
  154. package/packs/solution-architect/skills/context-graph/evals/files/6/README.md +4 -0
  155. package/packs/solution-architect/skills/context-graph/evals/files/6/docs/adr/0001-monorepo-layout.md +10 -0
  156. package/packs/solution-architect/skills/context-graph/evals/files/6/docs/adr/0002-billing-on-postgres.md +13 -0
  157. package/packs/solution-architect/skills/context-graph/evals/files/6/docs/adr/0004-ledger-sync-nightly-batch.md +9 -0
  158. package/packs/solution-architect/skills/context-graph/evals/files/6/docs/rfcs/RFC-0003-invoice-ids.md +9 -0
  159. package/packs/solution-architect/skills/context-graph/evals/files/6/docs/rfcs/RFC-0007-invoice-ids-ulid.md +11 -0
  160. package/packs/solution-architect/skills/context-graph/evals/files/6/docs/rfcs/RFC-0009-billing-event-store.md +15 -0
  161. package/packs/solution-architect/skills/context-graph/evals/files/6/docs/rfcs/RFC-0010-revenue-service.md +13 -0
  162. package/packs/solution-architect/skills/context-graph/evals/files/6/libs/money/ulid.py +8 -0
  163. package/packs/solution-architect/skills/context-graph/evals/files/6/services/billing/src/events.py +10 -0
  164. package/packs/solution-architect/skills/context-graph/evals/files/6/services/billing/src/store.py +10 -0
  165. package/packs/solution-architect/skills/context-graph/evals/files/6/services/invoicing/ids/generator.py +7 -0
  166. package/packs/solution-architect/skills/context-graph/evals/files/6/services/ledger/README.md +4 -0
  167. package/packs/solution-architect/skills/context-graph/evals/files/6/services/ledger/sync.py +3 -0
  168. package/packs/solution-architect/skills/context-graph/evals/files/7/README.md +4 -0
  169. package/packs/solution-architect/skills/context-graph/evals/files/7/db/queries/orders.sql +2 -0
  170. package/packs/solution-architect/skills/context-graph/evals/files/7/db/sharding/router.py +9 -0
  171. package/packs/solution-architect/skills/context-graph/evals/files/7/design/decisions/DD-001-postgres-primary.md +8 -0
  172. package/packs/solution-architect/skills/context-graph/evals/files/7/design/decisions/DD-002-kafka-event-bus.md +20 -0
  173. package/packs/solution-architect/skills/context-graph/evals/files/7/design/decisions/DD-003-no-orm.md +9 -0
  174. package/packs/solution-architect/skills/context-graph/evals/files/7/design/decisions/DD-006-tenant-sharding.md +9 -0
  175. package/packs/solution-architect/skills/context-graph/evals/files/7/design/decisions/DD-011-orm-for-admin-console.md +11 -0
  176. package/packs/solution-architect/skills/context-graph/evals/files/7/pipelines/fanout/router.py +4 -0
  177. package/packs/solution-architect/skills/context-graph/evals/files/7/pipelines/ingest/consumer.py +5 -0
  178. package/packs/solution-architect/skills/context-graph/evals/files/7/pipelines/ingest/topics.yaml +8 -0
  179. package/packs/solution-architect/skills/context-graph/evals/files/7/rfcs/0004-region-sharding.md +15 -0
  180. package/packs/solution-architect/skills/context-graph/evals/files/7/services/admin-console/models.py +11 -0
  181. package/packs/solution-architect/skills/context-graph/evals/files/7/services/orders/repo.py +9 -0
  182. package/packs/solution-architect/skills/context-graph/evals/files/8/README.md +11 -0
  183. package/packs/solution-architect/skills/context-graph/evals/files/8/api/ledger.proto +30 -0
  184. package/packs/solution-architect/skills/context-graph/evals/files/8/config/http.yaml +10 -0
  185. package/packs/solution-architect/skills/context-graph/evals/files/8/config/messaging.yaml +8 -0
  186. package/packs/solution-architect/skills/context-graph/evals/files/8/docs/adr/0001-record-architecture-decisions.md +20 -0
  187. package/packs/solution-architect/skills/context-graph/evals/files/8/docs/adr/0002-postgresql-for-ledger.md +22 -0
  188. package/packs/solution-architect/skills/context-graph/evals/files/8/docs/adr/0003-rabbitmq-domain-events.md +22 -0
  189. package/packs/solution-architect/skills/context-graph/evals/files/8/docs/adr/0004-integer-minor-units.md +32 -0
  190. package/packs/solution-architect/skills/context-graph/evals/files/8/docs/adr/0005-provider-http-retries.md +26 -0
  191. package/packs/solution-architect/skills/context-graph/evals/files/8/docs/adr/0006-openapi-generated-clients.md +20 -0
  192. package/packs/solution-architect/skills/context-graph/evals/files/8/docs/adr/0007-kafka-domain-events.md +25 -0
  193. package/packs/solution-architect/skills/context-graph/evals/files/8/docs/adr/0008-structured-logging.md +18 -0
  194. package/packs/solution-architect/skills/context-graph/evals/files/8/docs/rfcs/RFC-012-idempotency-keys.md +19 -0
  195. package/packs/solution-architect/skills/context-graph/evals/files/8/services/ledger/api/idempotency.py +19 -0
  196. package/packs/solution-architect/skills/context-graph/evals/files/8/services/ledger/api/server.py +35 -0
  197. package/packs/solution-architect/skills/context-graph/evals/files/8/services/ledger/clients/provider.py +33 -0
  198. package/packs/solution-architect/skills/context-graph/evals/files/8/services/ledger/db/migrations/0001_init.sql +14 -0
  199. package/packs/solution-architect/skills/context-graph/evals/files/8/services/ledger/db/migrations/0008_idempotency_keys.sql +5 -0
  200. package/packs/solution-architect/skills/context-graph/evals/files/8/services/ledger/db/migrations/0009_fx_entries.sql +4 -0
  201. package/packs/solution-architect/skills/context-graph/evals/files/8/services/ledger/events/publisher.py +28 -0
  202. package/packs/solution-architect/skills/context-graph/evals/files/8/services/ledger/logging.py +25 -0
  203. package/packs/solution-architect/skills/context-graph/evals/files/8/services/ledger/models/entry.py +16 -0
  204. package/packs/solution-architect/skills/context-graph/evals/files/9/.env.example +3 -0
  205. package/packs/solution-architect/skills/context-graph/evals/files/9/README.md +5 -0
  206. package/packs/solution-architect/skills/context-graph/evals/files/9/docker-compose.yml +18 -0
  207. package/packs/solution-architect/skills/context-graph/evals/files/9/docs/decisions/001-session-storage.md +21 -0
  208. package/packs/solution-architect/skills/context-graph/evals/files/9/docs/decisions/002-api-rate-limiting.md +32 -0
  209. package/packs/solution-architect/skills/context-graph/evals/files/9/docs/decisions/003-jwt-sessions.md +29 -0
  210. package/packs/solution-architect/skills/context-graph/evals/files/9/docs/decisions/004-catalog-cache.md +19 -0
  211. package/packs/solution-architect/skills/context-graph/evals/files/9/src/catalog/cache.ts +13 -0
  212. package/packs/solution-architect/skills/context-graph/evals/files/9/src/catalog/repository.ts +13 -0
  213. package/packs/solution-architect/skills/context-graph/evals/files/9/src/jobs/email.ts +3 -0
  214. package/packs/solution-architect/skills/context-graph/evals/files/9/src/jobs/export.ts +3 -0
  215. package/packs/solution-architect/skills/context-graph/evals/files/9/src/jobs/queue.ts +10 -0
  216. package/packs/solution-architect/skills/context-graph/evals/files/9/src/jobs/worker.ts +8 -0
  217. package/packs/solution-architect/skills/context-graph/evals/files/9/src/lib/db.ts +10 -0
  218. package/packs/solution-architect/skills/context-graph/evals/files/9/src/lib/redis.ts +8 -0
  219. package/packs/solution-architect/skills/context-graph/evals/files/9/src/middleware/rateLimit.ts +26 -0
  220. package/packs/solution-architect/skills/context-graph/evals/files/9/src/server.ts +17 -0
  221. package/packs/solution-architect/skills/context-graph/evals/files/9/src/session/store.ts +10 -0
  222. package/packs/solution-architect/skills/context-graph/examples/inputs/order-events/docs/adr/0004-kafka-for-order-events.md +19 -0
  223. package/packs/solution-architect/skills/context-graph/examples/inputs/order-events/docs/adr/0006-email-receipts.md +7 -0
  224. package/packs/solution-architect/skills/context-graph/examples/inputs/order-events/docs/adr/0009-sqs-for-order-events.md +19 -0
  225. package/packs/solution-architect/skills/context-graph/examples/inputs/order-events/services/notifications/receipts.py +0 -0
  226. package/packs/solution-architect/skills/context-graph/examples/inputs/order-events/services/orders/publisher.py +0 -0
  227. package/packs/solution-architect/skills/context-graph/examples/order-events.md +63 -0
  228. package/packs/solution-architect/skills/context-graph/manifest.json +33 -0
  229. package/packs/solution-architect/skills/context-graph/reference.md +104 -0
  230. package/packs/solution-architect/skills/context-graph/scripts/context_graph.py +634 -0
@@ -0,0 +1,32 @@
1
+ ---
2
+ id: ADR-0004
3
+ title: Store money as integer minor units
4
+ status: accepted
5
+ date: 2024-03-05
6
+ deciders: [m.okafor, j.lindqvist]
7
+ ---
8
+
9
+ # Store money as integer minor units
10
+
11
+ ## Context and Problem Statement
12
+
13
+ Floating point and arbitrary-precision decimals have both caused rounding bugs on previous
14
+ projects. How should monetary amounts be represented in the ledger?
15
+
16
+ ## Considered Options
17
+
18
+ * `NUMERIC` columns with Python `Decimal`
19
+ * Integer minor units (cents, pence) with a separate ISO 4217 currency code
20
+
21
+ ## Decision Outcome
22
+
23
+ Chosen option: "Integer minor units", because sums are exact and every currency we support
24
+ has a fixed minor-unit exponent.
25
+
26
+ Every amount column in `services/ledger/db/migrations/` is `BIGINT` named `*_minor`, paired
27
+ with a `currency CHAR(3)` column. `services/ledger/models/entry.py` exposes amounts as `int`.
28
+
29
+ ### Consequences
30
+
31
+ * Good, because addition is exact and fast.
32
+ * Bad, because display code must apply the currency exponent.
@@ -0,0 +1,26 @@
1
+ # ADR 0005: Retry and timeout policy for payment-provider calls
2
+
3
+ * Status: Accepted
4
+ * Date: 2024-04-18
5
+
6
+ ## Context
7
+
8
+ The payment provider occasionally returns 502/503 during their deploys. Unbounded retries
9
+ during an outage amplified load on them last year and held our worker threads.
10
+
11
+ ## Decision
12
+
13
+ Calls from `services/ledger/clients/provider.py` to the provider:
14
+
15
+ | Setting | Value |
16
+ |--------------------|-----------------------------------------|
17
+ | Request timeout | 5 seconds |
18
+ | Max retries | 3 |
19
+ | Backoff | exponential, 200 ms base, full jitter |
20
+ | Retry on | GET and idempotent POST (with key) only |
21
+
22
+ Values are read from `config/http.yaml`; the code must not hard-code them.
23
+
24
+ ## Consequences
25
+
26
+ Worst-case latency of a provider call is bounded at roughly 21 seconds.
@@ -0,0 +1,20 @@
1
+ # 6. Generate service clients from an OpenAPI spec
2
+
3
+ Date: 2024-06-03
4
+
5
+ ## Status
6
+
7
+ Accepted
8
+
9
+ ## Context
10
+
11
+ Three internal teams call the ledger and each hand-wrote a client.
12
+
13
+ ## Decision
14
+
15
+ The ledger's public interface is defined in `api/openapi.yaml`. Clients are generated from it
16
+ into `clients/generated/` on every release; hand-written clients are not accepted.
17
+
18
+ ## Consequences
19
+
20
+ - The spec is the source of truth; the server is checked against it in CI.
@@ -0,0 +1,25 @@
1
+ ---
2
+ id: ADR-0007
3
+ title: Move domain events to Kafka
4
+ status: accepted
5
+ date: 2025-03-11
6
+ supersedes: ADR-0003
7
+ ---
8
+
9
+ # Move domain events to Kafka
10
+
11
+ ## Context and Problem Statement
12
+
13
+ Reporting now needs to replay the full history of posted entries, which RabbitMQ queues
14
+ cannot give us once a message is acknowledged.
15
+
16
+ ## Decision Outcome
17
+
18
+ Domain events are published to the Kafka topic `ledger.entry-events`, keyed by account id,
19
+ with 30-day retention. This supersedes ADR-0003. Publishing stays in
20
+ `services/ledger/events/`; broker settings stay in `config/messaging.yaml`.
21
+
22
+ ### Consequences
23
+
24
+ * Good, because consumers can replay from any offset within retention.
25
+ * Bad, because the team now operates a Kafka cluster.
@@ -0,0 +1,18 @@
1
+ # ADR 0008: Structured JSON logging
2
+
3
+ * Status: Accepted
4
+ * Date: 2025-05-20
5
+
6
+ ## Context
7
+
8
+ Log search across services is painful because each one logs free text differently.
9
+
10
+ ## Decision
11
+
12
+ All ledger logging goes through `services/ledger/logging.py`, which emits one JSON object per
13
+ line with `ts`, `level`, `msg`, `trace_id` and `account_id` fields. The `trace_id` comes from
14
+ the tracing setup described in ADR-0011.
15
+
16
+ ## Consequences
17
+
18
+ Plain `print` and ad-hoc `logging.basicConfig` calls are not allowed in service code.
@@ -0,0 +1,19 @@
1
+ # RFC-012: Idempotency keys for posting entries
2
+
3
+ - **State:** Draft
4
+ - **Author:** j.lindqvist
5
+ - **Opened:** 2025-08-02
6
+
7
+ ## Summary
8
+
9
+ Clients send an `Idempotency-Key` with every `PostEntry` call. The ledger stores the key and
10
+ the response for 24 hours and returns the stored response on a repeat.
11
+
12
+ ## Proposed implementation
13
+
14
+ `services/ledger/api/idempotency.py`, backed by a table `idempotency_keys` in PostgreSQL.
15
+
16
+ ## Open questions
17
+
18
+ - Is 24 hours long enough for the batch importer, which can retry after a day?
19
+ - Should a key reused with a different body be a 409 or a 422?
@@ -0,0 +1,19 @@
1
+ import json
2
+ from datetime import timedelta
3
+
4
+ KEY_TTL = timedelta(hours=24)
5
+
6
+
7
+ def lookup(conn, key: str):
8
+ row = conn.execute(
9
+ "SELECT response FROM idempotency_keys WHERE key = %s AND created_at > now() - %s",
10
+ (key, KEY_TTL),
11
+ ).fetchone()
12
+ return json.loads(row[0]) if row else None
13
+
14
+
15
+ def store(conn, key: str, response: dict) -> None:
16
+ conn.execute(
17
+ "INSERT INTO idempotency_keys (key, response) VALUES (%s, %s) ON CONFLICT DO NOTHING",
18
+ (key, json.dumps(response)),
19
+ )
@@ -0,0 +1,35 @@
1
+ from concurrent import futures
2
+
3
+ import grpc
4
+
5
+ from services.ledger.api import idempotency
6
+ from services.ledger.api.gen import ledger_pb2, ledger_pb2_grpc
7
+ from services.ledger.logging import get_logger
8
+
9
+ log = get_logger(__name__)
10
+
11
+
12
+ class LedgerServicer(ledger_pb2_grpc.LedgerServicer):
13
+ def __init__(self, db):
14
+ self.db = db
15
+
16
+ def PostEntry(self, request, context):
17
+ with self.db.transaction() as conn:
18
+ cached = idempotency.lookup(conn, request.idempotency_key)
19
+ if cached:
20
+ return ledger_pb2.PostEntryResponse(**cached)
21
+ entry_id = self.db.post_entry(conn, request)
22
+ idempotency.store(conn, request.idempotency_key, {"entry_id": entry_id})
23
+ return ledger_pb2.PostEntryResponse(entry_id=entry_id)
24
+
25
+ def GetBalance(self, request, context):
26
+ return self.db.balance(request.account_id)
27
+
28
+
29
+ def serve(db, port: int = 50051) -> None:
30
+ server = grpc.server(futures.ThreadPoolExecutor(max_workers=16))
31
+ ledger_pb2_grpc.add_LedgerServicer_to_server(LedgerServicer(db), server)
32
+ server.add_insecure_port(f"[::]:{port}")
33
+ server.start()
34
+ log.info("ledger gRPC server started")
35
+ server.wait_for_termination()
@@ -0,0 +1,33 @@
1
+ import random
2
+ import time
3
+
4
+ import httpx
5
+ import yaml
6
+
7
+ with open("config/http.yaml") as fh:
8
+ _cfg = yaml.safe_load(fh)["provider"]
9
+
10
+
11
+ def _sleep(attempt: int) -> None:
12
+ base = _cfg["backoff"]["base_ms"] / 1000
13
+ time.sleep(random.uniform(0, base * (2 ** attempt)))
14
+
15
+
16
+ def call(method: str, path: str, **kwargs) -> httpx.Response:
17
+ retries = _cfg["max_retries"] if method in _cfg["retry_methods"] else 0
18
+ for attempt in range(retries + 1):
19
+ try:
20
+ resp = httpx.request(
21
+ method,
22
+ _cfg["base_url"] + path,
23
+ timeout=_cfg["timeout_seconds"],
24
+ headers={"Authorization": f"Bearer {_cfg['api_key']}"},
25
+ **kwargs,
26
+ )
27
+ if resp.status_code not in (502, 503):
28
+ return resp
29
+ except httpx.TransportError:
30
+ if attempt == retries:
31
+ raise
32
+ _sleep(attempt)
33
+ return resp
@@ -0,0 +1,14 @@
1
+ CREATE TABLE accounts (
2
+ id UUID PRIMARY KEY,
3
+ currency CHAR(3) NOT NULL,
4
+ balance_minor BIGINT NOT NULL DEFAULT 0,
5
+ frozen BOOLEAN NOT NULL DEFAULT FALSE
6
+ );
7
+
8
+ CREATE TABLE entries (
9
+ id UUID PRIMARY KEY,
10
+ account_id UUID NOT NULL REFERENCES accounts(id),
11
+ amount_minor BIGINT NOT NULL,
12
+ currency CHAR(3) NOT NULL,
13
+ posted_at TIMESTAMPTZ NOT NULL DEFAULT now()
14
+ );
@@ -0,0 +1,5 @@
1
+ CREATE TABLE idempotency_keys (
2
+ key TEXT PRIMARY KEY,
3
+ response JSONB NOT NULL,
4
+ created_at TIMESTAMPTZ NOT NULL DEFAULT now()
5
+ );
@@ -0,0 +1,4 @@
1
+ -- FX conversions: store the converted amount and the rate used.
2
+ ALTER TABLE entries ADD COLUMN fx_amount NUMERIC(18,4);
3
+ ALTER TABLE entries ADD COLUMN fx_currency CHAR(3);
4
+ ALTER TABLE entries ADD COLUMN fx_rate NUMERIC(18,8);
@@ -0,0 +1,28 @@
1
+ import json
2
+
3
+ import yaml
4
+ from confluent_kafka import Producer
5
+
6
+ from services.ledger.logging import get_logger
7
+
8
+ log = get_logger(__name__)
9
+
10
+ with open("config/messaging.yaml") as fh:
11
+ _cfg = yaml.safe_load(fh)
12
+
13
+ _producer = Producer({
14
+ "bootstrap.servers": ",".join(_cfg["bootstrap_servers"]),
15
+ "acks": _cfg["acks"],
16
+ })
17
+
18
+
19
+ def publish_entry_posted(entry) -> None:
20
+ payload = {
21
+ "type": "EntryPosted",
22
+ "entry_id": entry.id,
23
+ "account_id": entry.account_id,
24
+ "amount_minor": entry.amount_minor,
25
+ "currency": entry.currency,
26
+ }
27
+ _producer.produce(_cfg["topic"], key=entry.account_id, value=json.dumps(payload))
28
+ log.info("event published", extra={"account_id": entry.account_id})
@@ -0,0 +1,25 @@
1
+ import json
2
+ import logging
3
+ import sys
4
+ from datetime import datetime, timezone
5
+
6
+
7
+ class _JsonFormatter(logging.Formatter):
8
+ def format(self, record: logging.LogRecord) -> str:
9
+ return json.dumps({
10
+ "ts": datetime.now(timezone.utc).isoformat(),
11
+ "level": record.levelname,
12
+ "msg": record.getMessage(),
13
+ "trace_id": getattr(record, "trace_id", None),
14
+ "account_id": getattr(record, "account_id", None),
15
+ })
16
+
17
+
18
+ def get_logger(name: str) -> logging.Logger:
19
+ logger = logging.getLogger(name)
20
+ if not logger.handlers:
21
+ handler = logging.StreamHandler(sys.stdout)
22
+ handler.setFormatter(_JsonFormatter())
23
+ logger.addHandler(handler)
24
+ logger.setLevel(logging.INFO)
25
+ return logger
@@ -0,0 +1,16 @@
1
+ from dataclasses import dataclass
2
+ from datetime import datetime
3
+ from decimal import Decimal
4
+ from typing import Optional
5
+
6
+
7
+ @dataclass(frozen=True)
8
+ class Entry:
9
+ id: str
10
+ account_id: str
11
+ amount_minor: int
12
+ currency: str
13
+ posted_at: datetime
14
+ fx_amount: Optional[Decimal] = None
15
+ fx_currency: Optional[str] = None
16
+ fx_rate: Optional[Decimal] = None
@@ -0,0 +1,3 @@
1
+ DATABASE_URL=postgres://tidepool:example-only@postgres:5432/tidepool
2
+ REDIS_URL=redis://redis:6379/0
3
+ SESSION_SECRET=replace-me
@@ -0,0 +1,5 @@
1
+ # tidepool-shop
2
+
3
+ Storefront API for Tidepool Outfitters. TypeScript, Express, PostgreSQL, Redis.
4
+
5
+ Run locally with `docker compose up`. Decision records live in `docs/decisions/`.
@@ -0,0 +1,18 @@
1
+ services:
2
+ app:
3
+ build: .
4
+ ports: ["3000:3000"]
5
+ env_file: .env
6
+ depends_on: [postgres, redis]
7
+ worker:
8
+ build: .
9
+ command: ["node", "dist/jobs/worker.js"]
10
+ env_file: .env
11
+ depends_on: [redis]
12
+ postgres:
13
+ image: postgres:16
14
+ environment:
15
+ POSTGRES_PASSWORD: example-only
16
+ redis:
17
+ image: redis:7
18
+ ports: ["6379:6379"]
@@ -0,0 +1,21 @@
1
+ # 001. Store user sessions in Redis
2
+
3
+ Date: 2023-05-09
4
+
5
+ ## Status
6
+
7
+ Superseded by [003](003-jwt-sessions.md)
8
+
9
+ ## Context
10
+
11
+ We run three app containers behind the load balancer. In-memory sessions log users out
12
+ whenever a request lands on a different container.
13
+
14
+ ## Decision
15
+
16
+ Sessions are stored in Redis via `connect-redis`, configured in `src/session/store.ts`,
17
+ with a 7-day TTL.
18
+
19
+ ## Consequences
20
+
21
+ Redis becomes a hard dependency of login.
@@ -0,0 +1,32 @@
1
+ ---
2
+ status: accepted
3
+ date: 2023-11-20
4
+ deciders: platform team
5
+ ---
6
+
7
+ # Rate-limit the public API with a shared token bucket
8
+
9
+ ## Context and Problem Statement
10
+
11
+ A partner's misconfigured sync job hit `/v1/products` 40 times a second for an hour. We need
12
+ per-API-key limits that hold across all app containers.
13
+
14
+ ## Considered Options
15
+
16
+ * Per-container in-memory limiter
17
+ * Token bucket in Redis, shared by all containers
18
+ * Limit at the CDN edge
19
+
20
+ ## Decision Outcome
21
+
22
+ Chosen option: "Token bucket in Redis", because limits must be global and the CDN plan we
23
+ are on cannot key on API key.
24
+
25
+ Implemented in `src/middleware/rateLimit.ts`: 600 requests per minute per API key, using a
26
+ Lua script so refill-and-take is atomic. Redis availability and failover are covered in
27
+ DR-007.
28
+
29
+ ### Consequences
30
+
31
+ * Good, because limits are exact across containers.
32
+ * Bad, because if Redis is down the limiter fails open.
@@ -0,0 +1,29 @@
1
+ # RFC 003 — Stateless JWT sessions
2
+
3
+ | Field | Value |
4
+ |---------|------------|
5
+ | State | Proposed |
6
+ | Author | r.achebe |
7
+ | Created | 2026-09-02 |
8
+ | Replaces | 001 |
9
+
10
+ ## Motivation
11
+
12
+ Mobile clients want a bearer token rather than a cookie, and we would like login to keep
13
+ working during a Redis restart.
14
+
15
+ ## Proposal
16
+
17
+ Issue a signed access token (15 minutes) and a refresh token (7 days). The app verifies the
18
+ signature locally; no session lookup per request.
19
+
20
+ ## Open questions
21
+
22
+ 1. **Revocation.** Logout and "sign out everywhere" need to invalidate tokens before they
23
+ expire. Current thinking: keep a denylist of revoked token ids in Redis with a TTL equal to
24
+ the token's remaining lifetime.
25
+ 2. Key rotation schedule — needs a decision before this is accepted.
26
+
27
+ ## Status of discussion
28
+
29
+ Not yet reviewed by the platform team.
@@ -0,0 +1,19 @@
1
+ # 004. Cache the product catalogue in process
2
+
3
+ - Status: Accepted
4
+ - Date: 2024-02-27
5
+
6
+ ## Context
7
+
8
+ Catalogue reads are about 70% of API traffic, and the catalogue changes a few times a day.
9
+
10
+ ## Decision
11
+
12
+ Each app container holds the catalogue in an in-process LRU cache (`lru-cache`, 5,000 items,
13
+ 10-minute TTL) in `src/catalog/cache.ts`. We deliberately do **not** put the catalogue in
14
+ Redis: it adds a network hop to the hottest path for data that is fine to be a few minutes
15
+ stale per container.
16
+
17
+ ## Consequences
18
+
19
+ Containers may briefly disagree after a catalogue update. That is acceptable.
@@ -0,0 +1,13 @@
1
+ import { redis } from "../lib/redis";
2
+ import { loadProduct, type Product } from "./repository";
3
+
4
+ const TTL_SECONDS = 600;
5
+
6
+ // Moved to Redis so all containers see catalogue updates at once (see #482).
7
+ export async function getProduct(id: string): Promise<Product | null> {
8
+ const hit = await redis.get(`catalog:${id}`);
9
+ if (hit) return JSON.parse(hit) as Product;
10
+ const product = await loadProduct(id);
11
+ if (product) await redis.set(`catalog:${id}`, JSON.stringify(product), { EX: TTL_SECONDS });
12
+ return product;
13
+ }
@@ -0,0 +1,13 @@
1
+ import { db } from "../lib/db";
2
+
3
+ export interface Product {
4
+ id: string;
5
+ name: string;
6
+ priceCents: number;
7
+ }
8
+
9
+ export async function loadProduct(id: string): Promise<Product | null> {
10
+ const rows = await db.query("SELECT id, name, price_cents FROM products WHERE id = $1", [id]);
11
+ if (rows.length === 0) return null;
12
+ return { id: rows[0].id, name: rows[0].name, priceCents: rows[0].price_cents };
13
+ }
@@ -0,0 +1,3 @@
1
+ export async function sendEmail(data: { orderId: string; to: string }): Promise<void> {
2
+ console.log(`would send order confirmation for ${data.orderId} to ${data.to}`);
3
+ }
@@ -0,0 +1,3 @@
1
+ export async function exportOrder(data: { orderId: string }): Promise<void> {
2
+ console.log(`would export order ${data.orderId} to the warehouse feed`);
3
+ }
@@ -0,0 +1,10 @@
1
+ import { Queue } from "bullmq";
2
+
3
+ const connection = { url: process.env.REDIS_URL };
4
+
5
+ export const emailQueue = new Queue("email", { connection });
6
+ export const orderExportQueue = new Queue("order-export", { connection });
7
+
8
+ export async function enqueueOrderConfirmation(orderId: string, to: string) {
9
+ await emailQueue.add("order-confirmation", { orderId, to }, { attempts: 5 });
10
+ }
@@ -0,0 +1,8 @@
1
+ import { Worker } from "bullmq";
2
+ import { sendEmail } from "./email";
3
+ import { exportOrder } from "./export";
4
+
5
+ const connection = { url: process.env.REDIS_URL };
6
+
7
+ new Worker("email", async (job) => sendEmail(job.data), { connection });
8
+ new Worker("order-export", async (job) => exportOrder(job.data), { connection });
@@ -0,0 +1,10 @@
1
+ import { Pool } from "pg";
2
+
3
+ const pool = new Pool({ connectionString: process.env.DATABASE_URL });
4
+
5
+ export const db = {
6
+ async query(text: string, params: unknown[] = []) {
7
+ const result = await pool.query(text, params);
8
+ return result.rows;
9
+ },
10
+ };
@@ -0,0 +1,8 @@
1
+ import { createClient } from "redis";
2
+
3
+ export const redis = createClient({ url: process.env.REDIS_URL });
4
+ redis.on("error", (err) => console.error("redis error", err));
5
+
6
+ export async function connectRedis(): Promise<void> {
7
+ if (!redis.isOpen) await redis.connect();
8
+ }
@@ -0,0 +1,26 @@
1
+ import type { Request, Response, NextFunction } from "express";
2
+ import { redis } from "../lib/redis";
3
+
4
+ const LIMIT_PER_MINUTE = 600;
5
+
6
+ const TAKE_TOKEN = `
7
+ local tokens = tonumber(redis.call('GET', KEYS[1]) or ARGV[1])
8
+ if tokens <= 0 then return 0 end
9
+ redis.call('SET', KEYS[1], tokens - 1, 'EX', 60, 'KEEPTTL')
10
+ return 1
11
+ `;
12
+
13
+ export async function rateLimit(req: Request, res: Response, next: NextFunction) {
14
+ const apiKey = req.header("x-api-key");
15
+ if (!apiKey) return next();
16
+ try {
17
+ const ok = await redis.eval(TAKE_TOKEN, {
18
+ keys: [`rl:${apiKey}`],
19
+ arguments: [String(LIMIT_PER_MINUTE)],
20
+ });
21
+ if (ok === 0) return res.status(429).json({ error: "rate_limited" });
22
+ } catch {
23
+ // fail open, per decision 002
24
+ }
25
+ next();
26
+ }
@@ -0,0 +1,17 @@
1
+ import express from "express";
2
+ import { connectRedis } from "./lib/redis";
3
+ import { sessionMiddleware } from "./session/store";
4
+ import { rateLimit } from "./middleware/rateLimit";
5
+ import { getProduct } from "./catalog/cache";
6
+
7
+ const app = express();
8
+ app.use(sessionMiddleware);
9
+ app.use("/v1", rateLimit);
10
+
11
+ app.get("/v1/products/:id", async (req, res) => {
12
+ const product = await getProduct(req.params.id);
13
+ if (!product) return res.status(404).end();
14
+ res.json(product);
15
+ });
16
+
17
+ connectRedis().then(() => app.listen(3000));
@@ -0,0 +1,10 @@
1
+ import session from "express-session";
2
+ import RedisStore from "connect-redis";
3
+ import { redis } from "../lib/redis";
4
+
5
+ export const sessionMiddleware = session({
6
+ store: new RedisStore({ client: redis, prefix: "sess:", ttl: 60 * 60 * 24 * 7 }),
7
+ secret: process.env.SESSION_SECRET ?? "",
8
+ resave: false,
9
+ saveUninitialized: false,
10
+ });
@@ -0,0 +1,19 @@
1
+ # ADR-0004: Kafka for order events
2
+
3
+ ## Status
4
+
5
+ Accepted
6
+
7
+ Date: 2025-03-02
8
+
9
+ ## Context
10
+
11
+ Order events feed billing, notifications and the warehouse. We need replay for new consumers.
12
+
13
+ ## Decision
14
+
15
+ Order events are published to Kafka from `services/orders/`, one topic per event type.
16
+
17
+ ## Consequences
18
+
19
+ Replay works for a new consumer. We run a Kafka cluster.
@@ -0,0 +1,7 @@
1
+ ---
2
+ status: accepted
3
+ date: 2025-06-11
4
+ ---
5
+ # Email receipts from the notifications service
6
+
7
+ Receipts are sent by `services/notifications/` when it reads an OrderPlaced event (see ADR-0004).