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,33 @@
|
|
|
1
|
+
---
|
|
2
|
+
rfc: 0001
|
|
3
|
+
title: Idempotency keys on every charge request
|
|
4
|
+
status: accepted
|
|
5
|
+
date: 2024-02-12
|
|
6
|
+
authors: [mira.okafor]
|
|
7
|
+
governs:
|
|
8
|
+
- src/payments/charge.ts
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# RFC-0001: Idempotency keys on every charge request
|
|
12
|
+
|
|
13
|
+
## Problem
|
|
14
|
+
|
|
15
|
+
Mobile clients retry on flaky networks. Without a dedupe key, a retried
|
|
16
|
+
`POST /charges` can bill a customer twice. We had 14 double-charge tickets in
|
|
17
|
+
January 2024.
|
|
18
|
+
|
|
19
|
+
## Decision
|
|
20
|
+
|
|
21
|
+
Every `POST /charges` request MUST carry an `Idempotency-Key` header. The
|
|
22
|
+
charge handler rejects requests without one (HTTP 400) and stores the key with
|
|
23
|
+
the charge for 24 hours. A repeat key returns the original response instead of
|
|
24
|
+
creating a new charge.
|
|
25
|
+
|
|
26
|
+
The check lives in the charge handler itself, not in the retry layer: the
|
|
27
|
+
retry layer only sees our own outbound retries to the card processor, not
|
|
28
|
+
inbound client retries.
|
|
29
|
+
|
|
30
|
+
## Consequences
|
|
31
|
+
|
|
32
|
+
- Clients must generate a UUID per logical charge.
|
|
33
|
+
- The `idempotency_keys` table grows; rows expire after 24 hours.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
---
|
|
2
|
+
rfc: 0002
|
|
3
|
+
title: Fixed-interval retries to the card processor
|
|
4
|
+
status: superseded
|
|
5
|
+
superseded_by: 0005
|
|
6
|
+
date: 2024-02-20
|
|
7
|
+
authors: [dev.raman]
|
|
8
|
+
governs:
|
|
9
|
+
- src/payments/retry.ts
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# RFC-0002: Fixed-interval retries to the card processor
|
|
13
|
+
|
|
14
|
+
## Problem
|
|
15
|
+
|
|
16
|
+
Calls to the card processor occasionally time out. We need a retry policy.
|
|
17
|
+
|
|
18
|
+
## Decision
|
|
19
|
+
|
|
20
|
+
Retry failed processor calls up to 3 times with a fixed 2-second delay
|
|
21
|
+
between attempts.
|
|
22
|
+
|
|
23
|
+
## Consequences
|
|
24
|
+
|
|
25
|
+
- Simple to reason about.
|
|
26
|
+
- Under a processor outage every instance retries in lockstep.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
---
|
|
2
|
+
rfc: 0003
|
|
3
|
+
title: Represent money as integer minor units
|
|
4
|
+
status: accepted
|
|
5
|
+
date: 2024-03-04
|
|
6
|
+
authors: [mira.okafor]
|
|
7
|
+
governs:
|
|
8
|
+
- src/payments/amount.ts
|
|
9
|
+
- src/payments/charge.ts
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# RFC-0003: Represent money as integer minor units
|
|
13
|
+
|
|
14
|
+
## Problem
|
|
15
|
+
|
|
16
|
+
A refund of 19.99 was stored as 19.989999999999998 after a floating-point
|
|
17
|
+
round trip, and the ledger failed to reconcile.
|
|
18
|
+
|
|
19
|
+
## Decision
|
|
20
|
+
|
|
21
|
+
All monetary amounts inside the service and on the wire are integers in the
|
|
22
|
+
currency's minor unit (cents for USD, pence for GBP), in a field named
|
|
23
|
+
`amountMinor`, paired with an ISO 4217 `currency` code. No decimal or
|
|
24
|
+
floating-point money fields anywhere in `src/payments/`. Conversion to a
|
|
25
|
+
display string happens only at the edge, via `formatAmount()` in
|
|
26
|
+
`src/payments/amount.ts`.
|
|
27
|
+
|
|
28
|
+
## Consequences
|
|
29
|
+
|
|
30
|
+
- API consumers must send `amountMinor: 1999`, not `amount: 19.99`.
|
|
31
|
+
- Currencies with three minor digits (e.g. KWD) work without special cases.
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
---
|
|
2
|
+
rfc: 0004
|
|
3
|
+
title: HMAC-SHA256 signatures on outbound webhooks
|
|
4
|
+
status: accepted
|
|
5
|
+
date: 2024-04-15
|
|
6
|
+
authors: [dev.raman]
|
|
7
|
+
governs:
|
|
8
|
+
- src/webhooks/sign.ts
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# RFC-0004: HMAC-SHA256 signatures on outbound webhooks
|
|
12
|
+
|
|
13
|
+
## Problem
|
|
14
|
+
|
|
15
|
+
Merchants cannot tell a genuine `charge.succeeded` webhook from a forged one.
|
|
16
|
+
|
|
17
|
+
## Decision
|
|
18
|
+
|
|
19
|
+
Every outbound webhook carries a `Ledgerline-Signature` header: an
|
|
20
|
+
HMAC-SHA256 of `timestamp + "." + body`, keyed with the merchant's webhook
|
|
21
|
+
secret. Signing is implemented in one place, `src/webhooks/sign.ts`, and every
|
|
22
|
+
sender must call `signPayload()` from it.
|
|
23
|
+
|
|
24
|
+
## Consequences
|
|
25
|
+
|
|
26
|
+
- Merchants verify with a constant-time compare.
|
|
27
|
+
- Rotating a merchant secret invalidates in-flight retries.
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
---
|
|
2
|
+
rfc: 0005
|
|
3
|
+
title: Exponential backoff with full jitter for processor retries
|
|
4
|
+
status: accepted
|
|
5
|
+
supersedes: 0002
|
|
6
|
+
date: 2024-06-03
|
|
7
|
+
authors: [dev.raman, mira.okafor]
|
|
8
|
+
governs:
|
|
9
|
+
- src/payments/retry.ts
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# RFC-0005: Exponential backoff with full jitter for processor retries
|
|
13
|
+
|
|
14
|
+
## Problem
|
|
15
|
+
|
|
16
|
+
During the processor outage on 2024-05-21, RFC-0002's fixed 2-second retries
|
|
17
|
+
made all 40 instances hit the processor in synchronised waves, extending the
|
|
18
|
+
outage on our side by about 25 minutes.
|
|
19
|
+
|
|
20
|
+
## Decision
|
|
21
|
+
|
|
22
|
+
Replace RFC-0002. Retry failed processor calls up to 4 times using
|
|
23
|
+
exponential backoff with full jitter: before attempt `n`, wait a random delay
|
|
24
|
+
between 0 and `min(8000, 250 * 2^n)` milliseconds. Never retry a 4xx response
|
|
25
|
+
from the processor.
|
|
26
|
+
|
|
27
|
+
## Consequences
|
|
28
|
+
|
|
29
|
+
- Retries spread out; no lockstep under outage.
|
|
30
|
+
- Worst-case added latency is bounded at roughly 15 seconds.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
---
|
|
2
|
+
rfc: 0006
|
|
3
|
+
title: Move idempotency handling to the API gateway
|
|
4
|
+
status: proposed
|
|
5
|
+
date: 2024-09-30
|
|
6
|
+
authors: [tomas.vey]
|
|
7
|
+
governs:
|
|
8
|
+
- src/payments/charge.ts
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# RFC-0006: Move idempotency handling to the API gateway
|
|
12
|
+
|
|
13
|
+
## Problem
|
|
14
|
+
|
|
15
|
+
Three services now implement their own idempotency-key tables, each slightly
|
|
16
|
+
differently.
|
|
17
|
+
|
|
18
|
+
## Proposal
|
|
19
|
+
|
|
20
|
+
Enforce `Idempotency-Key` once at the API gateway and remove the per-service
|
|
21
|
+
checks, including the one RFC-0001 put in the charge handler.
|
|
22
|
+
|
|
23
|
+
## Open questions
|
|
24
|
+
|
|
25
|
+
- The gateway team has not yet committed to a delivery date.
|
|
26
|
+
- How are responses replayed for keys the gateway has seen?
|
|
27
|
+
|
|
28
|
+
Not yet reviewed. Until this is accepted, RFC-0001 stands.
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# PR #412 — "Simplify charge path" (author: tomas.vey)
|
|
2
|
+
|
|
3
|
+
Description from the author:
|
|
4
|
+
> Cleanup of the charge path. Retries are back to the simple policy from RFC-0002,
|
|
5
|
+
> which is much easier to reason about. Dedupe now happens in the retry layer so the
|
|
6
|
+
> header check in the handler is redundant. All in line with our RFCs.
|
|
7
|
+
|
|
8
|
+
## Files changed
|
|
9
|
+
|
|
10
|
+
### src/payments/charge.ts (+9 / -14)
|
|
11
|
+
- `ChargeRequest.amountMinor: number` renamed to `amount: number`; value is now
|
|
12
|
+
decimal major units (e.g. `19.99`) "to match what the mobile app sends".
|
|
13
|
+
- Removed the `Idempotency-Key` header check and the `idempotencyStore.get/put`
|
|
14
|
+
calls. Comment added: `// dedupe handled by retry layer`.
|
|
15
|
+
- `assertMinorUnits(req.amountMinor)` call removed.
|
|
16
|
+
|
|
17
|
+
### src/payments/retry.ts (+6 / -12)
|
|
18
|
+
- `MAX_ATTEMPTS` 4 -> 3.
|
|
19
|
+
- Backoff replaced with a fixed `await sleep(2000)` between attempts.
|
|
20
|
+
- 4xx short-circuit kept.
|
|
21
|
+
- Header comment changed to `// Processor retry policy: RFC-0002`.
|
|
22
|
+
|
|
23
|
+
### src/webhooks/sign.ts -> src/webhooks/signature.ts (rename, no content change)
|
|
24
|
+
- `send.ts` import updated to `./signature`.
|
|
25
|
+
|
|
26
|
+
### src/webhooks/send.ts (+1 / -1)
|
|
27
|
+
- `console.log` replaced with `logger.info` (same fields).
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// RFC-0003: money is integer minor units.
|
|
2
|
+
export function assertMinorUnits(amountMinor: number): void {
|
|
3
|
+
if (!Number.isInteger(amountMinor) || amountMinor < 0) {
|
|
4
|
+
throw new Error("amountMinor must be a non-negative integer");
|
|
5
|
+
}
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function formatAmount(amountMinor: number, currency: string, digits = 2): string {
|
|
9
|
+
return `${(amountMinor / 10 ** digits).toFixed(digits)} ${currency}`;
|
|
10
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { retryProcessorCall } from "./retry";
|
|
2
|
+
import { assertMinorUnits } from "./amount";
|
|
3
|
+
import { idempotencyStore } from "./idempotency";
|
|
4
|
+
import { sendWebhook } from "../webhooks/send";
|
|
5
|
+
|
|
6
|
+
export interface ChargeRequest {
|
|
7
|
+
amountMinor: number; // integer minor units, see RFC-0003
|
|
8
|
+
currency: string; // ISO 4217
|
|
9
|
+
cardToken: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export async function createCharge(req: ChargeRequest, headers: Record<string, string>) {
|
|
13
|
+
const key = headers["idempotency-key"];
|
|
14
|
+
if (!key) {
|
|
15
|
+
return { status: 400, body: { error: "Idempotency-Key header is required" } }; // RFC-0001
|
|
16
|
+
}
|
|
17
|
+
const previous = await idempotencyStore.get(key);
|
|
18
|
+
if (previous) return previous;
|
|
19
|
+
|
|
20
|
+
assertMinorUnits(req.amountMinor);
|
|
21
|
+
const result = await retryProcessorCall(() => processor.charge(req));
|
|
22
|
+
const response = { status: 201, body: result };
|
|
23
|
+
await idempotencyStore.put(key, response, 24 * 3600);
|
|
24
|
+
await sendWebhook("charge.succeeded", result);
|
|
25
|
+
return response;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
declare const processor: { charge(req: ChargeRequest): Promise<unknown> };
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// Processor retry policy: RFC-0005 (supersedes RFC-0002).
|
|
2
|
+
const MAX_ATTEMPTS = 4;
|
|
3
|
+
const BASE_MS = 250;
|
|
4
|
+
const CAP_MS = 8000;
|
|
5
|
+
|
|
6
|
+
export async function retryProcessorCall<T>(fn: () => Promise<T>): Promise<T> {
|
|
7
|
+
let lastErr: unknown;
|
|
8
|
+
for (let n = 0; n < MAX_ATTEMPTS; n++) {
|
|
9
|
+
try {
|
|
10
|
+
return await fn();
|
|
11
|
+
} catch (err: any) {
|
|
12
|
+
if (err?.status >= 400 && err?.status < 500) throw err; // never retry 4xx
|
|
13
|
+
lastErr = err;
|
|
14
|
+
const ceiling = Math.min(CAP_MS, BASE_MS * 2 ** n);
|
|
15
|
+
await sleep(Math.random() * ceiling); // full jitter
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
throw lastErr;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { signPayload } from "./sign";
|
|
2
|
+
|
|
3
|
+
export async function sendWebhook(event: string, payload: unknown): Promise<void> {
|
|
4
|
+
const body = JSON.stringify({ event, payload });
|
|
5
|
+
const ts = Math.floor(Date.now() / 1000);
|
|
6
|
+
const signature = signPayload(process.env.WEBHOOK_SECRET ?? "placeholder-secret", ts, body);
|
|
7
|
+
console.log("webhook", event, signature.slice(0, 8));
|
|
8
|
+
// HTTP delivery omitted in this excerpt
|
|
9
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { createHmac } from "crypto";
|
|
2
|
+
|
|
3
|
+
// RFC-0004: the single place outbound webhooks are signed.
|
|
4
|
+
export function signPayload(secret: string, timestamp: number, body: string): string {
|
|
5
|
+
return createHmac("sha256", secret).update(`${timestamp}.${body}`).digest("hex");
|
|
6
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Services deployed to the production cluster
|
|
2
|
+
services:
|
|
3
|
+
- name: shipments
|
|
4
|
+
path: services/shipments
|
|
5
|
+
datastores: [postgres:haulmark_core]
|
|
6
|
+
- name: telemetry
|
|
7
|
+
path: services/telemetry
|
|
8
|
+
datastores: [postgres:haulmark_telemetry]
|
|
9
|
+
- name: api
|
|
10
|
+
path: services/api
|
|
11
|
+
datastores: [postgres:haulmark_core]
|
|
12
|
+
- name: dashboard
|
|
13
|
+
path: web/dashboard
|
|
14
|
+
datastores: []
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# 1. Record architecture decisions
|
|
2
|
+
|
|
3
|
+
Date: 2021-01-18
|
|
4
|
+
|
|
5
|
+
## Status
|
|
6
|
+
|
|
7
|
+
Accepted
|
|
8
|
+
|
|
9
|
+
## Context
|
|
10
|
+
|
|
11
|
+
We need to record the architectural decisions made on Haulmark.
|
|
12
|
+
|
|
13
|
+
## Decision
|
|
14
|
+
|
|
15
|
+
We will use Architecture Decision Records, as described by Michael Nygard,
|
|
16
|
+
kept in `doc/adr/`.
|
|
17
|
+
|
|
18
|
+
## Consequences
|
|
19
|
+
|
|
20
|
+
See Michael Nygard's article "Documenting Architecture Decisions".
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# 2. Use PostgreSQL for shipment data
|
|
2
|
+
|
|
3
|
+
Date: 2021-02-10
|
|
4
|
+
|
|
5
|
+
## Status
|
|
6
|
+
|
|
7
|
+
Accepted
|
|
8
|
+
|
|
9
|
+
## Context
|
|
10
|
+
|
|
11
|
+
Shipments, stops and consignees are relational and need transactional
|
|
12
|
+
updates when a stop is marked delivered. The team already runs PostgreSQL 13.
|
|
13
|
+
|
|
14
|
+
## Decision
|
|
15
|
+
|
|
16
|
+
Shipment data lives in a single PostgreSQL database, `haulmark_core`, owned by
|
|
17
|
+
`services/shipments/`. Other services read shipment data only through the
|
|
18
|
+
shipments API, never by connecting to the database directly.
|
|
19
|
+
|
|
20
|
+
## Consequences
|
|
21
|
+
|
|
22
|
+
Strong consistency for status transitions. Schema migrations are needed for
|
|
23
|
+
new shipment fields.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# 3. Store vehicle telemetry in Cassandra
|
|
2
|
+
|
|
3
|
+
Date: 2021-06-02
|
|
4
|
+
|
|
5
|
+
## Status
|
|
6
|
+
|
|
7
|
+
Superseded by [8. Store vehicle telemetry in TimescaleDB](0008-store-vehicle-telemetry-in-timescaledb.md)
|
|
8
|
+
|
|
9
|
+
## Context
|
|
10
|
+
|
|
11
|
+
Each truck reports GPS position every 5 seconds. At 1,200 trucks this is
|
|
12
|
+
about 20 million rows a day, too much write volume for `haulmark_core`.
|
|
13
|
+
|
|
14
|
+
## Decision
|
|
15
|
+
|
|
16
|
+
Vehicle telemetry is written to a 3-node Cassandra cluster by
|
|
17
|
+
`services/telemetry/`, partitioned by vehicle and day.
|
|
18
|
+
|
|
19
|
+
## Consequences
|
|
20
|
+
|
|
21
|
+
High write throughput. Ad-hoc queries (e.g. "all trucks within 5 km of the
|
|
22
|
+
depot yesterday") are hard to express.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# 4. Use Redis for session storage
|
|
2
|
+
|
|
3
|
+
Date: 2021-07-19
|
|
4
|
+
|
|
5
|
+
## Status
|
|
6
|
+
|
|
7
|
+
Accepted
|
|
8
|
+
|
|
9
|
+
## Context
|
|
10
|
+
|
|
11
|
+
The dispatcher web app needs server-side sessions shared across the API
|
|
12
|
+
instances in `services/api/`.
|
|
13
|
+
|
|
14
|
+
## Decision
|
|
15
|
+
|
|
16
|
+
Sessions are stored in Redis with a 12-hour TTL, keyed by an opaque session
|
|
17
|
+
ID held in a cookie.
|
|
18
|
+
|
|
19
|
+
## Consequences
|
|
20
|
+
|
|
21
|
+
One more piece of infrastructure to run. Logging out everywhere is a single
|
|
22
|
+
key delete.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# 5. Use React for the dispatcher dashboard
|
|
2
|
+
|
|
3
|
+
Date: 2021-09-06
|
|
4
|
+
|
|
5
|
+
## Status
|
|
6
|
+
|
|
7
|
+
Accepted
|
|
8
|
+
|
|
9
|
+
## Context
|
|
10
|
+
|
|
11
|
+
The dispatcher dashboard is moving off server-rendered templates.
|
|
12
|
+
|
|
13
|
+
## Decision
|
|
14
|
+
|
|
15
|
+
We will build the dashboard in React.
|
|
16
|
+
|
|
17
|
+
## Consequences
|
|
18
|
+
|
|
19
|
+
The front-end team needs a separate build pipeline.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# 6. Archive delivered shipments to object storage
|
|
2
|
+
|
|
3
|
+
Date: 2022-03-14
|
|
4
|
+
|
|
5
|
+
## Status
|
|
6
|
+
|
|
7
|
+
Accepted
|
|
8
|
+
|
|
9
|
+
## Context
|
|
10
|
+
|
|
11
|
+
`haulmark_core` has grown to 900 GB, most of it shipments delivered more
|
|
12
|
+
than a year ago that are only read for audits.
|
|
13
|
+
|
|
14
|
+
## Decision
|
|
15
|
+
|
|
16
|
+
A nightly job in `services/archiver/` moves shipments delivered more than
|
|
17
|
+
90 days ago out of `haulmark_core` into S3-compatible object storage as
|
|
18
|
+
Parquet files, one file per depot per day, and deletes them from PostgreSQL.
|
|
19
|
+
|
|
20
|
+
## Consequences
|
|
21
|
+
|
|
22
|
+
`haulmark_core` stays small. Audit queries over archived shipments go
|
|
23
|
+
through the object store, not the shipments API.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# 7. Move shipment records to MongoDB
|
|
2
|
+
|
|
3
|
+
Date: 2022-08-22
|
|
4
|
+
|
|
5
|
+
## Status
|
|
6
|
+
|
|
7
|
+
Accepted
|
|
8
|
+
|
|
9
|
+
## Context
|
|
10
|
+
|
|
11
|
+
Enterprise customers want custom fields on shipments (purchase order numbers,
|
|
12
|
+
temperature bands, hazmat codes) that differ per customer. Every new field
|
|
13
|
+
currently needs a PostgreSQL migration.
|
|
14
|
+
|
|
15
|
+
## Decision
|
|
16
|
+
|
|
17
|
+
Shipment records will be stored as documents in MongoDB, owned by
|
|
18
|
+
`services/shipments/`, so per-customer fields need no schema migration.
|
|
19
|
+
|
|
20
|
+
## Consequences
|
|
21
|
+
|
|
22
|
+
Status transitions that touch several stops lose multi-row transactions
|
|
23
|
+
unless we use MongoDB multi-document transactions.
|
|
@@ -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.
|