skilldrop-cli 1.0.1 → 1.1.1

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.
Files changed (311) hide show
  1. package/README.md +1 -1
  2. package/package.json +1 -1
  3. package/packs/ai-engineering/skills/judge-audit/SKILL.md +24 -6
  4. package/packs/ai-engineering/skills/judge-audit/evals/evals.json +29 -0
  5. package/packs/ai-engineering/skills/judge-audit/evals/files/10/judge_eval.csv +61 -0
  6. package/packs/ai-engineering/skills/judge-audit/evals/files/11/mod_sample_scored.csv +201 -0
  7. package/packs/ai-engineering/skills/judge-audit/manifest.json +1 -1
  8. package/packs/converters/skills/md-to-pptx/SKILL.md +25 -8
  9. package/packs/converters/skills/md-to-pptx/evals/evals.json +30 -0
  10. package/packs/converters/skills/md-to-pptx/evals/files/6/q3-status-report.md +72 -0
  11. package/packs/converters/skills/md-to-pptx/evals/files/7/runbooks/db-failover.md +59 -0
  12. package/packs/converters/skills/md-to-pptx/evals/files/7/runbooks/diagrams/topology.png +0 -0
  13. package/packs/converters/skills/md-to-pptx/manifest.json +1 -1
  14. package/packs/converters/skills/md-to-pptx/reference.md +9 -1
  15. package/packs/converters/skills/md-to-pptx/scripts/md_to_pptx.py +35 -2
  16. package/packs/core/pack.json +2 -2
  17. package/packs/{solution-architect → core}/skills/context-graph/SKILL.md +10 -4
  18. package/packs/{solution-architect → core}/skills/context-graph/evals/eval_queries.json +4 -1
  19. package/packs/{solution-architect → core}/skills/context-graph/evals/evals.json +30 -0
  20. package/packs/core/skills/context-graph/evals/files/16/README.md +4 -0
  21. package/packs/core/skills/context-graph/evals/files/16/docs/adr/0001-record-architecture-decisions.md +12 -0
  22. package/packs/core/skills/context-graph/evals/files/16/docs/adr/0002-postgres-for-the-ledger.md +11 -0
  23. package/packs/core/skills/context-graph/evals/files/16/docs/adr/0003-redis-for-ephemeral-state.md +12 -0
  24. package/packs/core/skills/context-graph/evals/files/16/docs/adr/0004-idempotency-keys-for-payment-creation.md +28 -0
  25. package/packs/core/skills/context-graph/evals/files/16/git-history.json +62 -0
  26. package/packs/core/skills/context-graph/evals/files/16/services/payments/api/create.py +7 -0
  27. package/packs/core/skills/context-graph/evals/files/16/services/payments/ledger.py +9 -0
  28. package/packs/core/skills/context-graph/evals/files/17/README.md +5 -0
  29. package/packs/core/skills/context-graph/evals/files/17/docs/adr/0001-record-architecture-decisions.md +9 -0
  30. package/packs/core/skills/context-graph/evals/files/17/docs/adr/0002-email-via-smtp-relay.md +9 -0
  31. package/packs/core/skills/context-graph/evals/files/17/docs/adr/0003-rabbitmq-for-async-messaging.md +9 -0
  32. package/packs/core/skills/context-graph/evals/files/17/docs/adr/0004-email-via-provider-api.md +11 -0
  33. package/packs/core/skills/context-graph/evals/files/17/docs/adr/0005-server-side-template-rendering.md +10 -0
  34. package/packs/core/skills/context-graph/evals/files/17/docs/adr/0006-notification-retry-backoff.md +10 -0
  35. package/packs/core/skills/context-graph/evals/files/17/docs/adr/0007-kafka-for-event-streaming.md +12 -0
  36. package/packs/core/skills/context-graph/evals/files/17/docs/rfcs/RFC-012-unified-retry-policy.md +10 -0
  37. package/packs/core/skills/context-graph/evals/files/17/packages/notification-templates/README.md +4 -0
  38. package/packs/core/skills/context-graph/evals/files/17/packages/notification-templates/invoice_issued.html +2 -0
  39. package/packs/core/skills/context-graph/evals/files/17/packages/notification-templates/welcome.html +2 -0
  40. package/packs/core/skills/context-graph/evals/files/17/services/billing/retry.py +17 -0
  41. package/packs/core/skills/context-graph/evals/files/17/services/notifications/consumer.py +18 -0
  42. package/packs/core/skills/context-graph/evals/files/17/services/notifications/dispatch.py +21 -0
  43. package/packs/core/skills/context-graph/evals/files/17/services/notifications/email/sender.py +17 -0
  44. package/packs/core/skills/context-graph/evals/files/17/services/notifications/render.py +12 -0
  45. package/packs/core/skills/context-graph/evals/files/17/services/notifications/retry.py +14 -0
  46. package/packs/core/skills/context-graph/examples/inputs/order-events/services/notifications/receipts.py +0 -0
  47. package/packs/core/skills/context-graph/examples/inputs/order-events/services/orders/publisher.py +0 -0
  48. package/packs/{solution-architect → core}/skills/context-graph/manifest.json +3 -3
  49. package/packs/{solution-architect → core}/skills/context-graph/reference.md +28 -0
  50. package/packs/{solution-architect → core}/skills/context-graph/scripts/context_graph.py +105 -5
  51. package/packs/dev-team/loops/modernize/LOOP.md +11 -1
  52. package/packs/dev-team/loops/modernize/evals/evals.json +12 -0
  53. package/packs/dev-team/loops/modernize/loop.json +3 -3
  54. package/packs/frontend/skills/bundle-size-review/SKILL.md +5 -2
  55. package/packs/frontend/skills/bundle-size-review/evals/evals.json +30 -0
  56. package/packs/frontend/skills/bundle-size-review/evals/files/8/.browserslistrc +6 -0
  57. package/packs/frontend/skills/bundle-size-review/evals/files/8/babel.config.js +6 -0
  58. package/packs/frontend/skills/bundle-size-review/evals/files/8/dist/stats.json +854 -0
  59. package/packs/frontend/skills/bundle-size-review/evals/files/8/package-lock.json.fixture +73 -0
  60. package/packs/frontend/skills/bundle-size-review/evals/files/8/package.json.fixture +28 -0
  61. package/packs/frontend/skills/bundle-size-review/evals/files/8/src/app/App.jsx +23 -0
  62. package/packs/frontend/skills/bundle-size-review/evals/files/8/src/app/BookingForm.jsx +18 -0
  63. package/packs/frontend/skills/bundle-size-review/evals/files/8/src/app/admin/Reports.jsx +17 -0
  64. package/packs/frontend/skills/bundle-size-review/evals/files/8/src/app/index.jsx +4 -0
  65. package/packs/frontend/skills/bundle-size-review/evals/files/8/src/landing/Hero.jsx +17 -0
  66. package/packs/frontend/skills/bundle-size-review/evals/files/8/src/landing/index.jsx +4 -0
  67. package/packs/frontend/skills/bundle-size-review/evals/files/8/src/polyfills.js +3 -0
  68. package/packs/frontend/skills/bundle-size-review/evals/files/8/src/utils/format.js +4 -0
  69. package/packs/frontend/skills/bundle-size-review/evals/files/8/webpack.config.js +35 -0
  70. package/packs/frontend/skills/bundle-size-review/evals/files/9/.github/workflows/size.yml.fixture +12 -0
  71. package/packs/frontend/skills/bundle-size-review/evals/files/9/build.mjs +17 -0
  72. package/packs/frontend/skills/bundle-size-review/evals/files/9/meta.main.json +100 -0
  73. package/packs/frontend/skills/bundle-size-review/evals/files/9/meta.pr412.json +196 -0
  74. package/packs/frontend/skills/bundle-size-review/evals/files/9/package.json.fixture +17 -0
  75. package/packs/frontend/skills/bundle-size-review/evals/files/9/scripts/check-size.mjs +11 -0
  76. package/packs/frontend/skills/bundle-size-review/evals/files/9/src/api.ts +11 -0
  77. package/packs/frontend/skills/bundle-size-review/evals/files/9/src/composer.tsx +21 -0
  78. package/packs/frontend/skills/bundle-size-review/evals/files/9/src/emoji/data.ts +8 -0
  79. package/packs/frontend/skills/bundle-size-review/evals/files/9/src/emoji/picker.tsx +22 -0
  80. package/packs/frontend/skills/bundle-size-review/evals/files/9/src/styles.ts +11 -0
  81. package/packs/frontend/skills/bundle-size-review/evals/files/9/src/widget.ts +15 -0
  82. package/packs/frontend/skills/bundle-size-review/manifest.json +1 -1
  83. package/packs/solution-architect/loops/design/LOOP.md +14 -5
  84. package/packs/solution-architect/loops/design/evals/evals.json +12 -0
  85. package/packs/solution-architect/loops/design/loop.json +5 -5
  86. package/packs/solution-architect/skills/event-contract-draft/SKILL.md +36 -8
  87. package/packs/solution-architect/skills/event-contract-draft/evals/evals.json +30 -0
  88. package/packs/solution-architect/skills/event-contract-draft/evals/files/6/infra/topics.yaml +10 -0
  89. package/packs/solution-architect/skills/event-contract-draft/evals/files/6/src/checkout/order_events.py +57 -0
  90. package/packs/solution-architect/skills/event-contract-draft/evals/files/7/docs/webhooks.yaml +40 -0
  91. package/packs/solution-architect/skills/event-contract-draft/manifest.json +1 -1
  92. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/1/docs/adr/0001-record-architecture-decisions.md +0 -0
  93. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/1/docs/adr/0002-postgres-as-primary-database.md +0 -0
  94. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/1/docs/adr/0003-use-redis-for-session-storage.md +0 -0
  95. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/1/docs/adr/0004-structured-json-logging.md +0 -0
  96. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/1/docs/adr/0005-session-lifetime-eight-hours.md +0 -0
  97. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/1/docs/adr/0006-move-sessions-to-postgres.md +0 -0
  98. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/1/docs/adr/0007-stateless-signed-session-cookies.md +0 -0
  99. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/1/services/api/__init__.py +0 -0
  100. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/1/services/api/db.py +0 -0
  101. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/1/services/api/logging_setup.py +0 -0
  102. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/1/services/auth/__init__.py +0 -0
  103. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/1/services/auth/cookie_sessions.py +0 -0
  104. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/1/services/auth/redis_sessions.py +0 -0
  105. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/10/README.md +0 -0
  106. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/10/docs/adr/0001-record-architecture-decisions.md +0 -0
  107. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/10/docs/adr/0004-service-auth-hmac-shared-keys.md +0 -0
  108. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/10/docs/adr/0007-secrets-from-vault-agent.md +0 -0
  109. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/10/docs/adr/0011-service-auth-mtls.md +0 -0
  110. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/10/services/billing-api/app.py +0 -0
  111. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/10/services/billing-api/auth/__init__.py +0 -0
  112. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/10/services/billing-api/auth/hmac_verify.py +0 -0
  113. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/10/services/billing-api/config/settings.py +0 -0
  114. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/10/services/ledger-worker/auth/__init__.py +0 -0
  115. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/10/services/ledger-worker/auth/mtls.py +0 -0
  116. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/11/README.md +0 -0
  117. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/11/app/models/base.py +0 -0
  118. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/11/app/models/line_item.py +0 -0
  119. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/11/app/models/order.py +0 -0
  120. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/11/eng/decisions/DR-003-money-as-minor-units.md +0 -0
  121. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/11/eng/decisions/DR-005-orders-typed-columns-only.md +0 -0
  122. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/11/eng/decisions/DR-009-line-items-immutable.md +0 -0
  123. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/11/eng/decisions/DR-014-optional-order-attributes-json.md +0 -0
  124. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/11/eng/decisions/DR-016-order-attributes-table.md +0 -0
  125. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/11/migrations/0007_create_orders.sql +0 -0
  126. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/11/migrations/0012_add_order_attributes.sql +0 -0
  127. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/12/README.md +0 -0
  128. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/12/config/delivery.yaml +0 -0
  129. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/12/config/export.yaml +0 -0
  130. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/12/config/partners.yaml +0 -0
  131. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/12/docs/adr/0001-record-architecture-decisions.md +0 -0
  132. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/12/docs/adr/0002-postgres-delivery-log.md +0 -0
  133. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/12/docs/adr/0003-redis-idempotency-keys.md +0 -0
  134. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/12/docs/adr/0004-retry-policy.md +0 -0
  135. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/12/docs/adr/0005-hmac-signed-payloads.md +0 -0
  136. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/12/docs/adr/0006-postgres-idempotency.md +0 -0
  137. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/12/docs/adr/0007-sftp-batch-export.md +0 -0
  138. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/12/docs/adr/0008-partner-config-in-yaml.md +0 -0
  139. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/12/docs/adr/0009-partner-config-admin-api.md +0 -0
  140. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/12/migrations/003_idempotency_keys.sql +0 -0
  141. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/12/migrations/004_partners.sql +0 -0
  142. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/12/src/delivery/dispatcher.py +0 -0
  143. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/12/src/delivery/retry.py +0 -0
  144. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/12/src/delivery/signing.py +0 -0
  145. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/12/src/export/s3_export.py +0 -0
  146. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/12/src/idempotency/pg_store.py +0 -0
  147. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/12/src/idempotency/redis_store.py +0 -0
  148. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/12/src/partners/loader.py +0 -0
  149. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/12/src/store/delivery_log.py +0 -0
  150. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/13/README.md +0 -0
  151. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/13/config/cache.yaml +0 -0
  152. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/13/config/settings.yaml +0 -0
  153. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/13/docs/decisions/RFC-008-pricing-from-erp.md +0 -0
  154. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/13/docs/decisions/RFC-012-pricing-cache.md +0 -0
  155. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/13/docs/decisions/RFC-015-checkout-price-lock.md +0 -0
  156. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/13/docs/decisions/RFC-019-pricing-service.md +0 -0
  157. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/13/shopfront/catalog/views.py +0 -0
  158. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/13/shopfront/checkout/lock.py +0 -0
  159. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/13/shopfront/pricing/cache.py +0 -0
  160. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/13/shopfront/pricing/client.py +0 -0
  161. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/14/docs/rfcs/0001-idempotency-keys.md +0 -0
  162. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/14/docs/rfcs/0002-processor-retry-policy.md +0 -0
  163. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/14/docs/rfcs/0003-money-in-minor-units.md +0 -0
  164. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/14/docs/rfcs/0004-webhook-signing.md +0 -0
  165. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/14/docs/rfcs/0005-retry-with-jitter.md +0 -0
  166. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/14/docs/rfcs/0006-idempotency-at-gateway.md +0 -0
  167. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/14/pr-summary.md +0 -0
  168. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/14/src/payments/amount.ts +0 -0
  169. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/14/src/payments/charge.ts +0 -0
  170. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/14/src/payments/idempotency.ts +0 -0
  171. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/14/src/payments/retry.ts +0 -0
  172. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/14/src/webhooks/send.ts +0 -0
  173. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/14/src/webhooks/sign.ts +0 -0
  174. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/15/deploy/services.yaml +0 -0
  175. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/15/doc/adr/0001-record-architecture-decisions.md +0 -0
  176. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/15/doc/adr/0002-use-postgresql-for-shipment-data.md +0 -0
  177. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/15/doc/adr/0003-store-vehicle-telemetry-in-cassandra.md +0 -0
  178. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/15/doc/adr/0004-use-redis-for-session-storage.md +0 -0
  179. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/15/doc/adr/0005-use-react-for-dispatcher-dashboard.md +0 -0
  180. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/15/doc/adr/0006-archive-delivered-shipments-to-object-storage.md +0 -0
  181. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/15/doc/adr/0007-move-shipment-records-to-mongodb.md +0 -0
  182. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/15/doc/adr/0008-store-vehicle-telemetry-in-timescaledb.md +0 -0
  183. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/15/doc/adr/0009-stateless-sessions-with-signed-cookies.md +0 -0
  184. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/15/services/api/session.py +0 -0
  185. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/15/services/shipments/db.py +0 -0
  186. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/15/services/shipments/migrations.sql +0 -0
  187. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/15/services/telemetry/store.py +0 -0
  188. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/15/web/dashboard/App.jsx +0 -0
  189. /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
  190. /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
  191. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/2/docs/adr/0001-record-architecture-decisions.md +0 -0
  192. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/2/docs/adr/0002-rabbitmq-for-background-jobs.md +0 -0
  193. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/2/docs/adr/0003-thumbnail-service.md +0 -0
  194. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/2/docs/adr/0004-money-as-integer-minor-units.md +0 -0
  195. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/2/docs/adr/0005-worker-retries.md +0 -0
  196. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/2/docs/adr/0006-fx-amounts-with-four-decimals.md +0 -0
  197. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/2/docs/adr/0007-feature-flags.md +0 -0
  198. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/2/docs/adr/0008-retire-legacy-search.md +0 -0
  199. /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
  200. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/2/libs/money/amount.py +0 -0
  201. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/2/services/checkout/flags.py +0 -0
  202. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/2/services/checkout/publish.py +0 -0
  203. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/2/services/media/resize.py +0 -0
  204. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/2/services/worker/consumer.py +0 -0
  205. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/3/docs/rfcs/0001-how-we-write-rfcs.md +0 -0
  206. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/3/docs/rfcs/0002-payments-service-boundary.md +0 -0
  207. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/3/docs/rfcs/0003-webhook-retries-fixed-interval.md +0 -0
  208. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/3/docs/rfcs/0004-webhook-idempotency-keys.md +0 -0
  209. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/3/docs/rfcs/0005-notification-retries.md +0 -0
  210. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/3/docs/rfcs/0006-webhook-retry-backoff.md +0 -0
  211. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/3/docs/rfcs/0007-webhooks-on-the-job-queue.md +0 -0
  212. /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
  213. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/3/services/notifications/retry.py +0 -0
  214. /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
  215. /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
  216. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/3/services/payments/webhooks/retry.py +0 -0
  217. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/3/services/payments/webhooks/send.py +0 -0
  218. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/4/docs/adr/0001-record-architecture-decisions.md +0 -0
  219. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/4/docs/adr/0002-postgres-as-system-of-record.md +0 -0
  220. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/4/docs/adr/0004-redis-locks-for-scheduler.md +0 -0
  221. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/4/docs/adr/0007-remove-redis.md +0 -0
  222. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/4/docs/adr/0009-money-as-integer-minor-units.md +0 -0
  223. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/4/docs/onboarding.md +0 -0
  224. /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
  225. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/4/src/ledgerline/db/__init__.py +0 -0
  226. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/4/src/ledgerline/invoicing.py +0 -0
  227. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/4/src/ledgerline/money.py +0 -0
  228. /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
  229. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/4/src/ledgerline/scheduler/lock.py +0 -0
  230. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/4/src/ledgerline/scheduler/runner.py +0 -0
  231. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/5/lib/types/index.ts +0 -0
  232. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/5/rfcs/0001-service-layout.md +0 -0
  233. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/5/rfcs/0003-ingest-retry-policy.md +0 -0
  234. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/5/rfcs/0006-shared-retry-library.md +0 -0
  235. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/5/rfcs/0008-outbound-http-client.md +0 -0
  236. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/5/services/export/client.ts +0 -0
  237. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/5/services/export/retry.ts +0 -0
  238. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/5/services/ingest/client.ts +0 -0
  239. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/5/services/ingest/retry.ts +0 -0
  240. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/6/README.md +0 -0
  241. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/6/docs/adr/0001-monorepo-layout.md +0 -0
  242. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/6/docs/adr/0002-billing-on-postgres.md +0 -0
  243. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/6/docs/adr/0004-ledger-sync-nightly-batch.md +0 -0
  244. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/6/docs/rfcs/RFC-0003-invoice-ids.md +0 -0
  245. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/6/docs/rfcs/RFC-0007-invoice-ids-ulid.md +0 -0
  246. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/6/docs/rfcs/RFC-0009-billing-event-store.md +0 -0
  247. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/6/docs/rfcs/RFC-0010-revenue-service.md +0 -0
  248. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/6/libs/money/ulid.py +0 -0
  249. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/6/services/billing/src/events.py +0 -0
  250. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/6/services/billing/src/store.py +0 -0
  251. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/6/services/invoicing/ids/generator.py +0 -0
  252. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/6/services/ledger/README.md +0 -0
  253. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/6/services/ledger/sync.py +0 -0
  254. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/7/README.md +0 -0
  255. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/7/db/queries/orders.sql +0 -0
  256. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/7/db/sharding/router.py +0 -0
  257. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/7/design/decisions/DD-001-postgres-primary.md +0 -0
  258. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/7/design/decisions/DD-002-kafka-event-bus.md +0 -0
  259. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/7/design/decisions/DD-003-no-orm.md +0 -0
  260. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/7/design/decisions/DD-006-tenant-sharding.md +0 -0
  261. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/7/design/decisions/DD-011-orm-for-admin-console.md +0 -0
  262. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/7/pipelines/fanout/router.py +0 -0
  263. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/7/pipelines/ingest/consumer.py +0 -0
  264. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/7/pipelines/ingest/topics.yaml +0 -0
  265. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/7/rfcs/0004-region-sharding.md +0 -0
  266. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/7/services/admin-console/models.py +0 -0
  267. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/7/services/orders/repo.py +0 -0
  268. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/8/README.md +0 -0
  269. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/8/api/ledger.proto +0 -0
  270. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/8/config/http.yaml +0 -0
  271. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/8/config/messaging.yaml +0 -0
  272. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/8/docs/adr/0001-record-architecture-decisions.md +0 -0
  273. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/8/docs/adr/0002-postgresql-for-ledger.md +0 -0
  274. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/8/docs/adr/0003-rabbitmq-domain-events.md +0 -0
  275. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/8/docs/adr/0004-integer-minor-units.md +0 -0
  276. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/8/docs/adr/0005-provider-http-retries.md +0 -0
  277. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/8/docs/adr/0006-openapi-generated-clients.md +0 -0
  278. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/8/docs/adr/0007-kafka-domain-events.md +0 -0
  279. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/8/docs/adr/0008-structured-logging.md +0 -0
  280. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/8/docs/rfcs/RFC-012-idempotency-keys.md +0 -0
  281. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/8/services/ledger/api/idempotency.py +0 -0
  282. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/8/services/ledger/api/server.py +0 -0
  283. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/8/services/ledger/clients/provider.py +0 -0
  284. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/8/services/ledger/db/migrations/0001_init.sql +0 -0
  285. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/8/services/ledger/db/migrations/0008_idempotency_keys.sql +0 -0
  286. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/8/services/ledger/db/migrations/0009_fx_entries.sql +0 -0
  287. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/8/services/ledger/events/publisher.py +0 -0
  288. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/8/services/ledger/logging.py +0 -0
  289. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/8/services/ledger/models/entry.py +0 -0
  290. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/9/.env.example +0 -0
  291. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/9/README.md +0 -0
  292. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/9/docker-compose.yml +0 -0
  293. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/9/docs/decisions/001-session-storage.md +0 -0
  294. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/9/docs/decisions/002-api-rate-limiting.md +0 -0
  295. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/9/docs/decisions/003-jwt-sessions.md +0 -0
  296. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/9/docs/decisions/004-catalog-cache.md +0 -0
  297. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/9/src/catalog/cache.ts +0 -0
  298. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/9/src/catalog/repository.ts +0 -0
  299. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/9/src/jobs/email.ts +0 -0
  300. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/9/src/jobs/export.ts +0 -0
  301. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/9/src/jobs/queue.ts +0 -0
  302. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/9/src/jobs/worker.ts +0 -0
  303. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/9/src/lib/db.ts +0 -0
  304. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/9/src/lib/redis.ts +0 -0
  305. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/9/src/middleware/rateLimit.ts +0 -0
  306. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/9/src/server.ts +0 -0
  307. /package/packs/{solution-architect → core}/skills/context-graph/evals/files/9/src/session/store.ts +0 -0
  308. /package/packs/{solution-architect → core}/skills/context-graph/examples/inputs/order-events/docs/adr/0004-kafka-for-order-events.md +0 -0
  309. /package/packs/{solution-architect → core}/skills/context-graph/examples/inputs/order-events/docs/adr/0006-email-receipts.md +0 -0
  310. /package/packs/{solution-architect → core}/skills/context-graph/examples/inputs/order-events/docs/adr/0009-sqs-for-order-events.md +0 -0
  311. /package/packs/{solution-architect → core}/skills/context-graph/examples/order-events.md +0 -0
