skilldrop-cli 1.0.1 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/packs/core/pack.json +2 -2
- package/packs/{solution-architect → core}/skills/context-graph/SKILL.md +10 -4
- package/packs/{solution-architect → core}/skills/context-graph/evals/eval_queries.json +4 -1
- package/packs/{solution-architect → core}/skills/context-graph/evals/evals.json +30 -0
- package/packs/core/skills/context-graph/evals/files/16/README.md +4 -0
- package/packs/core/skills/context-graph/evals/files/16/docs/adr/0001-record-architecture-decisions.md +12 -0
- package/packs/core/skills/context-graph/evals/files/16/docs/adr/0002-postgres-for-the-ledger.md +11 -0
- package/packs/core/skills/context-graph/evals/files/16/docs/adr/0003-redis-for-ephemeral-state.md +12 -0
- package/packs/core/skills/context-graph/evals/files/16/docs/adr/0004-idempotency-keys-for-payment-creation.md +28 -0
- package/packs/core/skills/context-graph/evals/files/16/git-history.json +62 -0
- package/packs/core/skills/context-graph/evals/files/16/services/payments/api/create.py +7 -0
- package/packs/core/skills/context-graph/evals/files/16/services/payments/ledger.py +9 -0
- package/packs/core/skills/context-graph/evals/files/17/README.md +5 -0
- package/packs/core/skills/context-graph/evals/files/17/docs/adr/0001-record-architecture-decisions.md +9 -0
- package/packs/core/skills/context-graph/evals/files/17/docs/adr/0002-email-via-smtp-relay.md +9 -0
- package/packs/core/skills/context-graph/evals/files/17/docs/adr/0003-rabbitmq-for-async-messaging.md +9 -0
- package/packs/core/skills/context-graph/evals/files/17/docs/adr/0004-email-via-provider-api.md +11 -0
- package/packs/core/skills/context-graph/evals/files/17/docs/adr/0005-server-side-template-rendering.md +10 -0
- package/packs/core/skills/context-graph/evals/files/17/docs/adr/0006-notification-retry-backoff.md +10 -0
- package/packs/core/skills/context-graph/evals/files/17/docs/adr/0007-kafka-for-event-streaming.md +12 -0
- package/packs/core/skills/context-graph/evals/files/17/docs/rfcs/RFC-012-unified-retry-policy.md +10 -0
- package/packs/core/skills/context-graph/evals/files/17/packages/notification-templates/README.md +4 -0
- package/packs/core/skills/context-graph/evals/files/17/packages/notification-templates/invoice_issued.html +2 -0
- package/packs/core/skills/context-graph/evals/files/17/packages/notification-templates/welcome.html +2 -0
- package/packs/core/skills/context-graph/evals/files/17/services/billing/retry.py +17 -0
- package/packs/core/skills/context-graph/evals/files/17/services/notifications/consumer.py +18 -0
- package/packs/core/skills/context-graph/evals/files/17/services/notifications/dispatch.py +21 -0
- package/packs/core/skills/context-graph/evals/files/17/services/notifications/email/sender.py +17 -0
- package/packs/core/skills/context-graph/evals/files/17/services/notifications/render.py +12 -0
- package/packs/core/skills/context-graph/evals/files/17/services/notifications/retry.py +14 -0
- package/packs/core/skills/context-graph/examples/inputs/order-events/services/notifications/receipts.py +0 -0
- package/packs/core/skills/context-graph/examples/inputs/order-events/services/orders/publisher.py +0 -0
- package/packs/{solution-architect → core}/skills/context-graph/manifest.json +3 -3
- package/packs/{solution-architect → core}/skills/context-graph/reference.md +28 -0
- package/packs/{solution-architect → core}/skills/context-graph/scripts/context_graph.py +105 -5
- package/packs/dev-team/loops/modernize/LOOP.md +11 -1
- package/packs/dev-team/loops/modernize/evals/evals.json +12 -0
- package/packs/dev-team/loops/modernize/loop.json +3 -3
- package/packs/solution-architect/loops/design/LOOP.md +14 -5
- package/packs/solution-architect/loops/design/evals/evals.json +12 -0
- package/packs/solution-architect/loops/design/loop.json +5 -5
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/1/docs/adr/0001-record-architecture-decisions.md +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/1/docs/adr/0002-postgres-as-primary-database.md +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/1/docs/adr/0003-use-redis-for-session-storage.md +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/1/docs/adr/0004-structured-json-logging.md +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/1/docs/adr/0005-session-lifetime-eight-hours.md +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/1/docs/adr/0006-move-sessions-to-postgres.md +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/1/docs/adr/0007-stateless-signed-session-cookies.md +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/1/services/api/__init__.py +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/1/services/api/db.py +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/1/services/api/logging_setup.py +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/1/services/auth/__init__.py +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/1/services/auth/cookie_sessions.py +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/1/services/auth/redis_sessions.py +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/10/README.md +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/10/docs/adr/0001-record-architecture-decisions.md +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/10/docs/adr/0004-service-auth-hmac-shared-keys.md +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/10/docs/adr/0007-secrets-from-vault-agent.md +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/10/docs/adr/0011-service-auth-mtls.md +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/10/services/billing-api/app.py +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/10/services/billing-api/auth/__init__.py +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/10/services/billing-api/auth/hmac_verify.py +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/10/services/billing-api/config/settings.py +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/10/services/ledger-worker/auth/__init__.py +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/10/services/ledger-worker/auth/mtls.py +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/11/README.md +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/11/app/models/base.py +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/11/app/models/line_item.py +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/11/app/models/order.py +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/11/eng/decisions/DR-003-money-as-minor-units.md +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/11/eng/decisions/DR-005-orders-typed-columns-only.md +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/11/eng/decisions/DR-009-line-items-immutable.md +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/11/eng/decisions/DR-014-optional-order-attributes-json.md +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/11/eng/decisions/DR-016-order-attributes-table.md +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/11/migrations/0007_create_orders.sql +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/11/migrations/0012_add_order_attributes.sql +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/12/README.md +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/12/config/delivery.yaml +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/12/config/export.yaml +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/12/config/partners.yaml +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/12/docs/adr/0001-record-architecture-decisions.md +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/12/docs/adr/0002-postgres-delivery-log.md +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/12/docs/adr/0003-redis-idempotency-keys.md +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/12/docs/adr/0004-retry-policy.md +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/12/docs/adr/0005-hmac-signed-payloads.md +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/12/docs/adr/0006-postgres-idempotency.md +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/12/docs/adr/0007-sftp-batch-export.md +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/12/docs/adr/0008-partner-config-in-yaml.md +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/12/docs/adr/0009-partner-config-admin-api.md +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/12/migrations/003_idempotency_keys.sql +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/12/migrations/004_partners.sql +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/12/src/delivery/dispatcher.py +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/12/src/delivery/retry.py +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/12/src/delivery/signing.py +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/12/src/export/s3_export.py +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/12/src/idempotency/pg_store.py +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/12/src/idempotency/redis_store.py +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/12/src/partners/loader.py +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/12/src/store/delivery_log.py +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/13/README.md +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/13/config/cache.yaml +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/13/config/settings.yaml +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/13/docs/decisions/RFC-008-pricing-from-erp.md +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/13/docs/decisions/RFC-012-pricing-cache.md +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/13/docs/decisions/RFC-015-checkout-price-lock.md +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/13/docs/decisions/RFC-019-pricing-service.md +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/13/shopfront/catalog/views.py +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/13/shopfront/checkout/lock.py +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/13/shopfront/pricing/cache.py +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/13/shopfront/pricing/client.py +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/14/docs/rfcs/0001-idempotency-keys.md +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/14/docs/rfcs/0002-processor-retry-policy.md +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/14/docs/rfcs/0003-money-in-minor-units.md +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/14/docs/rfcs/0004-webhook-signing.md +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/14/docs/rfcs/0005-retry-with-jitter.md +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/14/docs/rfcs/0006-idempotency-at-gateway.md +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/14/pr-summary.md +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/14/src/payments/amount.ts +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/14/src/payments/charge.ts +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/14/src/payments/idempotency.ts +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/14/src/payments/retry.ts +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/14/src/webhooks/send.ts +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/14/src/webhooks/sign.ts +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/15/deploy/services.yaml +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/15/doc/adr/0001-record-architecture-decisions.md +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/15/doc/adr/0002-use-postgresql-for-shipment-data.md +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/15/doc/adr/0003-store-vehicle-telemetry-in-cassandra.md +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/15/doc/adr/0004-use-redis-for-session-storage.md +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/15/doc/adr/0005-use-react-for-dispatcher-dashboard.md +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/15/doc/adr/0006-archive-delivered-shipments-to-object-storage.md +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/15/doc/adr/0007-move-shipment-records-to-mongodb.md +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/15/doc/adr/0008-store-vehicle-telemetry-in-timescaledb.md +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/15/doc/adr/0009-stateless-sessions-with-signed-cookies.md +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/15/services/api/session.py +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/15/services/shipments/db.py +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/15/services/shipments/migrations.sql +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/15/services/telemetry/store.py +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/15/web/dashboard/App.jsx +0 -0
- /package/packs/{solution-architect/skills/context-graph/evals/files/2/libs/money → core/skills/context-graph/evals/files/16/services/payments}/__init__.py +0 -0
- /package/packs/{solution-architect/skills/context-graph/evals/files/3/services/notifications → core/skills/context-graph/evals/files/16/services/payments/api}/__init__.py +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/2/docs/adr/0001-record-architecture-decisions.md +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/2/docs/adr/0002-rabbitmq-for-background-jobs.md +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/2/docs/adr/0003-thumbnail-service.md +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/2/docs/adr/0004-money-as-integer-minor-units.md +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/2/docs/adr/0005-worker-retries.md +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/2/docs/adr/0006-fx-amounts-with-four-decimals.md +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/2/docs/adr/0007-feature-flags.md +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/2/docs/adr/0008-retire-legacy-search.md +0 -0
- /package/packs/{solution-architect/skills/context-graph/evals/files/3/services/payments → core/skills/context-graph/evals/files/2/libs/money}/__init__.py +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/2/libs/money/amount.py +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/2/services/checkout/flags.py +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/2/services/checkout/publish.py +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/2/services/media/resize.py +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/2/services/worker/consumer.py +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/3/docs/rfcs/0001-how-we-write-rfcs.md +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/3/docs/rfcs/0002-payments-service-boundary.md +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/3/docs/rfcs/0003-webhook-retries-fixed-interval.md +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/3/docs/rfcs/0004-webhook-idempotency-keys.md +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/3/docs/rfcs/0005-notification-retries.md +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/3/docs/rfcs/0006-webhook-retry-backoff.md +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/3/docs/rfcs/0007-webhooks-on-the-job-queue.md +0 -0
- /package/packs/{solution-architect/skills/context-graph/evals/files/3/services/payments/webhooks → core/skills/context-graph/evals/files/3/services/notifications}/__init__.py +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/3/services/notifications/retry.py +0 -0
- /package/packs/{solution-architect/skills/context-graph/evals/files/4/src/ledgerline → core/skills/context-graph/evals/files/3/services/payments}/__init__.py +0 -0
- /package/packs/{solution-architect/skills/context-graph/evals/files/4/src/ledgerline/scheduler → core/skills/context-graph/evals/files/3/services/payments/webhooks}/__init__.py +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/3/services/payments/webhooks/retry.py +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/3/services/payments/webhooks/send.py +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/4/docs/adr/0001-record-architecture-decisions.md +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/4/docs/adr/0002-postgres-as-system-of-record.md +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/4/docs/adr/0004-redis-locks-for-scheduler.md +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/4/docs/adr/0007-remove-redis.md +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/4/docs/adr/0009-money-as-integer-minor-units.md +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/4/docs/onboarding.md +0 -0
- /package/packs/{solution-architect/skills/context-graph/examples/inputs/order-events/services/notifications/receipts.py → core/skills/context-graph/evals/files/4/src/ledgerline/__init__.py} +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/4/src/ledgerline/db/__init__.py +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/4/src/ledgerline/invoicing.py +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/4/src/ledgerline/money.py +0 -0
- /package/packs/{solution-architect/skills/context-graph/examples/inputs/order-events/services/orders/publisher.py → core/skills/context-graph/evals/files/4/src/ledgerline/scheduler/__init__.py} +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/4/src/ledgerline/scheduler/lock.py +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/4/src/ledgerline/scheduler/runner.py +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/5/lib/types/index.ts +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/5/rfcs/0001-service-layout.md +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/5/rfcs/0003-ingest-retry-policy.md +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/5/rfcs/0006-shared-retry-library.md +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/5/rfcs/0008-outbound-http-client.md +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/5/services/export/client.ts +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/5/services/export/retry.ts +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/5/services/ingest/client.ts +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/5/services/ingest/retry.ts +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/6/README.md +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/6/docs/adr/0001-monorepo-layout.md +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/6/docs/adr/0002-billing-on-postgres.md +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/6/docs/adr/0004-ledger-sync-nightly-batch.md +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/6/docs/rfcs/RFC-0003-invoice-ids.md +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/6/docs/rfcs/RFC-0007-invoice-ids-ulid.md +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/6/docs/rfcs/RFC-0009-billing-event-store.md +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/6/docs/rfcs/RFC-0010-revenue-service.md +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/6/libs/money/ulid.py +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/6/services/billing/src/events.py +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/6/services/billing/src/store.py +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/6/services/invoicing/ids/generator.py +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/6/services/ledger/README.md +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/6/services/ledger/sync.py +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/7/README.md +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/7/db/queries/orders.sql +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/7/db/sharding/router.py +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/7/design/decisions/DD-001-postgres-primary.md +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/7/design/decisions/DD-002-kafka-event-bus.md +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/7/design/decisions/DD-003-no-orm.md +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/7/design/decisions/DD-006-tenant-sharding.md +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/7/design/decisions/DD-011-orm-for-admin-console.md +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/7/pipelines/fanout/router.py +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/7/pipelines/ingest/consumer.py +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/7/pipelines/ingest/topics.yaml +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/7/rfcs/0004-region-sharding.md +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/7/services/admin-console/models.py +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/7/services/orders/repo.py +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/8/README.md +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/8/api/ledger.proto +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/8/config/http.yaml +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/8/config/messaging.yaml +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/8/docs/adr/0001-record-architecture-decisions.md +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/8/docs/adr/0002-postgresql-for-ledger.md +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/8/docs/adr/0003-rabbitmq-domain-events.md +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/8/docs/adr/0004-integer-minor-units.md +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/8/docs/adr/0005-provider-http-retries.md +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/8/docs/adr/0006-openapi-generated-clients.md +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/8/docs/adr/0007-kafka-domain-events.md +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/8/docs/adr/0008-structured-logging.md +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/8/docs/rfcs/RFC-012-idempotency-keys.md +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/8/services/ledger/api/idempotency.py +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/8/services/ledger/api/server.py +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/8/services/ledger/clients/provider.py +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/8/services/ledger/db/migrations/0001_init.sql +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/8/services/ledger/db/migrations/0008_idempotency_keys.sql +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/8/services/ledger/db/migrations/0009_fx_entries.sql +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/8/services/ledger/events/publisher.py +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/8/services/ledger/logging.py +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/8/services/ledger/models/entry.py +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/9/.env.example +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/9/README.md +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/9/docker-compose.yml +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/9/docs/decisions/001-session-storage.md +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/9/docs/decisions/002-api-rate-limiting.md +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/9/docs/decisions/003-jwt-sessions.md +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/9/docs/decisions/004-catalog-cache.md +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/9/src/catalog/cache.ts +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/9/src/catalog/repository.ts +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/9/src/jobs/email.ts +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/9/src/jobs/export.ts +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/9/src/jobs/queue.ts +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/9/src/jobs/worker.ts +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/9/src/lib/db.ts +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/9/src/lib/redis.ts +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/9/src/middleware/rateLimit.ts +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/9/src/server.ts +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/evals/files/9/src/session/store.ts +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/examples/inputs/order-events/docs/adr/0004-kafka-for-order-events.md +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/examples/inputs/order-events/docs/adr/0006-email-receipts.md +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/examples/inputs/order-events/docs/adr/0009-sqs-for-order-events.md +0 -0
- /package/packs/{solution-architect → core}/skills/context-graph/examples/order-events.md +0 -0
|
@@ -30,6 +30,7 @@ Every `.md` file in them, recursively, is a record, except:
|
|
|
30
30
|
| missing paths | a path that looks like the repository's (its first part is a top-level folder that exists) but doesn't exist |
|
|
31
31
|
| moved | for a missing path, an existing sibling with a close name (`services/ledger-sync` → `services/ledger`), where the code may have gone |
|
|
32
32
|
| commits | with `--git`: commits whose subject or body mentions the id, newest first, up to 20 |
|
|
33
|
+
| drift | with `--git`, for a current record that governs a path: the commits that changed those paths since the record's date, newest first. When the record has no date, they count from when its file was first committed. Each has its author and whether it cites the record. Also gives the authors and their counts, the last change, and when the record file was last edited. Merges are left out |
|
|
33
34
|
|
|
34
35
|
**Statuses, normalised:**
|
|
35
36
|
|
|
@@ -81,6 +82,11 @@ A file that isn't UTF-8 is skipped, with a note in the report.
|
|
|
81
82
|
"date": "2025-03-02", "path": "docs/adr/0004-….md",
|
|
82
83
|
"supersedes": [], "superseded_by": [], "amends": [], "cites": [], "cited_by": [],
|
|
83
84
|
"governs": ["services/orders/"], "missing_paths": [], "commits": [],
|
|
85
|
+
"drift": {"since": "2025-03-02", "since_from": "record", "count": 3, "not_citing_record": 2,
|
|
86
|
+
"authors": [{"name": "Ben", "commits": 2}, {"name": "Ana", "commits": 1}],
|
|
87
|
+
"last_change": "2026-06-04", "record_last_edited": "2025-03-02",
|
|
88
|
+
"commits": [{"sha": "a1b2c3d", "date": "2026-06-04", "author": "Ben", "subject": "…",
|
|
89
|
+
"cites_record": false}]},
|
|
84
90
|
"successors": ["ADR-0009"], "successors_only_claimed_by_successor": ["ADR-0009"],
|
|
85
91
|
"moved": {"services/kafka-bridge": ["services/bridge"]}
|
|
86
92
|
}],
|
|
@@ -91,9 +97,31 @@ A file that isn't UTF-8 is skipped, with a note in the report.
|
|
|
91
97
|
}
|
|
92
98
|
```
|
|
93
99
|
|
|
100
|
+
`drift` is `null` without `--git`, and for a record that isn't current or governs no path.
|
|
101
|
+
|
|
94
102
|
`successors` joins a record's own "superseded by" with every record that says it supersedes this
|
|
95
103
|
one. The second list holds the ones only the newer record records.
|
|
96
104
|
|
|
105
|
+
## Code changed since each decision (`--git`)
|
|
106
|
+
|
|
107
|
+
The full report gains a table of the current decisions whose governed code has changed since
|
|
108
|
+
they were made, most changed first:
|
|
109
|
+
|
|
110
|
+
| Column | Meaning |
|
|
111
|
+
|---|---|
|
|
112
|
+
| Decided | the record's date; `*` when it has none and the date is when the record was first committed |
|
|
113
|
+
| Commits since | commits to the governed paths since that date, merges left out |
|
|
114
|
+
| Not citing it | how many of those don't mention the record's id: the changes made without reference to it |
|
|
115
|
+
| By | the authors, with their counts, most first |
|
|
116
|
+
| Last change | the newest of those commits |
|
|
117
|
+
| Record last edited | the last commit to the record file itself |
|
|
118
|
+
|
|
119
|
+
`--why` adds a "Changed since" line under each current decision, and `--record` lists up to 10
|
|
120
|
+
of the commits. The Markdown table shows 15 rows; `--json` has them all.
|
|
121
|
+
|
|
122
|
+
A commit to a decision's code is a reason to check it, not drift by itself. Drift is a
|
|
123
|
+
requirement in the record that the code no longer meets.
|
|
124
|
+
|
|
97
125
|
## Limits
|
|
98
126
|
|
|
99
127
|
- **A decision made in a ticket or a chat, and never written down, isn't in the graph.** The
|
|
@@ -3,13 +3,14 @@
|
|
|
3
3
|
|
|
4
4
|
Reads decision records (ADRs, RFCs, design decisions) as Markdown, finds each one's id, title,
|
|
5
5
|
status and date, and the edges between them: what supersedes or amends what, which records cite
|
|
6
|
-
which, the files and folders each one governs, and, with --git, the commits that mention it
|
|
6
|
+
which, the files and folders each one governs, and, with --git, the commits that mention it and
|
|
7
|
+
the commits that changed each current decision's code since it was made, and who made them.
|
|
7
8
|
Then it checks the graph for what goes wrong as a codebase ages: a supersession only one side
|
|
8
9
|
records, a current decision pointing at code that is gone, two current decisions governing the
|
|
9
10
|
same path, references to records that don't exist.
|
|
10
11
|
|
|
11
12
|
python3 context_graph.py # find records under ./docs, check them
|
|
12
|
-
python3 context_graph.py --root repo --git # and the commits
|
|
13
|
+
python3 context_graph.py --root repo --git # and the commits behind each record, and since it
|
|
13
14
|
python3 context_graph.py --why src/auth/session.py
|
|
14
15
|
python3 context_graph.py --record ADR-0004
|
|
15
16
|
python3 context_graph.py --docs architecture/decisions --json -o graph.json --mermaid graph.mmd
|
|
@@ -191,7 +192,7 @@ def parse_record(path, root, text):
|
|
|
191
192
|
rec = {"id": rid, "title": title_of(meta, body, stem.replace("-", " ")), "status": status,
|
|
192
193
|
"status_text": raw_status, "date": date_of(meta, body), "path": rel,
|
|
193
194
|
"supersedes": [], "superseded_by": [], "amends": [], "cites": [], "governs": [],
|
|
194
|
-
"missing_paths": [], "commits": []}
|
|
195
|
+
"missing_paths": [], "commits": [], "drift": None}
|
|
195
196
|
rec["supersedes"] += list_ids(meta.get("supersedes", "")) + list_ids(meta.get("replaces", ""))
|
|
196
197
|
rec["superseded_by"] += list_ids(meta.get("superseded_by", "")) + list_ids(meta.get("replaced_by", ""))
|
|
197
198
|
rec["amends"] += list_ids(meta.get("amends", ""))
|
|
@@ -290,6 +291,69 @@ def git_commits(root, records, limit):
|
|
|
290
291
|
return None
|
|
291
292
|
|
|
292
293
|
|
|
294
|
+
def _git(root, *args):
|
|
295
|
+
"""stdout of a git command in root, or None when git is missing or the command fails."""
|
|
296
|
+
try:
|
|
297
|
+
out = subprocess.run(["git", "-C", root, *args], capture_output=True, text=True,
|
|
298
|
+
encoding="utf-8", errors="replace", timeout=60)
|
|
299
|
+
except (OSError, subprocess.SubprocessError):
|
|
300
|
+
return None
|
|
301
|
+
return out.stdout if out.returncode == 0 else None
|
|
302
|
+
|
|
303
|
+
|
|
304
|
+
def git_drift(root, records, limit):
|
|
305
|
+
"""For each current decision that governs code: the commits that changed that code since the
|
|
306
|
+
decision was made, who made them, and when the record itself was last edited. Code that keeps
|
|
307
|
+
changing under a record nobody revisits is where a decision quietly stops being true."""
|
|
308
|
+
for r in records:
|
|
309
|
+
if r["status"] not in CURRENT or r["successors"] or not r["governs"]:
|
|
310
|
+
continue
|
|
311
|
+
since, source = r["date"], "record"
|
|
312
|
+
if not since: # no date in the record: when the record was first committed
|
|
313
|
+
added = _git(root, "log", "--diff-filter=A", "--follow", "--date=short", "--format=%ad", "--", r["path"])
|
|
314
|
+
since, source = ((added or "").split() or [""])[-1], "git"
|
|
315
|
+
if not since:
|
|
316
|
+
r["drift"] = {"since": None, "note": "the record has no date and isn't committed, so changes since it can't be dated"}
|
|
317
|
+
continue
|
|
318
|
+
log = _git(root, "log", "--no-merges", "--since=%s 00:00" % since, "-n", str(limit), "--date=short",
|
|
319
|
+
"--format=%h%x1f%ad%x1f%an%x1f%s%x1f%b%x1e", "--", *[g.rstrip("/") or "." for g in r["governs"]])
|
|
320
|
+
commits = []
|
|
321
|
+
for entry in (log or "").split("\x1e"):
|
|
322
|
+
parts = entry.strip("\n").split("\x1f")
|
|
323
|
+
if len(parts) < 4:
|
|
324
|
+
continue
|
|
325
|
+
body = parts[4] if len(parts) > 4 else ""
|
|
326
|
+
commits.append({"sha": parts[0], "date": parts[1], "author": parts[2], "subject": parts[3][:120],
|
|
327
|
+
"cites_record": r["id"] in ids_in(parts[3] + "\n" + body)})
|
|
328
|
+
authors = {}
|
|
329
|
+
for c in commits:
|
|
330
|
+
authors[c["author"]] = authors.get(c["author"], 0) + 1
|
|
331
|
+
edited = (_git(root, "log", "-1", "--date=short", "--format=%ad", "--", r["path"]) or "").strip()
|
|
332
|
+
r["drift"] = {
|
|
333
|
+
"since": since, "since_from": source, "count": len(commits),
|
|
334
|
+
"not_citing_record": sum(1 for c in commits if not c["cites_record"]),
|
|
335
|
+
"authors": [{"name": n, "commits": k} for n, k in sorted(authors.items(), key=lambda x: (-x[1], x[0]))],
|
|
336
|
+
"last_change": commits[0]["date"] if commits else None,
|
|
337
|
+
"record_last_edited": edited or None,
|
|
338
|
+
"commits": commits[:20],
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
|
|
342
|
+
def drift_line(d):
|
|
343
|
+
"""One line: '12 commits by Ana (9), Ben (3) since 2026-02-10, the last on 2026-09-30'."""
|
|
344
|
+
if not d or d.get("since") is None:
|
|
345
|
+
return (d or {}).get("note", "")
|
|
346
|
+
if not d["count"]:
|
|
347
|
+
return "no commits have changed its code since %s" % d["since"]
|
|
348
|
+
who = ", ".join("%s (%d)" % (a["name"], a["commits"]) for a in d["authors"][:4]) + (" …" if len(d["authors"]) > 4 else "")
|
|
349
|
+
cite = "" if not d["not_citing_record"] else ("; %d don't cite the record" % d["not_citing_record"]
|
|
350
|
+
if d["not_citing_record"] < d["count"] else "; none cites the record")
|
|
351
|
+
return "%d commit%s by %s since %s%s, the last on %s%s" % (
|
|
352
|
+
d["count"], "" if d["count"] == 1 else "s", who, d["since"],
|
|
353
|
+
" (when the record was committed; it has no date)" if d.get("since_from") == "git" else "",
|
|
354
|
+
d["last_change"], cite)
|
|
355
|
+
|
|
356
|
+
|
|
293
357
|
SKIP_DIRS = {".git", "node_modules", "vendor", "dist", "build", ".venv", "venv", "__pycache__", "target"}
|
|
294
358
|
|
|
295
359
|
|
|
@@ -432,7 +496,8 @@ def why(records, path, root):
|
|
|
432
496
|
hits.sort(key=lambda h: (0 if live(h[0]) else 1, rank.get(h[0]["status"], 2),
|
|
433
497
|
-int(h[0]["date"].replace("-", "") or 0)))
|
|
434
498
|
return p, [{"id": r["id"], "title": r["title"], "status": r["status"], "date": r["date"], "path": r["path"],
|
|
435
|
-
"via": g, "superseded_by": sorted(set(succ.get(r["id"], []))), "current": live(r)
|
|
499
|
+
"via": g, "superseded_by": sorted(set(succ.get(r["id"], []))), "current": live(r),
|
|
500
|
+
"drift": r.get("drift")} for r, g in hits]
|
|
436
501
|
|
|
437
502
|
|
|
438
503
|
def mermaid(records):
|
|
@@ -479,6 +544,8 @@ def report(records, findings, note, query, dirs, root):
|
|
|
479
544
|
state = h["status"] + (", current" if h["current"] else "")
|
|
480
545
|
out.append("- **%s %s** (%s%s), via `%s`: %s" % (
|
|
481
546
|
h["id"], h["title"], state, ", " + h["date"] if h["date"] else "", h["via"], h["path"]))
|
|
547
|
+
if h.get("drift"):
|
|
548
|
+
out.append(" - Changed since: %s" % drift_line(h["drift"]))
|
|
482
549
|
if not cur:
|
|
483
550
|
out.append("\nNone of these is current: the decisions that governed it were superseded, "
|
|
484
551
|
"deprecated or never accepted.")
|
|
@@ -502,6 +569,13 @@ def report(records, findings, note, query, dirs, root):
|
|
|
502
569
|
out.append("- %s: %s" % (label, ", ".join(r[k])))
|
|
503
570
|
for c in r["commits"]:
|
|
504
571
|
out.append("- Commit %s %s: %s" % (c["sha"], c["date"], c["subject"]))
|
|
572
|
+
if r.get("drift"):
|
|
573
|
+
out.append("- Code changed since the decision: %s" % drift_line(r["drift"]))
|
|
574
|
+
if r["drift"].get("record_last_edited"):
|
|
575
|
+
out.append("- Record last edited: %s" % r["drift"]["record_last_edited"])
|
|
576
|
+
for c in r["drift"].get("commits", [])[:10]:
|
|
577
|
+
out.append(" - %s %s %s: %s%s" % (c["sha"], c["date"], c["author"], c["subject"],
|
|
578
|
+
" (cites it)" if c["cites_record"] else ""))
|
|
505
579
|
out.append("")
|
|
506
580
|
counts = {}
|
|
507
581
|
for r in records:
|
|
@@ -516,6 +590,28 @@ def report(records, findings, note, query, dirs, root):
|
|
|
516
590
|
out += ["- %s %s" % (rid, msg) for _, rid, msg in probs] or ["None."]
|
|
517
591
|
out.append("\n## Warnings (%d)\n" % len(warns))
|
|
518
592
|
out += ["- %s %s" % (rid, msg) for _, rid, msg in warns] or ["None."]
|
|
593
|
+
drifted = sorted((r for r in records if r.get("drift") and r["drift"].get("count")),
|
|
594
|
+
key=lambda r: (-r["drift"]["count"], r["id"]))
|
|
595
|
+
if not query and any(r.get("drift") for r in records):
|
|
596
|
+
out.append("\n## Code changed since each decision\n")
|
|
597
|
+
if not drifted:
|
|
598
|
+
out.append("No commit has changed the code a current decision governs since it was made.")
|
|
599
|
+
else:
|
|
600
|
+
out.append("Current decisions whose code has changed since they were made, most changed first. "
|
|
601
|
+
"Each is a place to check that the code still does what the record says.\n")
|
|
602
|
+
out.append("| Record | Decided | Commits since | Not citing it | By | Last change | Record last edited |")
|
|
603
|
+
out.append("|---|---|---|---|---|---|---|")
|
|
604
|
+
for r in drifted[:15]:
|
|
605
|
+
d = r["drift"]
|
|
606
|
+
out.append("| %s %s | %s%s | %d | %d | %s | %s | %s |" % (
|
|
607
|
+
r["id"], r["title"].replace("|", "/")[:50], d["since"], "*" if d["since_from"] == "git" else "",
|
|
608
|
+
d["count"], d["not_citing_record"],
|
|
609
|
+
", ".join("%s (%d)" % (a["name"].replace("|", "/"), a["commits"]) for a in d["authors"][:3])
|
|
610
|
+
+ (" …" if len(d["authors"]) > 3 else ""), d["last_change"], d["record_last_edited"] or "-"))
|
|
611
|
+
if len(drifted) > 15:
|
|
612
|
+
out.append("\n…and %d more; `--json` lists them all." % (len(drifted) - 15))
|
|
613
|
+
if any(r["drift"]["since_from"] == "git" for r in drifted[:15]):
|
|
614
|
+
out.append("\n\\* The record has no date, so this counts from when it was first committed.")
|
|
519
615
|
if not query:
|
|
520
616
|
out.append("\n## Graph\n")
|
|
521
617
|
out.append("| Record | Status | Date | Supersedes | Superseded by | Governs |")
|
|
@@ -536,7 +632,9 @@ def _main(argv=None):
|
|
|
536
632
|
q = ap.add_mutually_exclusive_group()
|
|
537
633
|
q.add_argument("--why", metavar="PATH", help="list the decisions that govern a file or folder")
|
|
538
634
|
q.add_argument("--record", metavar="ID", help="show one record and its neighbours, e.g. ADR-0004")
|
|
539
|
-
ap.add_argument("--git", action="store_true",
|
|
635
|
+
ap.add_argument("--git", action="store_true",
|
|
636
|
+
help="also read the commits that mention each record, and those that changed each current "
|
|
637
|
+
"decision's code since it was made")
|
|
540
638
|
ap.add_argument("--git-limit", type=int, default=5000)
|
|
541
639
|
ap.add_argument("--json", action="store_true", help="print the graph as JSON instead of Markdown")
|
|
542
640
|
ap.add_argument("--mermaid", metavar="FILE", help="also write a Mermaid flowchart of the graph")
|
|
@@ -579,6 +677,8 @@ def _main(argv=None):
|
|
|
579
677
|
claimed = [o["id"] for o in records if r["id"] in o["supersedes"]]
|
|
580
678
|
r["successors"] = sorted(set(r["superseded_by"]) | set(claimed))
|
|
581
679
|
r["successors_only_claimed_by_successor"] = sorted(set(claimed) - set(r["superseded_by"]))
|
|
680
|
+
if a.git and not any("no commits were read" in n for n in notes):
|
|
681
|
+
git_drift(root, records, a.git_limit)
|
|
582
682
|
# Guides and READMEs that still cite a superseded decision may still teach it.
|
|
583
683
|
by_id = {r["id"]: r for r in records}
|
|
584
684
|
docs = other_docs(root, {r["path"] for r in records})
|
|
@@ -16,7 +16,7 @@ can accept what the recordings don't cover and approve every intended change.
|
|
|
16
16
|
|
|
17
17
|
| # | Stage | Type | Skills | Gate |
|
|
18
18
|
|---|---|---|---|---|
|
|
19
|
-
| 1 | `survey` | generate | [`codebase-tour`](../../skills/codebase-tour/SKILL.md) | — |
|
|
19
|
+
| 1 | `survey` | generate | [`codebase-tour`](../../skills/codebase-tour/SKILL.md), [`context-graph`](../../../core/skills/context-graph/SKILL.md) | — |
|
|
20
20
|
| 2 | `plan` | generate | [`refactor-plan`](../../skills/refactor-plan/SKILL.md), [`migration-plan`](../../skills/migration-plan/SKILL.md) | — |
|
|
21
21
|
| 3 | `capture` | generate | [`behaviour-equivalence`](../../skills/behaviour-equivalence/SKILL.md) | — |
|
|
22
22
|
| 4 | `rebuild` | generate | [`feature-implement-loop`](../../skills/feature-implement-loop/SKILL.md) | — |
|
|
@@ -29,6 +29,15 @@ can accept what the recordings don't cover and approve every intended change.
|
|
|
29
29
|
must end with the boundary it will be swapped at (an HTTP API, a function signature, a
|
|
30
30
|
batch file, a queue) and every caller and side effect that crosses it, each cited to a
|
|
31
31
|
file. A boundary nobody can name means the component isn't ready to be replaced.
|
|
32
|
+
|
|
33
|
+
Then run `context-graph` on the same component, with git history. It finds the recorded
|
|
34
|
+
decisions that govern it: why it was built this way, what was tried before, and the
|
|
35
|
+
constraints the rewrite must keep. It also finds where the code has changed since those
|
|
36
|
+
decisions were made, and who changed it. Carry every constraint a current decision sets
|
|
37
|
+
into `plan`. A behaviour the old code has that contradicts its decision is a question for
|
|
38
|
+
the owner before `capture`: record it as it is, and ask whether the rewrite should keep it.
|
|
39
|
+
Each person who changed the code since the decision is worth asking what the records don't
|
|
40
|
+
say.
|
|
32
41
|
2. **Plan the work.**
|
|
33
42
|
- Restructuring the code in place → `refactor-plan`.
|
|
34
43
|
- Moving data, a schema or a platform → `migration-plan`.
|
|
@@ -122,3 +131,4 @@ The log holds verdicts only: never recordings, code or data.
|
|
|
122
131
|
- ❌ **Rebuilding everything, then comparing once.** Each planned step merges on its own and is compared again.
|
|
123
132
|
- ❌ **Calling the migration done at G10.** Equivalence on the recordings is evidence; the cutover is a person's decision, with a rollback.
|
|
124
133
|
- ❌ **Skipping `survey` because the old code is "well known".** The callers and side effects nobody remembers are the ones that break.
|
|
134
|
+
- ❌ **Rewriting without reading the decisions behind the old code.** A constraint someone recorded years ago, such as a retention period or an ordering guarantee, is easy to drop in a rewrite and expensive to rediscover in production.
|
|
@@ -22,6 +22,18 @@
|
|
|
22
22
|
"Says a new or wider normalisation rule is a return to capture, not a fix",
|
|
23
23
|
"Counts the return under the cap of 3"
|
|
24
24
|
]
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"id": "survey-reads-the-recorded-decisions",
|
|
28
|
+
"prompt": "Run the modernize loop. We're replacing services/billing (a Python 2 batch job) with a Go service. The repo has ADRs under docs/adr.",
|
|
29
|
+
"assertions": [
|
|
30
|
+
"Surveys the boundary and every caller and side effect that crosses it, each cited to a file, before planning",
|
|
31
|
+
"Reads the ADRs that govern services/billing and names the current one by id, status and date",
|
|
32
|
+
"Carries each constraint the current decision sets into the plan",
|
|
33
|
+
"Says where the code has changed since the decision was made, from the git history, and who changed it, or says git history wasn't available",
|
|
34
|
+
"Treats a behaviour that contradicts the recorded decision as a question for the owner before capture, not as something to fix silently in the rewrite",
|
|
35
|
+
"Does not invent decisions, ids, commits or people that are not in the repository"
|
|
36
|
+
]
|
|
25
37
|
}
|
|
26
38
|
]
|
|
27
39
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$comment": "RFC-0057. A legacy component replaced without changing what it does. The old system's behaviour is recorded before anything is rebuilt, a script decides G10 from the recordings, and a person decides the cutover, because only they can accept the coverage and the listed changes.",
|
|
3
3
|
"name": "modernize",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.2.0",
|
|
5
5
|
"description": "Replace or rewrite a legacy component without changing what it does: orient in the old code, plan the restructuring and the move, record the old system's behaviour before anything is rebuilt, rebuild in small steps, prove the new one gives the same results on the recordings, and cut over only when a person accepts the evidence. Use when the user is migrating, rewriting or porting a legacy service, batch job or module (COBOL to Java, Python 2 to 3, a monolith to a service, one framework to another), says \"run the modernize loop\", or asks how to replace old code without breaking what depends on it.",
|
|
6
6
|
"entrypoint": "LOOP.md",
|
|
7
7
|
"kind": "loop",
|
|
@@ -10,8 +10,8 @@
|
|
|
10
10
|
{
|
|
11
11
|
"id": "survey",
|
|
12
12
|
"type": "generate",
|
|
13
|
-
"intent": "Find the boundary the component is replaced at, what crosses it, who calls it, and the traps in the old code, each cited to a file.",
|
|
14
|
-
"skills": ["codebase-tour"]
|
|
13
|
+
"intent": "Find the boundary the component is replaced at, what crosses it, who calls it, and the traps in the old code, each cited to a file; then the recorded decisions that govern it, and which of them the code has drifted from.",
|
|
14
|
+
"skills": ["codebase-tour", "context-graph"]
|
|
15
15
|
},
|
|
16
16
|
{
|
|
17
17
|
"id": "plan",
|
|
@@ -14,16 +14,19 @@ a script, and why the decision is recorded only *after* the panel agrees.
|
|
|
14
14
|
|
|
15
15
|
| # | Stage | Type | Skills | Gate |
|
|
16
16
|
|---|---|---|---|---|
|
|
17
|
-
| 1 | `constrain` | generate | [`nfr-spec`](../../skills/nfr-spec/SKILL.md) | — |
|
|
17
|
+
| 1 | `constrain` | generate | [`context-graph`](../../../core/skills/context-graph/SKILL.md), [`nfr-spec`](../../skills/nfr-spec/SKILL.md) | — |
|
|
18
18
|
| 2 | `shape` | generate | [`design-doc`](../../skills/design-doc/SKILL.md), [`architecture-diagrams`](../../skills/architecture-diagrams/SKILL.md) | — |
|
|
19
19
|
| 3 | `threat` | verify | [`threat-model`](../../skills/threat-model/SKILL.md) | — |
|
|
20
20
|
| 4 | `ratify` | gate | [`council-review`](../../../core/skills/council-review/SKILL.md) | **G1** review |
|
|
21
|
-
| 5 | `record` | generate | [`adr-generator`](../../skills/adr-generator/SKILL.md) | — |
|
|
21
|
+
| 5 | `record` | generate | [`adr-generator`](../../skills/adr-generator/SKILL.md), [`context-graph`](../../../core/skills/context-graph/SKILL.md) | — |
|
|
22
22
|
|
|
23
23
|
## How to run this loop
|
|
24
24
|
|
|
25
|
-
1. **Constrain before shaping.** `
|
|
26
|
-
|
|
25
|
+
1. **Constrain before shaping.** First run `context-graph` where the design will land. List the
|
|
26
|
+
decisions already in force there, each by id, and any the code has drifted from. A new
|
|
27
|
+
design either keeps each one or says which it supersedes; when the repo has no records,
|
|
28
|
+
say so. Then run `nfr-spec`, always. A design produced before its quality targets exist
|
|
29
|
+
gets judged on taste, and taste does not survive a review panel.
|
|
27
30
|
2. **Shape prose and picture together.** `design-doc` and `architecture-diagrams` are one
|
|
28
31
|
artifact in two renderings. A diagram that disagrees with the doc is a defect in both.
|
|
29
32
|
3. **Attack it before ratifying.** `threat-model` consumes the design directly. Running it
|
|
@@ -33,7 +36,10 @@ a script, and why the decision is recorded only *after* the panel agrees.
|
|
|
33
36
|
`REVISE` returns to `shape`. `SPLIT` and `RECONSIDER` leave the loop.
|
|
34
37
|
5. **Record last.** `adr-generator` writes the decision *after* it is a decision, with the
|
|
35
38
|
rejected options and the reasoning that killed them. An ADR written before G1 is a proposal
|
|
36
|
-
wearing an ADR's clothes.
|
|
39
|
+
wearing an ADR's clothes. Then run `context-graph` again. Every record the new ADR
|
|
40
|
+
supersedes must now say "Superseded by" it, and the new ADR must name the code it governs.
|
|
41
|
+
A supersession recorded on one side only is the most common way a decision log starts to
|
|
42
|
+
lie.
|
|
37
43
|
|
|
38
44
|
## Verdicts
|
|
39
45
|
|
|
@@ -74,10 +80,13 @@ which end in `BLOCKED`.
|
|
|
74
80
|
- **The ADR names what was rejected and why.** A decision record with one option is a description of what happened, not a decision.
|
|
75
81
|
- **Dissent is recorded, not resolved away.** A panel that always reaches consensus is not seating real positions.
|
|
76
82
|
- **The diagram and the doc say the same thing.** They are checked against each other before G1, not after someone builds from the wrong one.
|
|
83
|
+
- **The design names the decisions already in force where it lands,** and which of them it keeps or supersedes.
|
|
84
|
+
- **After `record`, the records agree with each other:** each record the new ADR supersedes says so too.
|
|
77
85
|
|
|
78
86
|
## Anti-patterns to avoid
|
|
79
87
|
|
|
80
88
|
- ❌ **Writing the ADR before G1.** It pre-commits the panel to a decision it has not made, and every reviewer can feel it.
|
|
81
89
|
- ❌ **Running `threat-model` after ratification.** The panel then ratified an unattacked design, which is the failure this ordering exists to prevent.
|
|
82
90
|
- ❌ **Treating `PROCEED WITH CONDITIONS` as `PROCEED`.** The conditions must reach the ADR; dropping them is how a conditional approval silently becomes unconditional.
|
|
91
|
+
- ❌ **Designing over a decision nobody read.** A new design that quietly contradicts a current ADR leaves two decisions in force on one system, and the next reader can't tell which one wins.
|
|
83
92
|
- ❌ **Skipping `constrain` because the NFRs are "obvious".** Obvious targets are the ones nobody writes down and everybody remembers differently.
|
|
@@ -21,6 +21,18 @@
|
|
|
21
21
|
"Carries the named condition into the ADR as part of the decision",
|
|
22
22
|
"Proceeds to `record` rather than looping back to `shape`"
|
|
23
23
|
]
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"id": "reads-existing-decisions-and-closes-the-supersession",
|
|
27
|
+
"prompt": "Run the design loop for moving order events from Kafka to SQS. The repo has ADRs under docs/adr, and ADR-0004 chose Kafka.",
|
|
28
|
+
"assertions": [
|
|
29
|
+
"Before setting targets, lists the decisions already in force for order events by id, including ADR-0004",
|
|
30
|
+
"States whether the new design keeps or supersedes each of those decisions",
|
|
31
|
+
"Runs nfr-spec and expresses targets as numbers, not adjectives",
|
|
32
|
+
"Writes the ADR only after G1, and the new ADR says it supersedes ADR-0004",
|
|
33
|
+
"After writing the ADR, checks that ADR-0004 is marked superseded by the new record, and gives the exact edit when it isn't",
|
|
34
|
+
"Does not invent records, ids or dates that are not in the repository"
|
|
35
|
+
]
|
|
24
36
|
}
|
|
25
37
|
]
|
|
26
38
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$comment": "RFC-0028. The loop with the worst reversibility profile: a design mistake surfaces months later in code nobody wants to unwind. That is why its gate is a panel, not a script.",
|
|
3
3
|
"name": "design",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.3.0",
|
|
5
5
|
"description": "Take a ratified requirement to a recorded architectural decision — constraints, structure, diagrams, threats, then a panel that must agree before the decision is written down. Use when the user is designing a system, choosing an architecture, or asking how something should be built.",
|
|
6
6
|
"entrypoint": "LOOP.md",
|
|
7
7
|
"kind": "loop",
|
|
@@ -10,8 +10,8 @@
|
|
|
10
10
|
{
|
|
11
11
|
"id": "constrain",
|
|
12
12
|
"type": "generate",
|
|
13
|
-
"intent": "
|
|
14
|
-
"skills": ["nfr-spec"]
|
|
13
|
+
"intent": "Read the decisions already in force where the design will land, then name the quality targets before the structure, so the design is judged against numbers and the existing decisions rather than taste.",
|
|
14
|
+
"skills": ["context-graph", "nfr-spec"]
|
|
15
15
|
},
|
|
16
16
|
{
|
|
17
17
|
"id": "shape",
|
|
@@ -40,8 +40,8 @@
|
|
|
40
40
|
{
|
|
41
41
|
"id": "record",
|
|
42
42
|
"type": "generate",
|
|
43
|
-
"intent": "Write the decision down with its rejected alternatives, so the next team inherits the reasoning and not just the outcome.",
|
|
44
|
-
"skills": ["adr-generator"]
|
|
43
|
+
"intent": "Write the decision down with its rejected alternatives, so the next team inherits the reasoning and not just the outcome, then check the records still agree with each other.",
|
|
44
|
+
"skills": ["adr-generator", "context-graph"]
|
|
45
45
|
}
|
|
46
46
|
]
|
|
47
47
|
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
/package/packs/{solution-architect → core}/skills/context-graph/evals/files/1/services/api/db.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
/package/packs/{solution-architect → core}/skills/context-graph/evals/files/11/app/models/base.py
RENAMED
|
File without changes
|
|
File without changes
|
/package/packs/{solution-architect → core}/skills/context-graph/evals/files/11/app/models/order.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
/package/packs/{solution-architect → core}/skills/context-graph/evals/files/12/config/delivery.yaml
RENAMED
|
File without changes
|
/package/packs/{solution-architect → core}/skills/context-graph/evals/files/12/config/export.yaml
RENAMED
|
File without changes
|
/package/packs/{solution-architect → core}/skills/context-graph/evals/files/12/config/partners.yaml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
/package/packs/{solution-architect → core}/skills/context-graph/evals/files/12/src/delivery/retry.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
/package/packs/{solution-architect → core}/skills/context-graph/evals/files/13/config/cache.yaml
RENAMED
|
File without changes
|
/package/packs/{solution-architect → core}/skills/context-graph/evals/files/13/config/settings.yaml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
/package/packs/{solution-architect → core}/skills/context-graph/evals/files/14/pr-summary.md
RENAMED
|
File without changes
|
|
File without changes
|