skilldrop-cli 0.16.45 → 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/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
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# 8. Store vehicle telemetry in TimescaleDB
|
|
2
|
+
|
|
3
|
+
Date: 2023-04-11
|
|
4
|
+
|
|
5
|
+
## Status
|
|
6
|
+
|
|
7
|
+
Accepted
|
|
8
|
+
|
|
9
|
+
Supersedes [3. Store vehicle telemetry in Cassandra](0003-store-vehicle-telemetry-in-cassandra.md)
|
|
10
|
+
|
|
11
|
+
## Context
|
|
12
|
+
|
|
13
|
+
The Cassandra cluster from ADR 3 needs specialist operations knowledge
|
|
14
|
+
nobody on the team has, and geo queries over telemetry are now a product
|
|
15
|
+
requirement.
|
|
16
|
+
|
|
17
|
+
## Decision
|
|
18
|
+
|
|
19
|
+
Vehicle telemetry moves to TimescaleDB (a PostgreSQL extension) in a
|
|
20
|
+
separate database, `haulmark_telemetry`, written by `services/telemetry/`,
|
|
21
|
+
using a hypertable partitioned by time with PostGIS for geo queries.
|
|
22
|
+
Raw points are kept for 30 days and downsampled after that, as set out in
|
|
23
|
+
[10. Telemetry retention and downsampling](0010-telemetry-retention-and-downsampling.md).
|
|
24
|
+
|
|
25
|
+
## Consequences
|
|
26
|
+
|
|
27
|
+
One database technology to operate instead of two. Write throughput must be
|
|
28
|
+
watched as the fleet grows past 2,000 trucks.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# 9. Stateless sessions with signed cookies
|
|
2
|
+
|
|
3
|
+
Date: 2024-05-07
|
|
4
|
+
|
|
5
|
+
## Status
|
|
6
|
+
|
|
7
|
+
Accepted
|
|
8
|
+
|
|
9
|
+
Supersedes [4. Use Redis for session storage](0004-use-redis-for-session-storage.md)
|
|
10
|
+
|
|
11
|
+
## Context
|
|
12
|
+
|
|
13
|
+
Redis was the only stateful dependency of `services/api/` and caused two
|
|
14
|
+
outages in Q1 2024 when its primary failed over.
|
|
15
|
+
|
|
16
|
+
## Decision
|
|
17
|
+
|
|
18
|
+
Sessions are stateless: a signed, encrypted cookie carries the dispatcher's
|
|
19
|
+
user ID, role and an 8-hour expiry. Redis is removed from `services/api/`.
|
|
20
|
+
|
|
21
|
+
## Consequences
|
|
22
|
+
|
|
23
|
+
No session store to operate. Revoking a single session before expiry needs a
|
|
24
|
+
short deny-list, kept in `haulmark_core`.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""Dispatcher sessions for services/api (ADR 9): signed, encrypted cookies, no server store."""
|
|
2
|
+
import os
|
|
3
|
+
import time
|
|
4
|
+
|
|
5
|
+
from itsdangerous import URLSafeTimedSerializer
|
|
6
|
+
|
|
7
|
+
SECRET = os.environ.get("SESSION_SECRET", "placeholder-not-a-real-secret")
|
|
8
|
+
MAX_AGE_SECONDS = 8 * 3600
|
|
9
|
+
_serializer = URLSafeTimedSerializer(SECRET, salt="haulmark-session")
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def issue(user_id: str, role: str) -> str:
|
|
13
|
+
return _serializer.dumps({"uid": user_id, "role": role, "iat": int(time.time())})
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def read(cookie: str):
|
|
17
|
+
return _serializer.loads(cookie, max_age=MAX_AGE_SECONDS)
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"""Shipment persistence for services/shipments (ADR 2)."""
|
|
2
|
+
import os
|
|
3
|
+
|
|
4
|
+
import psycopg
|
|
5
|
+
|
|
6
|
+
DSN = os.environ.get("SHIPMENTS_DSN", "postgresql://haulmark:placeholder@localhost:5432/haulmark_core")
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def connect():
|
|
10
|
+
return psycopg.connect(DSN)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def get_shipment(conn, shipment_id: str):
|
|
14
|
+
with conn.cursor() as cur:
|
|
15
|
+
cur.execute(
|
|
16
|
+
"SELECT id, customer_id, status, custom_fields FROM shipments WHERE id = %s",
|
|
17
|
+
(shipment_id,),
|
|
18
|
+
)
|
|
19
|
+
return cur.fetchone()
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def mark_stop_delivered(conn, shipment_id: str, stop_id: str):
|
|
23
|
+
with conn.transaction():
|
|
24
|
+
with conn.cursor() as cur:
|
|
25
|
+
cur.execute("UPDATE stops SET status = 'delivered' WHERE id = %s", (stop_id,))
|
|
26
|
+
cur.execute(
|
|
27
|
+
"UPDATE shipments SET status = 'delivered' WHERE id = %s "
|
|
28
|
+
"AND NOT EXISTS (SELECT 1 FROM stops WHERE shipment_id = %s AND status <> 'delivered')",
|
|
29
|
+
(shipment_id, shipment_id),
|
|
30
|
+
)
|
package/packs/solution-architect/skills/context-graph/evals/files/15/services/telemetry/store.py
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"""Telemetry writer for services/telemetry (ADR 8)."""
|
|
2
|
+
import os
|
|
3
|
+
|
|
4
|
+
import psycopg
|
|
5
|
+
|
|
6
|
+
DSN = os.environ.get("TELEMETRY_DSN", "postgresql://haulmark:placeholder@localhost:5432/haulmark_telemetry")
|
|
7
|
+
|
|
8
|
+
SCHEMA = """
|
|
9
|
+
CREATE TABLE IF NOT EXISTS positions (
|
|
10
|
+
vehicle_id TEXT NOT NULL,
|
|
11
|
+
recorded_at TIMESTAMPTZ NOT NULL,
|
|
12
|
+
location GEOGRAPHY(POINT) NOT NULL
|
|
13
|
+
);
|
|
14
|
+
SELECT create_hypertable('positions', 'recorded_at', if_not_exists => TRUE);
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def write_position(conn, vehicle_id: str, recorded_at, lon: float, lat: float):
|
|
19
|
+
with conn.cursor() as cur:
|
|
20
|
+
cur.execute(
|
|
21
|
+
"INSERT INTO positions VALUES (%s, %s, ST_MakePoint(%s, %s)::geography)",
|
|
22
|
+
(vehicle_id, recorded_at, lon, lat),
|
|
23
|
+
)
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
---
|
|
2
|
+
status: accepted
|
|
3
|
+
date: 2023-05-22
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# RabbitMQ for background jobs
|
|
7
|
+
|
|
8
|
+
## Context and Problem Statement
|
|
9
|
+
|
|
10
|
+
Checkout sends receipts and stock updates inline, which slows the request.
|
|
11
|
+
|
|
12
|
+
## Decision Outcome
|
|
13
|
+
|
|
14
|
+
Chosen option: "RabbitMQ", because the hosting provider offers it managed. Jobs are consumed by
|
|
15
|
+
`services/worker/`, and `services/checkout/` only publishes.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# 3. A separate thumbnail service
|
|
2
|
+
|
|
3
|
+
Date: 2023-09-11
|
|
4
|
+
|
|
5
|
+
## Status
|
|
6
|
+
|
|
7
|
+
Accepted
|
|
8
|
+
|
|
9
|
+
## Context
|
|
10
|
+
|
|
11
|
+
Resizing product images inside the API blocks request threads.
|
|
12
|
+
|
|
13
|
+
## Decision
|
|
14
|
+
|
|
15
|
+
Image resizing runs in its own service, `services/thumbnailer/`, which consumes `resize` jobs
|
|
16
|
+
from the queue described in ADR-0002.
|
|
17
|
+
|
|
18
|
+
## Consequences
|
|
19
|
+
|
|
20
|
+
The API never touches image bytes after upload.
|
|
@@ -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,))
|