@@ -0,0 +1,14 @@
1
+ import time
2
+
3
+ MAX_ATTEMPTS = 5
4
+ BASE_DELAY_SECONDS = 2
5
+
6
+
7
+ def with_retry(fn):
8
+ for attempt in range(1, MAX_ATTEMPTS + 1):
9
+ try:
10
+ return fn()
11
+ except Exception:
12
+ if attempt == MAX_ATTEMPTS:
13
+ raise
14
+ time.sleep(BASE_DELAY_SECONDS * 2 ** (attempt - 1))
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "context-graph",
3
- "version": "0.1.0",
4
- "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
+ "version": "0.2.0",
4
+ "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.",
5
5
  "entrypoint": "SKILL.md",
6
6
  "deps": { "npm": [], "pip": [] },
7
7
  "env": { "required": [], "optional": [] },
@@ -24,7 +24,7 @@
24
24
  "network": [],
25
25
  "commands": ["git"],
26
26
  "files": "named-paths",
27
- "notes": "context_graph.py reads the decision-record folders under --root and checks whether the paths they name exist. With --git it runs `git log` read-only. It writes only to the -o and --mermaid paths."
27
+ "notes": "context_graph.py reads the decision-record folders under --root and checks whether the paths they name exist. With --git it runs `git log` read-only, for commit messages and for the commits that touched each governed path. It writes only to the -o and --mermaid paths."
28
28
  },
