skilldrop-cli 1.0.0 → 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/ai-engineering/skills/ai-usage-report/examples/weekly-engineering-team.md +24 -24
- package/packs/ai-engineering/skills/ai-usage-report/templates/usage-event-schema.md +9 -9
- package/packs/converters/skills/file-to-markdown/manifest.json +1 -1
- package/packs/converters/skills/file-to-markdown/scripts/to_markdown.py +116 -38
- 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/dev-team/skills/dependency-upgrade-review/evals/files/4/Dockerfile +1 -1
- package/packs/dev-team/skills/rollout-plan/manifest.json +1 -1
- package/packs/dev-team/skills/rollout-plan/scripts/canary_math.py +2 -2
- package/packs/research/skills/hypothesis-comparison/manifest.json +1 -1
- package/packs/research/skills/hypothesis-comparison/scripts/ach_matrix.py +9 -5
- 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/sre-oncall/skills/delivery-metrics-report/manifest.json +1 -1
- package/packs/sre-oncall/skills/delivery-metrics-report/scripts/delivery_metrics.py +7 -1
- package/packs/trackers/skills/team-status-report/manifest.json +1 -1
- package/packs/trackers/skills/team-status-report/scripts/status_counts.py +5 -2
- /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
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "skilldrop-cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Portable AI-agent skills, measured against the agent without them. ADRs, PRDs, runbooks, decks and reviews, installable into Claude Code, Cursor, Kiro, and any IDE that takes custom instructions. One command per skill or per role-based pack.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Sanjay Ananthanarayan",
|
|
@@ -6,22 +6,22 @@ A small synthetic event log demonstrating the three views — including graceful
|
|
|
6
6
|
|
|
7
7
|
```csv
|
|
8
8
|
timestamp,user,tool,session_id,output_consumed,consumed_at,outcome
|
|
9
|
-
2026-05-19T09:14:23Z,alice@acme.
|
|
10
|
-
2026-05-19T09:14:55Z,alice@acme.
|
|
11
|
-
2026-05-19T09:18:02Z,alice@acme.
|
|
12
|
-
2026-05-19T10:01:11Z,bob@acme.
|
|
13
|
-
2026-05-19T10:01:30Z,bob@acme.
|
|
14
|
-
2026-05-19T10:02:11Z,bob@acme.
|
|
15
|
-
2026-05-19T10:03:11Z,bob@acme.
|
|
16
|
-
2026-05-19T10:04:11Z,bob@acme.
|
|
17
|
-
2026-05-20T14:22:08Z,carol@acme.
|
|
18
|
-
2026-05-20T15:22:08Z,carol@acme.
|
|
19
|
-
2026-05-20T16:22:08Z,carol@acme.
|
|
20
|
-
2026-05-19T11:14:23Z,dan@acme.
|
|
21
|
-
2026-05-19T11:15:23Z,dan@acme.
|
|
22
|
-
2026-05-20T08:14:23Z,eve@acme.
|
|
23
|
-
2026-05-20T08:15:23Z,eve@acme.
|
|
24
|
-
2026-05-20T08:16:23Z,eve@acme.
|
|
9
|
+
2026-05-19T09:14:23Z,alice@acme.example,claude-code,sess-001,1,2026-05-19T11:02:00Z,success
|
|
10
|
+
2026-05-19T09:14:55Z,alice@acme.example,claude-code,sess-001,1,2026-05-19T11:02:00Z,success
|
|
11
|
+
2026-05-19T09:18:02Z,alice@acme.example,deck-builder,sess-001,1,2026-05-19T14:30:00Z,success
|
|
12
|
+
2026-05-19T10:01:11Z,bob@acme.example,cursor,sess-014,0,,success
|
|
13
|
+
2026-05-19T10:01:30Z,bob@acme.example,cursor,sess-015,0,,success
|
|
14
|
+
2026-05-19T10:02:11Z,bob@acme.example,cursor,sess-016,0,,success
|
|
15
|
+
2026-05-19T10:03:11Z,bob@acme.example,cursor,sess-017,0,,success
|
|
16
|
+
2026-05-19T10:04:11Z,bob@acme.example,cursor,sess-018,0,,success
|
|
17
|
+
2026-05-20T14:22:08Z,carol@acme.example,claude-code,sess-029,1,2026-05-22T08:11:00Z,success
|
|
18
|
+
2026-05-20T15:22:08Z,carol@acme.example,adr-generator,sess-029,1,2026-05-22T08:11:00Z,success
|
|
19
|
+
2026-05-20T16:22:08Z,carol@acme.example,design-doc,sess-030,1,2026-05-22T08:11:00Z,success
|
|
20
|
+
2026-05-19T11:14:23Z,dan@acme.example,claude-code,sess-101,1,2026-05-19T13:00:00Z,success
|
|
21
|
+
2026-05-19T11:15:23Z,dan@acme.example,claude-code,sess-101,0,,success
|
|
22
|
+
2026-05-20T08:14:23Z,eve@acme.example,claude-code,sess-201,1,2026-05-20T09:00:00Z,success
|
|
23
|
+
2026-05-20T08:15:23Z,eve@acme.example,claude-code,sess-201,1,2026-05-20T09:00:00Z,success
|
|
24
|
+
2026-05-20T08:16:23Z,eve@acme.example,cursor,sess-202,1,2026-05-20T10:00:00Z,success
|
|
25
25
|
```
|
|
26
26
|
|
|
27
27
|
16 events, 5 users, 5 tools, period 2026-05-19 → 2026-05-20.
|
|
@@ -56,11 +56,11 @@ python3 scripts/build_report.py events.csv \
|
|
|
56
56
|
|
|
57
57
|
| User | Invocations | Sessions | Avg / session | Distinct tools | Effectiveness rate | Single-shot rate |
|
|
58
58
|
|---|---:|---:|---:|---:|---:|---:|
|
|
59
|
-
| alice@acme.
|
|
60
|
-
| bob@acme.
|
|
61
|
-
| carol@acme.
|
|
62
|
-
| dan@acme.
|
|
63
|
-
| eve@acme.
|
|
59
|
+
| alice@acme.example | 3 | 1 | 3.0 | 2 | 100% | 0% |
|
|
60
|
+
| bob@acme.example | 5 | 5 | 1.0 | 1 | 0% | 100% |
|
|
61
|
+
| carol@acme.example | 3 | 2 | 1.5 | 3 | 100% | 50% |
|
|
62
|
+
| dan@acme.example | 2 | 1 | 2.0 | 1 | 50% | 0% |
|
|
63
|
+
| eve@acme.example | 3 | 2 | 1.5 | 2 | 100% | 50% |
|
|
64
64
|
```
|
|
65
65
|
|
|
66
66
|
**Reading the data:**
|
|
@@ -99,13 +99,13 @@ If the input is missing optional fields, columns drop silently to `—`:
|
|
|
99
99
|
|
|
100
100
|
```csv
|
|
101
101
|
timestamp,user,tool
|
|
102
|
-
2026-05-19T09:14:23Z,alice@acme.
|
|
103
|
-
2026-05-19T09:14:55Z,alice@acme.
|
|
102
|
+
2026-05-19T09:14:23Z,alice@acme.example,claude-code
|
|
103
|
+
2026-05-19T09:14:55Z,alice@acme.example,claude-code
|
|
104
104
|
```
|
|
105
105
|
|
|
106
106
|
```markdown
|
|
107
107
|
| User | Invocations | Sessions | Avg / session | Distinct tools | Effectiveness rate | Single-shot rate |
|
|
108
|
-
| alice@acme.
|
|
108
|
+
| alice@acme.example | 2 | — | — | 1 | — | — |
|
|
109
109
|
```
|
|
110
110
|
|
|
111
111
|
The "Where signals are missing" section then explains *what* couldn't be computed and *why*.
|
|
@@ -49,20 +49,20 @@ When a column is omitted, the report includes a *"Where signals are missing"* se
|
|
|
49
49
|
|
|
50
50
|
```csv
|
|
51
51
|
timestamp,user,tool,session_id,output_consumed,consumed_at,outcome
|
|
52
|
-
2026-05-21T09:14:23Z,alice@acme.
|
|
53
|
-
2026-05-21T09:14:55Z,alice@acme.
|
|
54
|
-
2026-05-21T09:18:02Z,alice@acme.
|
|
55
|
-
2026-05-21T10:01:11Z,bob@acme.
|
|
56
|
-
2026-05-21T10:01:30Z,bob@acme.
|
|
57
|
-
2026-05-21T14:22:08Z,carol@acme.
|
|
52
|
+
2026-05-21T09:14:23Z,alice@acme.example,claude-code,sess-001,1,2026-05-21T11:02:00Z,success
|
|
53
|
+
2026-05-21T09:14:55Z,alice@acme.example,claude-code,sess-001,1,2026-05-21T11:02:00Z,success
|
|
54
|
+
2026-05-21T09:18:02Z,alice@acme.example,deck-builder,sess-001,1,2026-05-21T14:30:00Z,success
|
|
55
|
+
2026-05-21T10:01:11Z,bob@acme.example,cursor,sess-014,0,,success
|
|
56
|
+
2026-05-21T10:01:30Z,bob@acme.example,cursor,sess-014,0,,success
|
|
57
|
+
2026-05-21T14:22:08Z,carol@acme.example,claude-code,sess-029,1,2026-05-22T08:11:00Z,success
|
|
58
58
|
```
|
|
59
59
|
|
|
60
60
|
## Example: JSONL
|
|
61
61
|
|
|
62
62
|
```jsonl
|
|
63
|
-
{"timestamp": "2026-05-21T09:14:23Z", "user": "alice@acme.
|
|
64
|
-
{"timestamp": "2026-05-21T09:14:55Z", "user": "alice@acme.
|
|
65
|
-
{"timestamp": "2026-05-21T10:01:11Z", "user": "bob@acme.
|
|
63
|
+
{"timestamp": "2026-05-21T09:14:23Z", "user": "alice@acme.example", "tool": "claude-code", "session_id": "sess-001", "output_consumed": true, "outcome": "success"}
|
|
64
|
+
{"timestamp": "2026-05-21T09:14:55Z", "user": "alice@acme.example", "tool": "claude-code", "session_id": "sess-001", "output_consumed": true, "outcome": "success"}
|
|
65
|
+
{"timestamp": "2026-05-21T10:01:11Z", "user": "bob@acme.example", "tool": "cursor", "session_id": "sess-014", "output_consumed": false, "outcome": "success"}
|
|
66
66
|
```
|
|
67
67
|
|
|
68
68
|
## A note on the `output_consumed` signal
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "file-to-markdown",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.2",
|
|
4
4
|
"description": "Convert a Word document, PowerPoint deck, Excel workbook, web page, CSV, JSON, text file, email (.eml or Outlook .msg) or PDF into clean Markdown that keeps headings, lists, tables, links, bold and italic, slide titles and speaker notes, and reports everything it dropped (images, charts, comments, formulas, tracked changes), so the content can be read, reviewed or fed to another skill. Use when the user hands over a .docx, .pptx, .xlsx, .html, .csv, .json, .eml, .msg or .pdf and says \"read this\", \"turn this into markdown\", \"extract the text\", or wants it critiqued or briefed.",
|
|
5
5
|
"entrypoint": "SKILL.md",
|
|
6
6
|
"deps": {
|
|
@@ -26,6 +26,7 @@ import subprocess
|
|
|
26
26
|
import sys
|
|
27
27
|
import tempfile
|
|
28
28
|
import zipfile
|
|
29
|
+
import zlib
|
|
29
30
|
from collections import OrderedDict
|
|
30
31
|
from datetime import datetime, timedelta
|
|
31
32
|
from html.parser import HTMLParser
|
|
@@ -148,12 +149,29 @@ def truthy(el, attr=W + "val"):
|
|
|
148
149
|
return v is None or v not in ("0", "false", "off", "none")
|
|
149
150
|
|
|
150
151
|
|
|
152
|
+
def span_of(value, cap):
|
|
153
|
+
"""A cell's column span, from 1 to the format's own limit (Word: 63 columns; HTML: colspan
|
|
154
|
+
1000), so a damaged or hostile value can't make a row of a billion cells."""
|
|
155
|
+
v = (value or "1").strip()
|
|
156
|
+
return min(int(v), cap) if v.isascii() and v.isdigit() and int(v) > 0 else 1
|
|
157
|
+
|
|
158
|
+
|
|
151
159
|
def open_zip(path, kind):
|
|
152
160
|
try:
|
|
153
|
-
|
|
161
|
+
z = zipfile.ZipFile(str(path))
|
|
154
162
|
except zipfile.BadZipFile:
|
|
163
|
+
with open(str(path), "rb") as fh:
|
|
164
|
+
if fh.read(8) == b"\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1":
|
|
165
|
+
# Office saves a password-protected .docx/.pptx/.xlsx as a compound file holding
|
|
166
|
+
# the encrypted package, not as a zip
|
|
167
|
+
sys.exit("error: %s is password-protected (or an old binary Office file). Open it, remove "
|
|
168
|
+
"the password or save it as %s, and rerun." % (path, kind))
|
|
155
169
|
sys.exit("error: %s is not a valid %s file (not a zip package). If it is an old binary "
|
|
156
170
|
"Office file, save it as %s first." % (path, kind, kind))
|
|
171
|
+
if any(info.flag_bits & 0x1 for info in z.infolist()):
|
|
172
|
+
sys.exit("error: %s has encrypted parts, which Office never writes; it is damaged, or was "
|
|
173
|
+
"zipped with a password by another tool" % path)
|
|
174
|
+
return z
|
|
157
175
|
|
|
158
176
|
|
|
159
177
|
# ---------------------------------------------------------------- docx
|
|
@@ -210,6 +228,9 @@ def docx_to_md(path, rep):
|
|
|
210
228
|
break
|
|
211
229
|
|
|
212
230
|
body = ET.fromstring(z.read("word/document.xml")).find(W + "body")
|
|
231
|
+
if body is None:
|
|
232
|
+
sys.exit("error: %s has no document body (word/document.xml has no w:body in the WordprocessingML "
|
|
233
|
+
"namespace); it is not a Word document, or it is damaged" % path)
|
|
213
234
|
|
|
214
235
|
def para_segments(p, in_table=False):
|
|
215
236
|
segs = []
|
|
@@ -312,7 +333,7 @@ def docx_to_md(path, rep):
|
|
|
312
333
|
return ("decimal", 0)
|
|
313
334
|
return None
|
|
314
335
|
fmt = num_fmt.get(num_id, {}).get(ilvl, "bullet")
|
|
315
|
-
return (fmt, int(ilvl))
|
|
336
|
+
return (fmt, min(max(int(ilvl), 0), 8)) # Word's list levels are 0 to 8
|
|
316
337
|
|
|
317
338
|
def table_md(tbl):
|
|
318
339
|
rows = []
|
|
@@ -332,7 +353,7 @@ def docx_to_md(path, rep):
|
|
|
332
353
|
text = ""
|
|
333
354
|
row.append(text)
|
|
334
355
|
if tcpr is not None and tcpr.find(W + "gridSpan") is not None:
|
|
335
|
-
row.extend([""] * (
|
|
356
|
+
row.extend([""] * (span_of(tcpr.find(W + "gridSpan").get(W + "val"), 63) - 1))
|
|
336
357
|
rows.append(row)
|
|
337
358
|
if len(list(tbl.iter(W + "tbl"))) > 1:
|
|
338
359
|
rep.notes.append("a nested table was flattened into its parent cell")
|
|
@@ -523,7 +544,7 @@ def pptx_to_md(path, rep):
|
|
|
523
544
|
if not text:
|
|
524
545
|
continue
|
|
525
546
|
ppr = p.find(A + "pPr")
|
|
526
|
-
lvl = int(ppr.get("lvl", "0")) if ppr is not None else 0
|
|
547
|
+
lvl = min(max(int(ppr.get("lvl", "0")), 0), 8) if ppr is not None else 0 # PowerPoint's are 0 to 8
|
|
527
548
|
bullet = is_body
|
|
528
549
|
if ppr is not None:
|
|
529
550
|
if ppr.find(A + "buNone") is not None:
|
|
@@ -618,12 +639,16 @@ def pptx_to_md(path, rep):
|
|
|
618
639
|
BUILTIN_DATE_FMTS = set(range(14, 23)) | {45, 46, 47}
|
|
619
640
|
|
|
620
641
|
|
|
621
|
-
def
|
|
622
|
-
|
|
642
|
+
def cell_ref(ref):
|
|
643
|
+
"""(row, column) from a cell reference such as B7 or $B$7, counting from 0; None for anything
|
|
644
|
+
else, so a damaged reference skips that cell rather than the sheet."""
|
|
645
|
+
m = re.fullmatch(r"\$?([A-Za-z]{1,3})\$?(\d{1,7})", ref.strip())
|
|
646
|
+
if not m or int(m.group(2)) < 1:
|
|
647
|
+
return None
|
|
623
648
|
n = 0
|
|
624
|
-
for ch in
|
|
649
|
+
for ch in m.group(1).upper():
|
|
625
650
|
n = n * 26 + (ord(ch) - 64)
|
|
626
|
-
return n - 1
|
|
651
|
+
return int(m.group(2)) - 1, n - 1
|
|
627
652
|
|
|
628
653
|
|
|
629
654
|
def xlsx_to_md(path, rep, max_rows=0):
|
|
@@ -678,8 +703,10 @@ def xlsx_to_md(path, rep, max_rows=0):
|
|
|
678
703
|
ref = c.get("r")
|
|
679
704
|
if not ref:
|
|
680
705
|
continue
|
|
681
|
-
|
|
682
|
-
|
|
706
|
+
rc = cell_ref(ref)
|
|
707
|
+
if rc is None:
|
|
708
|
+
continue
|
|
709
|
+
row, col = rc
|
|
683
710
|
t = c.get("t", "n")
|
|
684
711
|
v = c.find(S + "v")
|
|
685
712
|
if c.find(S + "f") is not None:
|
|
@@ -717,13 +744,14 @@ def xlsx_to_md(path, rep, max_rows=0):
|
|
|
717
744
|
out.append("*(empty sheet)*")
|
|
718
745
|
out.append("")
|
|
719
746
|
continue
|
|
747
|
+
# Only the rows and columns that hold a value: one stray cell at ZZZ999999 must not
|
|
748
|
+
# build a grid of a million empty rows.
|
|
720
749
|
rows_used = sorted({r for r, _ in grid})
|
|
721
750
|
cols_used = sorted({c for _, c in grid})
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
table = [r for r in table if any(r)]
|
|
751
|
+
gaps = cols_used[-1] - cols_used[0] + 1 - len(cols_used)
|
|
752
|
+
if gaps:
|
|
753
|
+
rep.notes.append("sheet '%s': %d empty column(s) left out" % (name, gaps))
|
|
754
|
+
table = [[grid.get((r, c), "") for c in cols_used] for r in rows_used]
|
|
727
755
|
if max_rows and len(table) > max_rows + 1:
|
|
728
756
|
rep.notes.append("sheet '%s' cut to %d of %d data rows (--max-rows)"
|
|
729
757
|
% (name, max_rows, len(table) - 1))
|
|
@@ -860,8 +888,7 @@ class HTMLToMD(HTMLParser):
|
|
|
860
888
|
if not self.table:
|
|
861
889
|
self.table.append([])
|
|
862
890
|
self.cell = []
|
|
863
|
-
|
|
864
|
-
self._span = span
|
|
891
|
+
self._span = span_of(a.get("colspan"), 1000)
|
|
865
892
|
|
|
866
893
|
def handle_endtag(self, tag):
|
|
867
894
|
if tag in self.SKIP:
|
|
@@ -1028,29 +1055,60 @@ def _human_size(n):
|
|
|
1028
1055
|
n /= 1024.0
|
|
1029
1056
|
|
|
1030
1057
|
|
|
1058
|
+
def _part_text(part):
|
|
1059
|
+
"""A text part's content. A charset Python doesn't know (a typo, or a vendor's own label) is
|
|
1060
|
+
read as UTF-8 with replacement characters, as html_to_md does, rather than lost."""
|
|
1061
|
+
try:
|
|
1062
|
+
return part.get_content()
|
|
1063
|
+
except LookupError:
|
|
1064
|
+
return (part.get_payload(decode=True) or b"").decode("utf-8", "replace")
|
|
1065
|
+
|
|
1066
|
+
|
|
1067
|
+
def _unescape(text):
|
|
1068
|
+
"""The email package keeps undecodable header bytes as surrogates, which can't be written as
|
|
1069
|
+
UTF-8. Read them as UTF-8, with a replacement character for any that aren't."""
|
|
1070
|
+
return text.encode("utf-8", "surrogateescape").decode("utf-8", "replace")
|
|
1071
|
+
|
|
1072
|
+
|
|
1073
|
+
def _header(msg, name):
|
|
1074
|
+
"""A header's decoded value, or its raw text when it won't parse: Python 3.9's email package
|
|
1075
|
+
raises on a malformed date or address that later versions keep."""
|
|
1076
|
+
try:
|
|
1077
|
+
return _unescape(str(msg.get(name, "")))
|
|
1078
|
+
except (TypeError, ValueError, IndexError, AttributeError, OverflowError):
|
|
1079
|
+
return _unescape(next((" ".join(str(v).split()) for k, v in msg.raw_items() if k.lower() == name.lower()), ""))
|
|
1080
|
+
|
|
1081
|
+
|
|
1031
1082
|
def eml_to_md(path, rep):
|
|
1032
1083
|
"""An RFC 5322 message (.eml): the plain-text part when there is one, else the HTML part."""
|
|
1033
1084
|
import email
|
|
1034
1085
|
from email import policy
|
|
1035
1086
|
msg = email.message_from_bytes(path.read_bytes(), policy=policy.default)
|
|
1036
|
-
rows = [(
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1087
|
+
rows = [(h, _header(msg, h)) for h in ("From", "To", "Cc", "Date")]
|
|
1088
|
+
try:
|
|
1089
|
+
plain = msg.get_body(preferencelist=("plain",))
|
|
1090
|
+
html = msg.get_body(preferencelist=("html",))
|
|
1091
|
+
parts = list(msg.iter_attachments())
|
|
1092
|
+
except (AttributeError, TypeError, KeyError):
|
|
1093
|
+
# Python 3.9 fails inside get_body on a multipart message whose boundary never appears.
|
|
1094
|
+
# Read it as one text body, which is what the raw message holds.
|
|
1095
|
+
raw = msg.get_payload()
|
|
1096
|
+
rep.notes.append("the message's MIME parts could not be separated, so its body is the raw text")
|
|
1097
|
+
return _email_md(_header(msg, "Subject"), rows, _email_text(_unescape(raw) if isinstance(raw, str) else ""), [], rep)
|
|
1040
1098
|
if plain is not None:
|
|
1041
|
-
body = _email_text(plain
|
|
1099
|
+
body = _email_text(_part_text(plain))
|
|
1042
1100
|
if html is not None:
|
|
1043
1101
|
rep.drop("HTML alternative (the plain-text part was used)", 1)
|
|
1044
1102
|
elif html is not None:
|
|
1045
|
-
body = _html_text_to_md(html
|
|
1103
|
+
body = _html_text_to_md(_part_text(html), rep)
|
|
1046
1104
|
else:
|
|
1047
1105
|
body = ""
|
|
1048
1106
|
attachments = []
|
|
1049
|
-
for part in
|
|
1107
|
+
for part in parts:
|
|
1050
1108
|
payload = part.get_payload(decode=True)
|
|
1051
1109
|
name = part.get_filename() or ("forwarded message" if part.get_content_type() == "message/rfc822" else None)
|
|
1052
1110
|
attachments.append((name, len(payload) if payload is not None else None))
|
|
1053
|
-
return _email_md(
|
|
1111
|
+
return _email_md(_header(msg, "Subject"), rows, body, attachments, rep)
|
|
1054
1112
|
|
|
1055
1113
|
|
|
1056
1114
|
class _CFB:
|
|
@@ -1073,14 +1131,20 @@ class _CFB:
|
|
|
1073
1131
|
minifat_start, nminifat, difat_start, ndifat = struct.unpack_from("<IIII", data, 60)
|
|
1074
1132
|
difat = list(struct.unpack_from("<109I", data, 76))
|
|
1075
1133
|
per = self.ss // 4
|
|
1076
|
-
|
|
1077
|
-
|
|
1134
|
+
# A FAT never needs more sectors than it takes to map the file, and lists each one once:
|
|
1135
|
+
# a repeated or looping list would otherwise build a table far larger than the file.
|
|
1136
|
+
nsect = len(data) // self.ss - 1
|
|
1137
|
+
need = -(-nsect // per)
|
|
1138
|
+
s, seen = difat_start, set()
|
|
1139
|
+
while ndifat and s < 0xFFFFFFFA and s not in seen and len(seen) < need:
|
|
1140
|
+
seen.add(s)
|
|
1078
1141
|
vals = struct.unpack("<%dI" % per, self._sector(s))
|
|
1079
1142
|
difat += vals[:-1]
|
|
1080
|
-
s,
|
|
1081
|
-
self.fat = []
|
|
1082
|
-
for fs in difat[:nfat]:
|
|
1083
|
-
if fs < 0xFFFFFFFA:
|
|
1143
|
+
s, ndifat = vals[-1], ndifat - 1
|
|
1144
|
+
self.fat, used = [], set()
|
|
1145
|
+
for fs in difat[:min(nfat, need)]:
|
|
1146
|
+
if fs < 0xFFFFFFFA and fs not in used:
|
|
1147
|
+
used.add(fs)
|
|
1084
1148
|
self.fat += struct.unpack("<%dI" % per, self._sector(fs))
|
|
1085
1149
|
raw = self._chain(dir_start)
|
|
1086
1150
|
self.entries = []
|
|
@@ -1108,16 +1172,17 @@ class _CFB:
|
|
|
1108
1172
|
return self.d[off:off + self.ss]
|
|
1109
1173
|
|
|
1110
1174
|
def _chain(self, start, mini=False):
|
|
1111
|
-
fat, out, n, seen = (self.minifat if mini else self.fat), [], start,
|
|
1175
|
+
fat, out, n, seen = (self.minifat if mini else self.fat), [], start, set()
|
|
1112
1176
|
while n < 0xFFFFFFFA:
|
|
1113
|
-
if n >= len(fat) or seen
|
|
1177
|
+
if n >= len(fat) or n in seen: # off the table, or a loop: each sector is read once
|
|
1114
1178
|
raise ValueError("compound file has a broken sector chain")
|
|
1179
|
+
seen.add(n)
|
|
1115
1180
|
if mini:
|
|
1116
1181
|
off = n * self.mss
|
|
1117
1182
|
out.append(self.ministream[off:off + self.mss])
|
|
1118
1183
|
else:
|
|
1119
1184
|
out.append(self._sector(n))
|
|
1120
|
-
n
|
|
1185
|
+
n = fat[n]
|
|
1121
1186
|
return b"".join(out)
|
|
1122
1187
|
|
|
1123
1188
|
def children(self, idx):
|
|
@@ -1171,8 +1236,11 @@ def _msg_fixed(cfb, idx, header):
|
|
|
1171
1236
|
ptype, pid = tag & 0xFFFF, "%04X" % (tag >> 16)
|
|
1172
1237
|
if ptype == 0x0040:
|
|
1173
1238
|
ft, = struct.unpack_from("<Q", data, off + 8)
|
|
1174
|
-
|
|
1175
|
-
|
|
1239
|
+
try:
|
|
1240
|
+
if ft:
|
|
1241
|
+
out[pid] = _dt(1601, 1, 1) + timedelta(microseconds=ft // 10)
|
|
1242
|
+
except OverflowError:
|
|
1243
|
+
pass # a time past the year 9999 is a placeholder or damage; the message has no date
|
|
1176
1244
|
elif ptype == 0x0003:
|
|
1177
1245
|
out[pid] = struct.unpack_from("<i", data, off + 8)[0]
|
|
1178
1246
|
return out
|
|
@@ -1679,6 +1747,9 @@ HANDLERS = {
|
|
|
1679
1747
|
".html": html_to_md, ".htm": html_to_md, ".csv": csv_to_md, ".tsv": csv_to_md,
|
|
1680
1748
|
".json": json_to_md, ".pdf": pdf_to_md, ".eml": eml_to_md, ".msg": msg_to_md,
|
|
1681
1749
|
}
|
|
1750
|
+
# What a damaged or truncated file raises from the zip, XML, compound-file and email layers.
|
|
1751
|
+
DAMAGED = (zipfile.BadZipFile, zipfile.LargeZipFile, zlib.error, EOFError, ET.ParseError, ValueError, LookupError,
|
|
1752
|
+
KeyError, IndexError, NotImplementedError, OSError)
|
|
1682
1753
|
LEGACY = {".doc": ".docx", ".ppt": ".pptx", ".xls": ".xlsx", ".rtf": ".docx", ".odt": ".docx",
|
|
1683
1754
|
".pages": ".docx", ".key": ".pptx", ".numbers": ".xlsx"}
|
|
1684
1755
|
|
|
@@ -1711,8 +1782,15 @@ def main(argv=None):
|
|
|
1711
1782
|
md += "\n"
|
|
1712
1783
|
elif ext in HANDLERS:
|
|
1713
1784
|
fn = HANDLERS[ext]
|
|
1714
|
-
|
|
1715
|
-
|
|
1785
|
+
try:
|
|
1786
|
+
md = (fn(path, rep, a.max_rows) if fn is xlsx_to_md else
|
|
1787
|
+
fn(path, rep, ocr=not a.no_ocr, ocr_lang=a.ocr_lang) if fn is pdf_to_md else fn(path, rep))
|
|
1788
|
+
except DAMAGED as e:
|
|
1789
|
+
if ext not in (".docx", ".pptx", ".xlsx", ".xlsm", ".msg", ".eml"):
|
|
1790
|
+
raise
|
|
1791
|
+
# A file that opens in its own app is a bug here; one that doesn't is damaged.
|
|
1792
|
+
sys.exit("error: %s looks damaged or truncated, so it could not be read as %s (%s: %s). If it "
|
|
1793
|
+
"opens normally in its own app, please report it." % (path, ext, type(e).__name__, e))
|
|
1716
1794
|
else:
|
|
1717
1795
|
sys.exit("error: unsupported file type %r. Supported: %s, .txt, .md"
|
|
1718
1796
|
% (ext, ", ".join(sorted(HANDLERS))))
|
package/packs/core/pack.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
|
-
"description": "The cross-role toolkit every pack builds on: structured intake, document critique, the review council,
|
|
2
|
+
"description": "The cross-role toolkit every pack builds on: structured intake, document critique, the review council, output hygiene, and the decision graph behind a codebase, plus the ship-a-draft wrapper. Installed automatically with every role pack.",
|
|
3
3
|
"display_name": "Core",
|
|
4
|
-
"keywords": ["intake", "critique", "review", "hygiene"],
|
|
4
|
+
"keywords": ["intake", "critique", "review", "hygiene", "decisions"],
|
|
5
5
|
"links": {
|
|
6
6
|
"documentation": "https://sananthanarayan.github.io/skilldrop/packs/core/"
|
|
7
7
|
},
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: context-graph
|
|
3
|
-
description: Map the decisions behind a codebase from its ADRs, RFCs and design records — each record's status and date, what supersedes or amends what, which records cite which, the files and folders each decision governs, and with git the commits that implemented it — then answer "why is it like this?" from the record and check the record is still true. Finds supersessions only one side records, current decisions pointing at code that is gone, two current decisions governing the same path, and references to records that don't exist. Use when the user asks "why do we use X", "which decisions govern this file or service", "is this ADR still current", "are our ADRs out of date", "what was decided before I change this", wants a map of the architecture decisions, or is onboarding onto a codebase's history.
|
|
3
|
+
description: Map the decisions behind a codebase from its ADRs, RFCs and design records — each record's status and date, what supersedes or amends what, which records cite which, the files and folders each decision governs, and with git the commits that implemented it and the commits that changed each decision's code since it was made, and who made them — then answer "why is it like this?" from the record and check the record is still true. Finds supersessions only one side records, current decisions pointing at code that is gone, code that has drifted from its decision, two current decisions governing the same path, and references to records that don't exist. Use when the user asks "why do we use X", "which decisions govern this file or service", "is this ADR still current", "has the code drifted since we decided this", "are our ADRs out of date", "what was decided before I change this", wants a map of the architecture decisions, or is onboarding onto a codebase's history.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# context-graph
|
|
@@ -31,7 +31,7 @@ the code itself is `reverse-architecture`.
|
|
|
31
31
|
# Other IDEs (from the skill folder)
|
|
32
32
|
python3 scripts/context_graph.py --root <repo> --docs architecture/decisions --json -o graph.json --mermaid graph.mmd
|
|
33
33
|
```
|
|
34
|
-
`--git` adds the commits whose messages mention each record. A path counts as governed when a record names it in backticks or links to it, and it exists. Without the script, do the same by hand and say the links were found by reading, not computed.
|
|
34
|
+
`--git` adds the commits whose messages mention each record. For each current decision that governs code, it also lists the commits that changed that code since the decision's date (or since the record was committed, when it has no date). It gives who made them, how many cite the record, and when the record itself was last edited. A path counts as governed when a record names it in backticks or links to it, and it exists. Without the script, do the same by hand and say the links were found by reading, not computed.
|
|
35
35
|
|
|
36
36
|
**The script's findings are leads, not answers.** Confirm each one by reading the records and the code before you report it, and drop the ones that don't hold. Two records "governing the same path" often don't clash at all: say so only when their decisions actually conflict, and quote both.
|
|
37
37
|
|
|
@@ -43,12 +43,15 @@ the code itself is `reverse-architecture`.
|
|
|
43
43
|
|
|
44
44
|
4. **Check the deciding record against the code.** Open the files it governs and check each thing the record requires (every "must", every named field, check, limit or setting), not only its headline. Confirm or contradict each with the code, quoting the line: ✅ *"`scheduler/jobs.py` line 18 still opens a database transaction per job, as ADR-0012 says."* Report drift as a finding with both sides: ✅ *"ADR-0015 promises 30 days of audit-log retention; `ops/logging.yaml` sets `retention_days: 14`."* When the record describes a phased change (a migration, a dual-write, a rollout), say which phase the code is in. While it's mid-rollout, the old decision still holds for the parts not yet moved: that is a missing cross-reference, not a contradiction.
|
|
45
45
|
|
|
46
|
+
With git history, start with what changed since the decision: the commits to its code that don't cite it, newest first. Read what each one changed against the record's requirements. A commit is not drift by itself; a requirement the code no longer meets is. ✅ *"ADR-0009 (2026-02-10) requires one queue per tenant. Three commits since; `a1b2c3d` (Ben, 2026-05-03, 'Switch to shared queue') moved `orders/publish.py` to a single queue."* When the code changed a lot and the record was never touched, say so even when you find no contradiction. Name the people who made the changes: they are who to ask about what the record doesn't say.
|
|
47
|
+
|
|
46
48
|
5. **For "can I remove or change X?", search the code for every use of X,** not only the records: imports, client constructors, config keys, environment variables and container definitions. A use that no record covers is still a dependency: list it as "in use, no recorded decision", with its file. The records tell you why; only the code tells you everything that would break.
|
|
47
49
|
|
|
48
50
|
6. **Find the decisions that bear on it, not only the ones that name its path.** Read any record that amends, extends or narrows the governing one, and any that sets a rule for the same concern across the codebase (error handling, data access, logging, authentication), even when it names a different folder. A shared-library rule governs a change that creates a shared library. Before you call a related record unrelated, read it.
|
|
49
51
|
|
|
50
52
|
7. **Report what is no longer true, worst first.** Problems before warnings:
|
|
51
53
|
- **Supersession recorded on one side only:** "ADR-0004 still says Accepted". Fix it by marking it "Superseded by ADR-0009".
|
|
54
|
+
- **Code that no longer does what its current decision requires:** quote the requirement and the line, and name the commit that changed it when the history shows one.
|
|
52
55
|
- **A current decision pointing at code that is gone:** the record names a folder or file that no longer exists. When the script suggests where it moved (a renamed sibling), or a README says so, report it as moved, not gone, and give the path to update. Only a path with no successor is gone; then the decision is dead, so deprecate it.
|
|
53
56
|
- **A guide that still teaches a superseded decision:** an onboarding doc or a README that cites the old record, or describes its choice. Name the file and the line to change.
|
|
54
57
|
- **Two current decisions governing the same path, neither superseding nor amending the other:** a possible conflict. Read both, quote the clash if there is one, and recommend which one supersedes or amends the other.
|
|
@@ -56,7 +59,7 @@ the code itself is `reverse-architecture`.
|
|
|
56
59
|
|
|
57
60
|
8. **Give each fix as the exact edit.** The line to change in which record, such as changing `## Status` to `Superseded by ADR-0009`, linked to that record's file, or the new record that's needed and what it must decide. Don't rewrite records nobody asked about.
|
|
58
61
|
|
|
59
|
-
9. **Say what the record can't tell.** Decisions made in chat or tickets and never written down, code that no record governs, and, without `--git`, whether anything was actually implemented. When a question has no record, say so, and suggest writing one only if the decision is still live.
|
|
62
|
+
9. **Say what the record can't tell.** Decisions made in chat or tickets and never written down, code that no record governs, and, without `--git`, whether anything was actually implemented or has changed since. When a question has no record, say so, and suggest writing one only if the decision is still live.
|
|
60
63
|
|
|
61
64
|
10. **Write the reply in this order, at the size asked for:**
|
|
62
65
|
- the answer, in two or three sentences, with the governing record's id;
|
|
@@ -71,7 +74,7 @@ the code itself is `reverse-architecture`.
|
|
|
71
74
|
## Useful references in this skill
|
|
72
75
|
|
|
73
76
|
- [`reference.md`](reference.md): the record formats read (MADR, Nygard, RFC frontmatter), how status, supersession, amendment and governed paths are detected, every check and its fix, and the JSON shape
|
|
74
|
-
- [`scripts/context_graph.py`](scripts/context_graph.py): stdlib; builds the graph, checks it, answers `--why PATH` and `--record ID`; `--json`, `--mermaid`, `--git
|
|
77
|
+
- [`scripts/context_graph.py`](scripts/context_graph.py): stdlib; builds the graph, checks it, answers `--why PATH` and `--record ID`; `--json`, `--mermaid`, `--git` (commits behind each record, and changes since each decision); exits 1 on problems and 2 on bad input
|
|
75
78
|
- [`examples/order-events.md`](examples/order-events.md): worked example, a repo whose Kafka ADR was superseded on one side only and whose newer ADR names a folder that was deleted
|
|
76
79
|
|
|
77
80
|
## Quality bar
|
|
@@ -86,6 +89,7 @@ the code itself is `reverse-architecture`.
|
|
|
86
89
|
- **A removal or change question lists every use of the thing in the code,** recorded or not.
|
|
87
90
|
- **Related records are read before they're called unrelated:** amendments, and rules for the same concern elsewhere in the codebase.
|
|
88
91
|
- **A moved path is reported as moved, with its new location; only a path with no successor is "gone".**
|
|
92
|
+
- **Drift is a requirement the code no longer meets, with the record's words, the line, and the commit when the history shows one.** A count of commits is a reason to look, never a finding by itself.
|
|
89
93
|
- **A mid-rollout pair of records is a missing cross-reference, not a contradiction.**
|
|
90
94
|
- **The reply fits the size asked for (a stated limit is obeyed exactly), and never mentions a script, a graph or a tool.**
|
|
91
95
|
- **Statuses, links and governed paths come from the script's output**, or are marked as read by hand when it couldn't run.
|
|
@@ -96,6 +100,7 @@ the code itself is `reverse-architecture`.
|
|
|
96
100
|
- ✅ "Why do we use X?" or "Why is this service built this way?" in a repo with ADRs or RFCs
|
|
97
101
|
- ✅ "Which decisions govern this file, folder or service before I change it?"
|
|
98
102
|
- ✅ "Are our ADRs still current?" or "Find the stale or contradictory decisions"
|
|
103
|
+
- ✅ "Has the code drifted from what we decided?" or "What changed in this service since the ADR, and who changed it?"
|
|
99
104
|
- ✅ A map of a codebase's architecture decisions for onboarding or a review
|
|
100
105
|
|
|
101
106
|
## When NOT to use this skill
|
|
@@ -115,5 +120,6 @@ the code itself is `reverse-architecture`.
|
|
|
115
120
|
- ❌ **Passing on a script warning unread.** "Both govern the same path" is a prompt to read both, not a conflict.
|
|
116
121
|
- ❌ **Answering "can I remove it?" from the records alone.** The uses nobody recorded are the ones that break.
|
|
117
122
|
- ❌ **Taking the record's word for it.** A decision the code no longer follows is the most useful thing to find; read the files.
|
|
123
|
+
- ❌ **Calling it drift because the code changed.** Most commits since a decision implement or refine it. Only a requirement the code no longer meets is drift.
|
|
118
124
|
- ❌ **"Gone" for a folder that was renamed.** Look for where it went before telling someone the decision is dead.
|
|
119
125
|
- ❌ **Telling the reader how you found it.** "The script shows" or "the graph says" means nothing to them; say what the records and the code say.
|
|
@@ -7,5 +7,8 @@
|
|
|
7
7
|
{"query": "We just decided to move our search from Postgres full-text to a hosted search engine. Write the ADR for it in MADR format", "should_trigger": false},
|
|
8
8
|
{"query": "I start on the checkout repo on Monday. How do I run it locally, run the tests, and where's the code for applying discount codes?", "should_trigger": false},
|
|
9
9
|
{"query": "There are no design docs for this monolith. Work out its architecture from the code and the docker-compose file and draw me a C4 diagram", "should_trigger": false},
|
|
10
|
-
{"query": "Here are the notes from this morning's pricing steering meeting. Pull out what was decided, who owns each action and when it's due", "should_trigger": false}
|
|
10
|
+
{"query": "Here are the notes from this morning's pricing steering meeting. Pull out what was decided, who owns each action and when it's due", "should_trigger": false},
|
|
11
|
+
{"query": "ADR-0009 says order events go through one SQS queue per tenant. Has the orders service drifted from that since we decided it, and who changed it?", "should_trigger": true},
|
|
12
|
+
{"query": "Before I rewrite the billing batch job, what decisions did we record about it, and has anyone changed the code since without updating them?", "should_trigger": true},
|
|
13
|
+
{"query": "Show me who committed to services/orders in the last month and summarise what changed", "should_trigger": false}
|
|
11
14
|
]
|
|
@@ -220,6 +220,36 @@
|
|
|
220
220
|
"Delivers the map itself as one pasteable markdown block (e.g. a table of ID, title, store, status, date, replaced by/replaces) with the issues in a short separate list, rather than a long narrative or per-file walkthrough; any check that needs git history (e.g. whether ADR 7 was ever implemented) is given as a command to run, not as a claimed result.",
|
|
221
221
|
"Invents nothing not in the input: no ADR numbers, dates, databases, services, sizes or commit details beyond what the files in doc/adr/, services/, deploy/ and web/ contain."
|
|
222
222
|
]
|
|
223
|
+
},
|
|
224
|
+
{
|
|
225
|
+
"id": 16,
|
|
226
|
+
"held_out": "2026-10-22",
|
|
227
|
+
"prompt": "I'm about to rework how payments handles client retries. ADR-0004 is the idempotency decision. Is the code in services/payments still doing what it says? If anything has drifted, which commit did it and who? Keep it short, I just need to know what I can safely assume before I start.",
|
|
228
|
+
"assertions": [
|
|
229
|
+
"Identifies the real drift: services/payments/idempotency.py no longer compares the stored body fingerprint in lookup(), so a request that reuses an Idempotency-Key with a different body gets the cached response back instead of a 422. That breaks ADR-0004 rule 4, and the response points out that the 422 branch in services/payments/api/create.py can no longer fire because IdempotencyConflict is never raised.",
|
|
230
|
+
"Pins the drift to the 2025-08-19 commit 'refactor: simplify idempotency store lookups' by Dmitri Vasquez, notes that its message says 'No behaviour change intended' even though behaviour did change, and notes that the same commit skipped test_mismatched_body_raises_conflict in tests/test_idempotency.py (reason 'flaky since lookup refactor, see PAY-311').",
|
|
231
|
+
"Does NOT report the 48-hour TTL (TTL_SECONDS = 48 * 60 * 60, i.e. 172800) as drift from ADR-0004's 24 hours. It recognises that ADR-0007 (2025-06-02, Accepted) amends rule 3 and that Lena Hargrove's 2025-06-04 commit implemented it.",
|
|
232
|
+
"Points out that the amendment is recorded on one side only: ADR-0007 says 'Amends: ADR-0004', but ADR-0004 still reads 'Status: Accepted' with 24 hours and no 'Amended by ADR-0007', even though ADR-0001 requires both sides to say so.",
|
|
233
|
+
"Confirms what still holds, so the user knows what is safe to assume: a missing Idempotency-Key is still rejected with 400, records are still scoped per merchant (key idem:{merchant_id}:{key}) and stored in Redis with a TTL, and ADR-0007 says all other ADR-0004 rules are unchanged, so rule 4 is still meant to apply.",
|
|
234
|
+
"Treats the later commits correctly as legitimate or neutral: Priya Natarajan's 2025-03-10 implementation commit, Tomás Okafor's 2025-03-12 tests and his 2025-09-30 debug-logging commit are not flagged as drift.",
|
|
235
|
+
"Answers the question directly and compactly: it starts with whether the code matches ADR-0004 (no, one rule is broken) plus the commit and author, and recommends restoring the comparison and un-skipping the test, or else writing a superseding ADR if dropping rule 4 was intended. It does not pad the answer with a full catalogue of unrelated ADRs (0001-0003).",
|
|
236
|
+
"Invents nothing: every commit, date, author, file, constant and ticket it cites (including PAY-311, which it does not claim to know the contents of) exists in the repository or its git history, and it invents no other ADRs, incidents or email addresses."
|
|
237
|
+
]
|
|
238
|
+
},
|
|
239
|
+
{
|
|
240
|
+
"id": 17,
|
|
241
|
+
"held_out": "2026-10-22",
|
|
242
|
+
"prompt": "hey, joined the platform team last week and i'm inheriting the notifications service. someone told me \"half our ADRs are lies\" lol. which of the decision docs can i actually trust for notifications, and is anything in there just wrong? repo's checked out here",
|
|
243
|
+
"assertions": [
|
|
244
|
+
"Gives a direct trust verdict for each decision record that governs notifications: ADR-0002, ADR-0003, ADR-0004, ADR-0005, ADR-0006, ADR-0007 and RFC-012 (via services/*/retry.py). Each one gets its status and a short trust / don't trust / partly answer, rather than a generic essay about ADR hygiene.",
|
|
245
|
+
"Flags ADR-0003 (RabbitMQ) as superseded on one side only: ADR-0007 says 'Supersedes: ADR-0003', but ADR-0003 still says 'Status: Accepted', breaking the both-sides rule in ADR-0001. It confirms from services/notifications/consumer.py (KafkaConsumer, topics user-events and invoice-events, group notifications) that Kafka is what actually runs, so ADR-0003 should not be trusted.",
|
|
246
|
+
"Flags that ADR-0006 (5 attempts, 2s base) and RFC-012 (3 attempts, 1s base, full jitter, 30s cap) are both Accepted, both govern services/notifications/retry.py, and contradict each other, with neither recording that it supersedes the other. It notes that notifications' retry.py follows ADR-0006 (MAX_ATTEMPTS = 5, BASE_DELAY_SECONDS = 2) while services/billing/retry.py follows RFC-012, and it leaves the conflict for the team to settle and record instead of quietly declaring one the winner.",
|
|
247
|
+
"Flags that ADR-0005 governs services/notifications/templates/, which no longer exists (the templates are in packages/notification-templates/, per its README), and gets the nuance right: rendering is still server-side via services/notifications/render.py with Jinja2 autoescape, so the decision itself still holds and only its governed path is stale.",
|
|
248
|
+
"Flags that ADR-0007 points to 'RFC-015 for the migration plan and cutover dates', but no RFC-015 exists in docs/rfcs/ (only RFC-012).",
|
|
249
|
+
"Does not flag the ADR-0002 to ADR-0004 supersession, which is recorded on both sides. It confirms that services/notifications/email/sender.py uses the provider HTTPS API and that dispatch.py logs the provider message id, so ADR-0004 can be trusted.",
|
|
250
|
+
"Says plainly that the checkout has no git history, so it cannot say when or by whom the code moved away from a record. It gives the commands the user could run to find out (for example git log --follow on services/notifications/retry.py, or git log on the templates move).",
|
|
251
|
+
"Invents nothing: no record, status, date, person, path, topic name or number beyond what the files contain. In particular it does not make up what RFC-015 says or a reason why the templates were moved beyond the README's stated reason (billing previews)."
|
|
252
|
+
]
|
|
223
253
|
}
|
|
224
254
|
]
|
|
225
255
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# ADR-0001: Record architecture decisions
|
|
2
|
+
|
|
3
|
+
Status: Accepted
|
|
4
|
+
Date: 2025-01-14
|
|
5
|
+
Deciders: Priya Natarajan
|
|
6
|
+
|
|
7
|
+
## Decision
|
|
8
|
+
|
|
9
|
+
We record significant decisions as numbered ADRs in `docs/adr/`. An ADR that is
|
|
10
|
+
replaced or changed by a later one says so in its Status line ("Superseded by
|
|
11
|
+
ADR-NNNN" or "Amended by ADR-NNNN"), and the later ADR says what it supersedes
|
|
12
|
+
or amends.
|
package/packs/core/skills/context-graph/evals/files/16/docs/adr/0002-postgres-for-the-ledger.md
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# ADR-0002: Postgres for the payment ledger
|
|
2
|
+
|
|
3
|
+
Status: Accepted
|
|
4
|
+
Date: 2025-01-20
|
|
5
|
+
Deciders: Priya Natarajan, Tomás Okafor
|
|
6
|
+
Governs: services/payments/ledger.py
|
|
7
|
+
|
|
8
|
+
## Decision
|
|
9
|
+
|
|
10
|
+
The ledger of record is a single Postgres database. Every payment is written in
|
|
11
|
+
one transaction to `payments` and `ledger_entries`.
|
package/packs/core/skills/context-graph/evals/files/16/docs/adr/0003-redis-for-ephemeral-state.md
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# ADR-0003: Redis for short-lived state
|
|
2
|
+
|
|
3
|
+
Status: Accepted
|
|
4
|
+
Date: 2025-02-03
|
|
5
|
+
Deciders: Tomás Okafor
|
|
6
|
+
Governs: services/payments/redis_client.py
|
|
7
|
+
|
|
8
|
+
## Decision
|
|
9
|
+
|
|
10
|
+
Short-lived, reconstructable state (rate-limit counters, idempotency records)
|
|
11
|
+
lives in Redis with an explicit TTL on every key. Nothing in Redis is the
|
|
12
|
+
system of record.
|