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.
- package/catalogue.json +1 -0
- package/model-routing.json +1 -0
- package/package.json +1 -1
- package/packs/converters/skills/md-to-html/manifest.json +1 -1
- package/packs/converters/skills/md-to-html/scripts/md_to_html.py +3 -0
- package/packs/design/skills/brand-kit/manifest.json +1 -1
- package/packs/design/skills/brand-kit/scripts/check_brand.py +11 -1
- package/packs/dev-team/skills/behaviour-equivalence/manifest.json +1 -1
- package/packs/dev-team/skills/behaviour-equivalence/scripts/golden_diff.py +11 -1
- package/packs/frontend/skills/bundle-size-review/manifest.json +1 -1
- package/packs/frontend/skills/bundle-size-review/scripts/bundle_stats.py +13 -1
- package/packs/frontend/skills/web-performance-review/manifest.json +1 -1
- package/packs/frontend/skills/web-performance-review/scripts/lh_summary.py +12 -2
- package/packs/grc/skills/risk-register/manifest.json +1 -1
- package/packs/grc/skills/risk-register/scripts/check_register.py +9 -1
- package/packs/infra-as-code/skills/terraform-plan-review/manifest.json +1 -1
- package/packs/infra-as-code/skills/terraform-plan-review/scripts/plan_summary.py +12 -2
- package/packs/product-manager/skills/feedback-synthesis/manifest.json +1 -1
- package/packs/product-manager/skills/feedback-synthesis/scripts/theme_counts.py +6 -1
- package/packs/research/skills/hypothesis-comparison/manifest.json +1 -1
- package/packs/research/skills/hypothesis-comparison/scripts/ach_matrix.py +10 -2
- package/packs/solution-architect/skills/context-graph/SKILL.md +119 -0
- package/packs/solution-architect/skills/context-graph/evals/eval_queries.json +11 -0
- package/packs/solution-architect/skills/context-graph/evals/evals.json +225 -0
- package/packs/solution-architect/skills/context-graph/evals/files/1/docs/adr/0001-record-architecture-decisions.md +19 -0
- package/packs/solution-architect/skills/context-graph/evals/files/1/docs/adr/0002-postgres-as-primary-database.md +16 -0
- package/packs/solution-architect/skills/context-graph/evals/files/1/docs/adr/0003-use-redis-for-session-storage.md +24 -0
- package/packs/solution-architect/skills/context-graph/evals/files/1/docs/adr/0004-structured-json-logging.md +15 -0
- package/packs/solution-architect/skills/context-graph/evals/files/1/docs/adr/0005-session-lifetime-eight-hours.md +22 -0
- package/packs/solution-architect/skills/context-graph/evals/files/1/docs/adr/0006-move-sessions-to-postgres.md +16 -0
- package/packs/solution-architect/skills/context-graph/evals/files/1/docs/adr/0007-stateless-signed-session-cookies.md +35 -0
- package/packs/solution-architect/skills/context-graph/evals/files/1/services/api/__init__.py +0 -0
- package/packs/solution-architect/skills/context-graph/evals/files/1/services/api/db.py +0 -0
- package/packs/solution-architect/skills/context-graph/evals/files/1/services/api/logging_setup.py +0 -0
- package/packs/solution-architect/skills/context-graph/evals/files/1/services/auth/__init__.py +0 -0
- package/packs/solution-architect/skills/context-graph/evals/files/1/services/auth/cookie_sessions.py +7 -0
- package/packs/solution-architect/skills/context-graph/evals/files/1/services/auth/redis_sessions.py +7 -0
- package/packs/solution-architect/skills/context-graph/evals/files/10/README.md +8 -0
- package/packs/solution-architect/skills/context-graph/evals/files/10/docs/adr/0001-record-architecture-decisions.md +10 -0
- package/packs/solution-architect/skills/context-graph/evals/files/10/docs/adr/0004-service-auth-hmac-shared-keys.md +21 -0
- package/packs/solution-architect/skills/context-graph/evals/files/10/docs/adr/0007-secrets-from-vault-agent.md +19 -0
- package/packs/solution-architect/skills/context-graph/evals/files/10/docs/adr/0011-service-auth-mtls.md +23 -0
- package/packs/solution-architect/skills/context-graph/evals/files/10/services/billing-api/app.py +16 -0
- package/packs/solution-architect/skills/context-graph/evals/files/10/services/billing-api/auth/__init__.py +4 -0
- package/packs/solution-architect/skills/context-graph/evals/files/10/services/billing-api/auth/hmac_verify.py +31 -0
- package/packs/solution-architect/skills/context-graph/evals/files/10/services/billing-api/config/settings.py +14 -0
- package/packs/solution-architect/skills/context-graph/evals/files/10/services/ledger-worker/auth/__init__.py +4 -0
- package/packs/solution-architect/skills/context-graph/evals/files/10/services/ledger-worker/auth/mtls.py +18 -0
- package/packs/solution-architect/skills/context-graph/evals/files/11/README.md +4 -0
- package/packs/solution-architect/skills/context-graph/evals/files/11/app/models/base.py +3 -0
- package/packs/solution-architect/skills/context-graph/evals/files/11/app/models/line_item.py +14 -0
- package/packs/solution-architect/skills/context-graph/evals/files/11/app/models/order.py +24 -0
- package/packs/solution-architect/skills/context-graph/evals/files/11/eng/decisions/DR-003-money-as-minor-units.md +10 -0
- package/packs/solution-architect/skills/context-graph/evals/files/11/eng/decisions/DR-005-orders-typed-columns-only.md +10 -0
- package/packs/solution-architect/skills/context-graph/evals/files/11/eng/decisions/DR-009-line-items-immutable.md +11 -0
- package/packs/solution-architect/skills/context-graph/evals/files/11/eng/decisions/DR-014-optional-order-attributes-json.md +16 -0
- package/packs/solution-architect/skills/context-graph/evals/files/11/eng/decisions/DR-016-order-attributes-table.md +11 -0
- package/packs/solution-architect/skills/context-graph/evals/files/11/migrations/0007_create_orders.sql +11 -0
- package/packs/solution-architect/skills/context-graph/evals/files/11/migrations/0012_add_order_attributes.sql +2 -0
- package/packs/solution-architect/skills/context-graph/evals/files/12/README.md +8 -0
- package/packs/solution-architect/skills/context-graph/evals/files/12/config/delivery.yaml +4 -0
- package/packs/solution-architect/skills/context-graph/evals/files/12/config/export.yaml +2 -0
- package/packs/solution-architect/skills/context-graph/evals/files/12/config/partners.yaml +10 -0
- package/packs/solution-architect/skills/context-graph/evals/files/12/docs/adr/0001-record-architecture-decisions.md +10 -0
- package/packs/solution-architect/skills/context-graph/evals/files/12/docs/adr/0002-postgres-delivery-log.md +15 -0
- package/packs/solution-architect/skills/context-graph/evals/files/12/docs/adr/0003-redis-idempotency-keys.md +15 -0
- package/packs/solution-architect/skills/context-graph/evals/files/12/docs/adr/0004-retry-policy.md +16 -0
- package/packs/solution-architect/skills/context-graph/evals/files/12/docs/adr/0005-hmac-signed-payloads.md +10 -0
- package/packs/solution-architect/skills/context-graph/evals/files/12/docs/adr/0006-postgres-idempotency.md +15 -0
- package/packs/solution-architect/skills/context-graph/evals/files/12/docs/adr/0007-sftp-batch-export.md +12 -0
- package/packs/solution-architect/skills/context-graph/evals/files/12/docs/adr/0008-partner-config-in-yaml.md +12 -0
- package/packs/solution-architect/skills/context-graph/evals/files/12/docs/adr/0009-partner-config-admin-api.md +13 -0
- package/packs/solution-architect/skills/context-graph/evals/files/12/migrations/003_idempotency_keys.sql +6 -0
- package/packs/solution-architect/skills/context-graph/evals/files/12/migrations/004_partners.sql +7 -0
- package/packs/solution-architect/skills/context-graph/evals/files/12/src/delivery/dispatcher.py +29 -0
- package/packs/solution-architect/skills/context-graph/evals/files/12/src/delivery/retry.py +15 -0
- package/packs/solution-architect/skills/context-graph/evals/files/12/src/delivery/signing.py +13 -0
- package/packs/solution-architect/skills/context-graph/evals/files/12/src/export/s3_export.py +24 -0
- package/packs/solution-architect/skills/context-graph/evals/files/12/src/idempotency/pg_store.py +11 -0
- package/packs/solution-architect/skills/context-graph/evals/files/12/src/idempotency/redis_store.py +8 -0
- package/packs/solution-architect/skills/context-graph/evals/files/12/src/partners/loader.py +15 -0
- package/packs/solution-architect/skills/context-graph/evals/files/12/src/store/delivery_log.py +18 -0
- package/packs/solution-architect/skills/context-graph/evals/files/13/README.md +3 -0
- package/packs/solution-architect/skills/context-graph/evals/files/13/config/cache.yaml +2 -0
- package/packs/solution-architect/skills/context-graph/evals/files/13/config/settings.yaml +2 -0
- package/packs/solution-architect/skills/context-graph/evals/files/13/docs/decisions/RFC-008-pricing-from-erp.md +10 -0
- package/packs/solution-architect/skills/context-graph/evals/files/13/docs/decisions/RFC-012-pricing-cache.md +23 -0
- package/packs/solution-architect/skills/context-graph/evals/files/13/docs/decisions/RFC-015-checkout-price-lock.md +15 -0
- package/packs/solution-architect/skills/context-graph/evals/files/13/docs/decisions/RFC-019-pricing-service.md +17 -0
- package/packs/solution-architect/skills/context-graph/evals/files/13/shopfront/catalog/views.py +8 -0
- package/packs/solution-architect/skills/context-graph/evals/files/13/shopfront/checkout/lock.py +14 -0
- package/packs/solution-architect/skills/context-graph/evals/files/13/shopfront/pricing/cache.py +22 -0
- package/packs/solution-architect/skills/context-graph/evals/files/13/shopfront/pricing/client.py +10 -0
- package/packs/solution-architect/skills/context-graph/evals/files/14/docs/rfcs/0001-idempotency-keys.md +33 -0
- package/packs/solution-architect/skills/context-graph/evals/files/14/docs/rfcs/0002-processor-retry-policy.md +26 -0
- package/packs/solution-architect/skills/context-graph/evals/files/14/docs/rfcs/0003-money-in-minor-units.md +31 -0
- package/packs/solution-architect/skills/context-graph/evals/files/14/docs/rfcs/0004-webhook-signing.md +27 -0
- package/packs/solution-architect/skills/context-graph/evals/files/14/docs/rfcs/0005-retry-with-jitter.md +30 -0
- package/packs/solution-architect/skills/context-graph/evals/files/14/docs/rfcs/0006-idempotency-at-gateway.md +28 -0
- package/packs/solution-architect/skills/context-graph/evals/files/14/pr-summary.md +27 -0
- package/packs/solution-architect/skills/context-graph/evals/files/14/src/payments/amount.ts +10 -0
- package/packs/solution-architect/skills/context-graph/evals/files/14/src/payments/charge.ts +28 -0
- package/packs/solution-architect/skills/context-graph/evals/files/14/src/payments/idempotency.ts +4 -0
- package/packs/solution-architect/skills/context-graph/evals/files/14/src/payments/retry.ts +21 -0
- package/packs/solution-architect/skills/context-graph/evals/files/14/src/webhooks/send.ts +9 -0
- package/packs/solution-architect/skills/context-graph/evals/files/14/src/webhooks/sign.ts +6 -0
- package/packs/solution-architect/skills/context-graph/evals/files/15/deploy/services.yaml +14 -0
- package/packs/solution-architect/skills/context-graph/evals/files/15/doc/adr/0001-record-architecture-decisions.md +20 -0
- package/packs/solution-architect/skills/context-graph/evals/files/15/doc/adr/0002-use-postgresql-for-shipment-data.md +23 -0
- package/packs/solution-architect/skills/context-graph/evals/files/15/doc/adr/0003-store-vehicle-telemetry-in-cassandra.md +22 -0
- package/packs/solution-architect/skills/context-graph/evals/files/15/doc/adr/0004-use-redis-for-session-storage.md +22 -0
- package/packs/solution-architect/skills/context-graph/evals/files/15/doc/adr/0005-use-react-for-dispatcher-dashboard.md +19 -0
- package/packs/solution-architect/skills/context-graph/evals/files/15/doc/adr/0006-archive-delivered-shipments-to-object-storage.md +23 -0
- package/packs/solution-architect/skills/context-graph/evals/files/15/doc/adr/0007-move-shipment-records-to-mongodb.md +23 -0
- package/packs/solution-architect/skills/context-graph/evals/files/15/doc/adr/0008-store-vehicle-telemetry-in-timescaledb.md +28 -0
- package/packs/solution-architect/skills/context-graph/evals/files/15/doc/adr/0009-stateless-sessions-with-signed-cookies.md +24 -0
- package/packs/solution-architect/skills/context-graph/evals/files/15/services/api/session.py +17 -0
- package/packs/solution-architect/skills/context-graph/evals/files/15/services/shipments/db.py +30 -0
- package/packs/solution-architect/skills/context-graph/evals/files/15/services/shipments/migrations.sql +3 -0
- package/packs/solution-architect/skills/context-graph/evals/files/15/services/telemetry/store.py +23 -0
- package/packs/solution-architect/skills/context-graph/evals/files/15/web/dashboard/App.jsx +3 -0
- package/packs/solution-architect/skills/context-graph/evals/files/2/docs/adr/0001-record-architecture-decisions.md +11 -0
- package/packs/solution-architect/skills/context-graph/evals/files/2/docs/adr/0002-rabbitmq-for-background-jobs.md +15 -0
- package/packs/solution-architect/skills/context-graph/evals/files/2/docs/adr/0003-thumbnail-service.md +20 -0
- package/packs/solution-architect/skills/context-graph/evals/files/2/docs/adr/0004-money-as-integer-minor-units.md +17 -0
- package/packs/solution-architect/skills/context-graph/evals/files/2/docs/adr/0005-worker-retries.md +12 -0
- package/packs/solution-architect/skills/context-graph/evals/files/2/docs/adr/0006-fx-amounts-with-four-decimals.md +18 -0
- package/packs/solution-architect/skills/context-graph/evals/files/2/docs/adr/0007-feature-flags.md +16 -0
- package/packs/solution-architect/skills/context-graph/evals/files/2/docs/adr/0008-retire-legacy-search.md +14 -0
- package/packs/solution-architect/skills/context-graph/evals/files/2/libs/money/__init__.py +0 -0
- package/packs/solution-architect/skills/context-graph/evals/files/2/libs/money/amount.py +9 -0
- package/packs/solution-architect/skills/context-graph/evals/files/2/services/checkout/flags.py +1 -0
- package/packs/solution-architect/skills/context-graph/evals/files/2/services/checkout/publish.py +0 -0
- package/packs/solution-architect/skills/context-graph/evals/files/2/services/media/resize.py +5 -0
- package/packs/solution-architect/skills/context-graph/evals/files/2/services/worker/consumer.py +0 -0
- package/packs/solution-architect/skills/context-graph/evals/files/3/docs/rfcs/0001-how-we-write-rfcs.md +9 -0
- package/packs/solution-architect/skills/context-graph/evals/files/3/docs/rfcs/0002-payments-service-boundary.md +16 -0
- package/packs/solution-architect/skills/context-graph/evals/files/3/docs/rfcs/0003-webhook-retries-fixed-interval.md +19 -0
- package/packs/solution-architect/skills/context-graph/evals/files/3/docs/rfcs/0004-webhook-idempotency-keys.md +16 -0
- package/packs/solution-architect/skills/context-graph/evals/files/3/docs/rfcs/0005-notification-retries.md +16 -0
- package/packs/solution-architect/skills/context-graph/evals/files/3/docs/rfcs/0006-webhook-retry-backoff.md +26 -0
- package/packs/solution-architect/skills/context-graph/evals/files/3/docs/rfcs/0007-webhooks-on-the-job-queue.md +12 -0
- package/packs/solution-architect/skills/context-graph/evals/files/3/services/notifications/__init__.py +0 -0
- package/packs/solution-architect/skills/context-graph/evals/files/3/services/notifications/retry.py +4 -0
- package/packs/solution-architect/skills/context-graph/evals/files/3/services/payments/__init__.py +0 -0
- package/packs/solution-architect/skills/context-graph/evals/files/3/services/payments/webhooks/__init__.py +0 -0
- package/packs/solution-architect/skills/context-graph/evals/files/3/services/payments/webhooks/retry.py +15 -0
- package/packs/solution-architect/skills/context-graph/evals/files/3/services/payments/webhooks/send.py +0 -0
- package/packs/solution-architect/skills/context-graph/evals/files/4/docs/adr/0001-record-architecture-decisions.md +19 -0
- package/packs/solution-architect/skills/context-graph/evals/files/4/docs/adr/0002-postgres-as-system-of-record.md +18 -0
- package/packs/solution-architect/skills/context-graph/evals/files/4/docs/adr/0004-redis-locks-for-scheduler.md +19 -0
- package/packs/solution-architect/skills/context-graph/evals/files/4/docs/adr/0007-remove-redis.md +27 -0
- package/packs/solution-architect/skills/context-graph/evals/files/4/docs/adr/0009-money-as-integer-minor-units.md +19 -0
- package/packs/solution-architect/skills/context-graph/evals/files/4/docs/onboarding.md +12 -0
- package/packs/solution-architect/skills/context-graph/evals/files/4/src/ledgerline/__init__.py +0 -0
- package/packs/solution-architect/skills/context-graph/evals/files/4/src/ledgerline/db/__init__.py +22 -0
- package/packs/solution-architect/skills/context-graph/evals/files/4/src/ledgerline/invoicing.py +9 -0
- package/packs/solution-architect/skills/context-graph/evals/files/4/src/ledgerline/money.py +10 -0
- package/packs/solution-architect/skills/context-graph/evals/files/4/src/ledgerline/scheduler/__init__.py +0 -0
- package/packs/solution-architect/skills/context-graph/evals/files/4/src/ledgerline/scheduler/lock.py +23 -0
- package/packs/solution-architect/skills/context-graph/evals/files/4/src/ledgerline/scheduler/runner.py +17 -0
- package/packs/solution-architect/skills/context-graph/evals/files/5/lib/types/index.ts +13 -0
- package/packs/solution-architect/skills/context-graph/evals/files/5/rfcs/0001-service-layout.md +20 -0
- package/packs/solution-architect/skills/context-graph/evals/files/5/rfcs/0003-ingest-retry-policy.md +26 -0
- package/packs/solution-architect/skills/context-graph/evals/files/5/rfcs/0006-shared-retry-library.md +24 -0
- package/packs/solution-architect/skills/context-graph/evals/files/5/rfcs/0008-outbound-http-client.md +22 -0
- package/packs/solution-architect/skills/context-graph/evals/files/5/services/export/client.ts +15 -0
- package/packs/solution-architect/skills/context-graph/evals/files/5/services/export/retry.ts +12 -0
- package/packs/solution-architect/skills/context-graph/evals/files/5/services/ingest/client.ts +14 -0
- package/packs/solution-architect/skills/context-graph/evals/files/5/services/ingest/retry.ts +20 -0
- package/packs/solution-architect/skills/context-graph/evals/files/6/README.md +4 -0
- package/packs/solution-architect/skills/context-graph/evals/files/6/docs/adr/0001-monorepo-layout.md +10 -0
- package/packs/solution-architect/skills/context-graph/evals/files/6/docs/adr/0002-billing-on-postgres.md +13 -0
- package/packs/solution-architect/skills/context-graph/evals/files/6/docs/adr/0004-ledger-sync-nightly-batch.md +9 -0
- package/packs/solution-architect/skills/context-graph/evals/files/6/docs/rfcs/RFC-0003-invoice-ids.md +9 -0
- package/packs/solution-architect/skills/context-graph/evals/files/6/docs/rfcs/RFC-0007-invoice-ids-ulid.md +11 -0
- package/packs/solution-architect/skills/context-graph/evals/files/6/docs/rfcs/RFC-0009-billing-event-store.md +15 -0
- package/packs/solution-architect/skills/context-graph/evals/files/6/docs/rfcs/RFC-0010-revenue-service.md +13 -0
- package/packs/solution-architect/skills/context-graph/evals/files/6/libs/money/ulid.py +8 -0
- package/packs/solution-architect/skills/context-graph/evals/files/6/services/billing/src/events.py +10 -0
- package/packs/solution-architect/skills/context-graph/evals/files/6/services/billing/src/store.py +10 -0
- package/packs/solution-architect/skills/context-graph/evals/files/6/services/invoicing/ids/generator.py +7 -0
- package/packs/solution-architect/skills/context-graph/evals/files/6/services/ledger/README.md +4 -0
- package/packs/solution-architect/skills/context-graph/evals/files/6/services/ledger/sync.py +3 -0
- package/packs/solution-architect/skills/context-graph/evals/files/7/README.md +4 -0
- package/packs/solution-architect/skills/context-graph/evals/files/7/db/queries/orders.sql +2 -0
- package/packs/solution-architect/skills/context-graph/evals/files/7/db/sharding/router.py +9 -0
- package/packs/solution-architect/skills/context-graph/evals/files/7/design/decisions/DD-001-postgres-primary.md +8 -0
- package/packs/solution-architect/skills/context-graph/evals/files/7/design/decisions/DD-002-kafka-event-bus.md +20 -0
- package/packs/solution-architect/skills/context-graph/evals/files/7/design/decisions/DD-003-no-orm.md +9 -0
- package/packs/solution-architect/skills/context-graph/evals/files/7/design/decisions/DD-006-tenant-sharding.md +9 -0
- package/packs/solution-architect/skills/context-graph/evals/files/7/design/decisions/DD-011-orm-for-admin-console.md +11 -0
- package/packs/solution-architect/skills/context-graph/evals/files/7/pipelines/fanout/router.py +4 -0
- package/packs/solution-architect/skills/context-graph/evals/files/7/pipelines/ingest/consumer.py +5 -0
- package/packs/solution-architect/skills/context-graph/evals/files/7/pipelines/ingest/topics.yaml +8 -0
- package/packs/solution-architect/skills/context-graph/evals/files/7/rfcs/0004-region-sharding.md +15 -0
- package/packs/solution-architect/skills/context-graph/evals/files/7/services/admin-console/models.py +11 -0
- package/packs/solution-architect/skills/context-graph/evals/files/7/services/orders/repo.py +9 -0
- package/packs/solution-architect/skills/context-graph/evals/files/8/README.md +11 -0
- package/packs/solution-architect/skills/context-graph/evals/files/8/api/ledger.proto +30 -0
- package/packs/solution-architect/skills/context-graph/evals/files/8/config/http.yaml +10 -0
- package/packs/solution-architect/skills/context-graph/evals/files/8/config/messaging.yaml +8 -0
- package/packs/solution-architect/skills/context-graph/evals/files/8/docs/adr/0001-record-architecture-decisions.md +20 -0
- package/packs/solution-architect/skills/context-graph/evals/files/8/docs/adr/0002-postgresql-for-ledger.md +22 -0
- package/packs/solution-architect/skills/context-graph/evals/files/8/docs/adr/0003-rabbitmq-domain-events.md +22 -0
- package/packs/solution-architect/skills/context-graph/evals/files/8/docs/adr/0004-integer-minor-units.md +32 -0
- package/packs/solution-architect/skills/context-graph/evals/files/8/docs/adr/0005-provider-http-retries.md +26 -0
- package/packs/solution-architect/skills/context-graph/evals/files/8/docs/adr/0006-openapi-generated-clients.md +20 -0
- package/packs/solution-architect/skills/context-graph/evals/files/8/docs/adr/0007-kafka-domain-events.md +25 -0
- package/packs/solution-architect/skills/context-graph/evals/files/8/docs/adr/0008-structured-logging.md +18 -0
- package/packs/solution-architect/skills/context-graph/evals/files/8/docs/rfcs/RFC-012-idempotency-keys.md +19 -0
- package/packs/solution-architect/skills/context-graph/evals/files/8/services/ledger/api/idempotency.py +19 -0
- package/packs/solution-architect/skills/context-graph/evals/files/8/services/ledger/api/server.py +35 -0
- package/packs/solution-architect/skills/context-graph/evals/files/8/services/ledger/clients/provider.py +33 -0
- package/packs/solution-architect/skills/context-graph/evals/files/8/services/ledger/db/migrations/0001_init.sql +14 -0
- package/packs/solution-architect/skills/context-graph/evals/files/8/services/ledger/db/migrations/0008_idempotency_keys.sql +5 -0
- package/packs/solution-architect/skills/context-graph/evals/files/8/services/ledger/db/migrations/0009_fx_entries.sql +4 -0
- package/packs/solution-architect/skills/context-graph/evals/files/8/services/ledger/events/publisher.py +28 -0
- package/packs/solution-architect/skills/context-graph/evals/files/8/services/ledger/logging.py +25 -0
- package/packs/solution-architect/skills/context-graph/evals/files/8/services/ledger/models/entry.py +16 -0
- package/packs/solution-architect/skills/context-graph/evals/files/9/.env.example +3 -0
- package/packs/solution-architect/skills/context-graph/evals/files/9/README.md +5 -0
- package/packs/solution-architect/skills/context-graph/evals/files/9/docker-compose.yml +18 -0
- package/packs/solution-architect/skills/context-graph/evals/files/9/docs/decisions/001-session-storage.md +21 -0
- package/packs/solution-architect/skills/context-graph/evals/files/9/docs/decisions/002-api-rate-limiting.md +32 -0
- package/packs/solution-architect/skills/context-graph/evals/files/9/docs/decisions/003-jwt-sessions.md +29 -0
- package/packs/solution-architect/skills/context-graph/evals/files/9/docs/decisions/004-catalog-cache.md +19 -0
- package/packs/solution-architect/skills/context-graph/evals/files/9/src/catalog/cache.ts +13 -0
- package/packs/solution-architect/skills/context-graph/evals/files/9/src/catalog/repository.ts +13 -0
- package/packs/solution-architect/skills/context-graph/evals/files/9/src/jobs/email.ts +3 -0
- package/packs/solution-architect/skills/context-graph/evals/files/9/src/jobs/export.ts +3 -0
- package/packs/solution-architect/skills/context-graph/evals/files/9/src/jobs/queue.ts +10 -0
- package/packs/solution-architect/skills/context-graph/evals/files/9/src/jobs/worker.ts +8 -0
- package/packs/solution-architect/skills/context-graph/evals/files/9/src/lib/db.ts +10 -0
- package/packs/solution-architect/skills/context-graph/evals/files/9/src/lib/redis.ts +8 -0
- package/packs/solution-architect/skills/context-graph/evals/files/9/src/middleware/rateLimit.ts +26 -0
- package/packs/solution-architect/skills/context-graph/evals/files/9/src/server.ts +17 -0
- package/packs/solution-architect/skills/context-graph/evals/files/9/src/session/store.ts +10 -0
- package/packs/solution-architect/skills/context-graph/examples/inputs/order-events/docs/adr/0004-kafka-for-order-events.md +19 -0
- package/packs/solution-architect/skills/context-graph/examples/inputs/order-events/docs/adr/0006-email-receipts.md +7 -0
- package/packs/solution-architect/skills/context-graph/examples/inputs/order-events/docs/adr/0009-sqs-for-order-events.md +19 -0
- package/packs/solution-architect/skills/context-graph/examples/inputs/order-events/services/notifications/receipts.py +0 -0
- package/packs/solution-architect/skills/context-graph/examples/inputs/order-events/services/orders/publisher.py +0 -0
- package/packs/solution-architect/skills/context-graph/examples/order-events.md +63 -0
- package/packs/solution-architect/skills/context-graph/manifest.json +33 -0
- package/packs/solution-architect/skills/context-graph/reference.md +104 -0
- package/packs/solution-architect/skills/context-graph/scripts/context_graph.py +634 -0
- package/packs/solution-architect/skills/event-contract-draft/manifest.json +1 -1
- package/packs/solution-architect/skills/event-contract-draft/scripts/check_asyncapi.py +14 -2
- package/packs/sre-oncall/skills/cloud-cost-review/manifest.json +1 -1
- package/packs/sre-oncall/skills/cloud-cost-review/scripts/cost_summary.py +2 -0
- package/packs/trackers/skills/backlog-triage/manifest.json +1 -1
- package/packs/trackers/skills/backlog-triage/scripts/triage_backlog.py +4 -1
- package/packs/trackers/skills/team-status-report/manifest.json +1 -1
- 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,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.
|
package/packs/solution-architect/skills/context-graph/evals/files/2/docs/adr/0007-feature-flags.md
ADDED
|
@@ -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.
|
|
File without changes
|
package/packs/solution-architect/skills/context-graph/evals/files/2/services/checkout/flags.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
FLAGS = {"new_basket": False}
|
package/packs/solution-architect/skills/context-graph/evals/files/2/services/checkout/publish.py
ADDED
|
File without changes
|
package/packs/solution-architect/skills/context-graph/evals/files/2/services/worker/consumer.py
ADDED
|
File without changes
|
|
@@ -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.
|
|
File without changes
|
package/packs/solution-architect/skills/context-graph/evals/files/3/services/payments/__init__.py
ADDED
|
File without changes
|
|
File without changes
|
|
@@ -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)
|
|
File without changes
|
|
@@ -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.
|
package/packs/solution-architect/skills/context-graph/evals/files/4/docs/adr/0007-remove-redis.md
ADDED
|
@@ -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
|
package/packs/solution-architect/skills/context-graph/evals/files/4/src/ledgerline/__init__.py
ADDED
|
File without changes
|
package/packs/solution-architect/skills/context-graph/evals/files/4/src/ledgerline/db/__init__.py
ADDED
|
@@ -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,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)
|
|
File without changes
|
package/packs/solution-architect/skills/context-graph/evals/files/4/src/ledgerline/scheduler/lock.py
ADDED
|
@@ -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()
|
package/packs/solution-architect/skills/context-graph/evals/files/5/rfcs/0001-service-layout.md
ADDED
|
@@ -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.
|
package/packs/solution-architect/skills/context-graph/evals/files/5/rfcs/0003-ingest-retry-policy.md
ADDED
|
@@ -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
|
+
}
|