29
29
  "model": {
30
30
  "tier": "standard",
@@ -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 that mention each record
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)} for r, g in hits]
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", help="also read commit messages for record ids")
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.1.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",
@@ -61,9 +61,10 @@ Reads the bundler's own record of what it built, and the repo files that decide
61
61
 
62
62
  11. **Reply short, in one message, in this order.**
63
63
  - **First line answers the question in the person's words.** ✅ *"Yes, the reviewer is right: echarts moved from the lazy reports chunk into vendors, so the first load grew from 311,282 to 803,674 bytes (+492,392) while the total fell 1,949."* ❌ *"This confirms it."* / *"Let me break this down."*
64
- - **Then the fixes, numbered by first-load impact:** what it is, the chain, the change, what to check after. Two to four lines each.
64
+ - **Then the fixes, numbered by first-load impact:** what it is, the chain, the change, what to check after. Two to four lines each. The change is the exact edit, in the file the chain names, ready to apply: the import line to replace and its replacement, or a short diff. ✅ *"In `src/components/Header.tsx`, replace `import * as Icons from "@acme/icons"` with `import { SearchIcon, MenuIcon } from "@acme/icons"`."* ❌ *"Consider lazy-loading the locales."*
65
+ - **Edit files only when asked to change the code** ("fix it", "cut it", "open the PR"). Then make the edit, keep everything users rely on working, and say you couldn't rebuild if you couldn't. A question ("why is it big?") gets the edits in the reply, and the files stay untouched.
65
66
  - **One line on lazy chunks** when they matter to the question.
66
- - **Then the guard:** the per-entry budget that would have caught it, with its threshold.
67
+ - **Then the guard:** the per-entry budget that would have caught it, with its threshold, as configuration for a tool the repo has or a named package to add: webpack `performance` (`hints: "error"`, `maxEntrypointSize`), a size-limit entry (`path`, `limit`), or the existing CI file's step. Never a script, flag or config key that doesn't exist; when you're unsure a tool takes an option, leave it out.
67
68
  - **"To confirm"** for what only the user can answer; omit it when empty. No module tables, no full package lists, no tool output pasted in, no flags or options that don't exist. About 250 words.
68
69
 
69
70
  **Non-interactive runs** (subagent, CI, headless): with no stats file, emit `BLOCKED: need the bundler stats file (webpack --json, esbuild --metafile, or rollup-plugin-visualizer raw-data)`. With only built files or a treemap screenshot, report the per-file sizes and emit `BLOCKED: need the stats file to say what is inside each chunk`. Browser targets absent from every project file (after looking) become `[assumption] browser targets unknown; polyfill findings unconfirmed`; a missing budget becomes `[assumption] budget set at the current initial size plus 5%`.
@@ -78,6 +79,8 @@ Reads the bundler's own record of what it built, and the repo files that decide
78
79
 
79
80
  - **The first line answers the question** in the person's terms, with the exact byte figures from the stats.
80
81
  - **The repo was read:** targets, versions and importing files are cited by file name; nothing is called absent without having been looked for.
82
+ - **Each fix is an exact edit to a named file;** files are changed only when the user asked for changes.
83
+ - **The guard is real configuration** for a tool that exists, with no invented flag, script or key.
81
84
  - **One unit system,** bytes with kB = 1000, and the kind of size (module, emitted, post-bundle, compressed) is named once.
82
85
  - **Compressed figures are measured or absent,** never estimated into the lead.
83
86
  - **Initial and lazy are kept apart,** and a lazy chunk is never ranked above an initial-chunk fix.
@@ -103,6 +103,36 @@
103
103
  "Invents nothing that is not in the input: no component names, package versions, file paths, chunk or asset sizes, or webpack/plugin options beyond what the stats and repo files contain. Any tool that would need installing from the network is given as a command, not presented as already run.",
104
104
  "Answers all three numbered questions directly, per app, in the order asked."
105
105
  ]
106
+ },
107
+ {
108
+ "id": 8,
109
+ "held_out": "2026-10-22",
110
+ "prompt": "ok so our welcome page (welcome.html, the `landing` entry) scores terribly on mobile and Priya from product says \"the bundle is huge, it's moment, just rip moment out\". is she right?? I ran the build with stats, it's at dist/stats.json, rest of the repo is here. what do we actually cut first, we want the landing page JS under 200kb",
111
+ "assertions": [
112
+ "Answers both questions directly up front: Priya is wrong about moment for this page, and it names what to cut first for the landing page (app-only vendor code, whole lodash, the legacy polyfills), not a generic bundle audit.",
113
+ "Shows that moment (./node_modules/moment/moment.js plus its locale context modules) is only in the lazy `admin` chunk (admin.e91c5d.js, 82,310 bytes), pulled in by the dynamic import() of ./admin/Reports in src/app/App.jsx, so it is not in the landing entry's first load and removing it would not shrink welcome.html's JavaScript.",
114
+ "Gives the landing entry's first-load JS as 287,994 bytes from the stats (runtime.3f1a9c.js 2,615 + vendors.8c2d41.js 281,467 + landing.a07e93.js 3,912), kept apart from the lazy admin chunk, and says the stats record minified, uncompressed bytes, so it cannot tell from them whether '200kb' (gzip or not, 1000 or 1024) is met; it gives a command to measure compressed size (e.g. `gzip -c dist/vendors.8c2d41.js | wc -c`) instead of making up a gzip figure.",
115
+ "Spots that the single `vendors` cacheGroup in webpack.config.js (fixed name: 'vendors', chunks: 'initial') makes the landing entry download node_modules that only the app entry uses. @northwind/datepicker and both copies of date-fns are reached only from ./src/app/BookingForm.jsx. It recommends dropping the fixed name, or splitting vendors per entry, so landing gets only what its own import chain needs.",
116
+ "Flags the whole-library lodash import and names the chain src/landing/index.jsx -> src/landing/Hero.jsx -> src/utils/format.js (`import _ from 'lodash'`) -> ./node_modules/lodash/lodash.js. It notes that lodash is CommonJS and can't be tree-shaken, and that only startCase, toLower and debounce are used, so it suggests per-method imports (e.g. lodash/startCase), lodash-es or native code.",
117
+ "Flags whatwg-fetch and promise-polyfill, which src/polyfills.js loads into both entries through the webpack entry arrays, as unneeded for the .browserslistrc targets (last 2 Chrome/Firefox/Safari/Edge/iOS versions, all with native fetch and Promise), and treats the IE11 comment as outdated.",
118
+ "Notes that date-fns is bundled twice: 2.30.0 at node_modules/date-fns (from the app's ^2.30.0) and 3.6.0 at node_modules/@northwind/datepicker/node_modules/date-fns, per package-lock.json.fixture/package-lock.json. It says that removing the duplicate means moving the app to date-fns 3 (checking the v2-to-v3 changes for format/parseISO in BookingForm.jsx), and it does not subtract the stats' module sizes, which are pre-minification source bytes (e.g. lodash.js at 544,098, more than the whole 281,467-byte vendors asset), from the emitted bytes as if they were savings. Post-change sizes are given as estimates to confirm by running `npm run build:stats`.",
119
+ "Invents nothing that isn't in the input: no module, package version, file name, byte count, gzip size or webpack option beyond what dist/stats.json and the repo files contain, and no claim that it ran a build."
120
+ ]
121
+ },
122
+ {
123
+ "id": 9,
124
+ "held_out": "2026-10-22",
125
+ "prompt": "We ship Parcelpost Chat, an embeddable support widget customers load with <script type=\"module\" src=\".../widget.js\">. Since PR #412 (emoji picker) went out, two customers have told us the widget adds a lot more to their page weight. But our size check in CI passed, and it actually said widget.js got smaller. The repo here is the PR branch, and I've saved the esbuild metafiles from a main build and a PR build as meta.main.json and meta.pr412.json. What grew and why? Is it on the initial load? And how should we change the size check so this gets caught next time?",
126
+ "assertions": [
127
+ "Answers directly up front: the widget's first-load JS went from 21,312 bytes (dist/widget.js alone on main) to 118,910 bytes on PR #412 (dist/widget.js 10,215 + the statically imported dist/chunk-R4N7QW2L.js 108,695), a growth of 97,598 bytes, and all of it is on the initial load.",
128
+ "Explains why widget.js 'got smaller' (21,312 to 10,215): preact and preact/hooks (10,214 + 1,436 bytesInOutput) moved out of widget.js into the shared chunk because the picker imports them too. This is a relocation, not a saving.",
129
+ "Names the cause and the import chain: src/emoji/emoji-data.json (96,512 bytesInOutput in the shared chunk) reaches the initial load via src/widget.ts -> src/composer.tsx -> src/emoji/data.ts -> src/emoji/emoji-data.json. The static `import { SHORTCODES } from './emoji/data'` added to composer.tsx defeats the dynamic import('./emoji/picker').",
130
+ "Keeps dist/picker-6YB3KD5M.js (5,920 bytes) separate as a lazy chunk, fetched only when the picker opens, and does not count it in the first-load growth.",
131
+ "Proposes a fix that keeps the emoji data off the initial load, such as loading SHORTCODES lazily (when the picker opens or the visitor first types ':') or using a small separate shortcode map, and says to confirm it with a rebuild (e.g. `META_OUT=meta.fix.json npm run build`) instead of stating post-fix byte counts as fact.",
132
+ "Explains that the check passed because scripts/check-size.mjs only measures dist/widget.js against 25,000 bytes. It proposes a per-entry first-load budget computed from the metafile (the entry output plus every output reachable through import-statement imports, excluding dynamic-import), which would have measured 118,910 bytes on PR #412 and failed the 25,000 limit. Lazy chunks can get their own separate limit.",
133
+ "Stays with the minified, uncompressed bytes the metafiles record and doesn't claim gzip/brotli transfer sizes (what customers' 'page weight' probably reflects). If it wants compressed numbers, it gives a command such as `gzip -c dist/chunk-*.js | wc -c` to run.",
134
+ "Invents nothing that isn't in the input: no output file, input module, byte count, esbuild option or CI step beyond what the metafiles and repo files contain, and no claim that it ran the build."
135
+ ]
106
136
  }
107
137
  ]
108
138
  }
@@ -0,0 +1,6 @@
1
+ # Support matrix agreed with product, 2025-03
2
+ last 2 Chrome versions
3
+ last 2 Firefox versions
4
+ last 2 Safari versions
5
+ last 2 Edge versions
6
+ last 2 iOS versions
@@ -0,0 +1,6 @@
1
+ module.exports = {
2
+ presets: [
3
+ ['@babel/preset-env', { modules: false }],
4
+ ['@babel/preset-react', { runtime: 'automatic' }],
5
+ ],
6
+ };