skilldrop-cli 0.16.29 → 0.16.31
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/bin/skilldrop.js +160 -33
- package/catalogue.json +10 -4
- package/model-routing.json +24 -0
- package/package.json +1 -1
- package/packs/ai-engineering/skills/mcp-pairing/SKILL.md +93 -0
- package/packs/ai-engineering/skills/mcp-pairing/evals/eval_queries.json +13 -0
- package/packs/ai-engineering/skills/mcp-pairing/evals/evals.json +76 -0
- package/packs/ai-engineering/skills/mcp-pairing/evals/files/2/mcp.json +32 -0
- package/packs/ai-engineering/skills/mcp-pairing/evals/files/4/.mcp.json +47 -0
- package/packs/ai-engineering/skills/mcp-pairing/evals/files/4/docs/pr-loop.md +12 -0
- package/packs/ai-engineering/skills/mcp-pairing/evals/files/5/docs/oncall-triage.md +18 -0
- package/packs/ai-engineering/skills/mcp-pairing/examples/build-loop-github-jira-datadog.md +69 -0
- package/packs/ai-engineering/skills/mcp-pairing/manifest.json +17 -0
- package/packs/ai-engineering/skills/mcp-pairing/reference.md +136 -0
- package/packs/ai-engineering/skills/mcp-pairing/templates/mcp-pairing.md +43 -0
- package/packs/dev-team/loops/build/LOOP.md +4 -2
- package/packs/dev-team/loops/build/loop.json +2 -2
- package/packs/dev-team/loops/release/LOOP.md +7 -5
- package/packs/dev-team/loops/release/loop.json +2 -2
- package/packs/dev-team/skills/codebase-tour/SKILL.md +115 -0
- package/packs/dev-team/skills/codebase-tour/evals/eval_queries.json +13 -0
- package/packs/dev-team/skills/codebase-tour/evals/evals.json +77 -0
- package/packs/dev-team/skills/codebase-tour/evals/files/1/.github/workflows/ci.yml +23 -0
- package/packs/dev-team/skills/codebase-tour/evals/files/1/README.md +23 -0
- package/packs/dev-team/skills/codebase-tour/evals/files/1/openapi.yaml +21 -0
- package/packs/dev-team/skills/codebase-tour/evals/files/1/package-lock.json +7 -0
- package/packs/dev-team/skills/codebase-tour/evals/files/1/package.json +29 -0
- package/packs/dev-team/skills/codebase-tour/evals/files/1/src/config.ts +20 -0
- package/packs/dev-team/skills/codebase-tour/evals/files/1/src/domain/shipments/filter.ts +24 -0
- package/packs/dev-team/skills/codebase-tour/evals/files/1/src/domain/shipments/repository.ts +15 -0
- package/packs/dev-team/skills/codebase-tour/evals/files/1/src/errors.ts +9 -0
- package/packs/dev-team/skills/codebase-tour/evals/files/1/src/generated/api-types.ts +9 -0
- package/packs/dev-team/skills/codebase-tour/evals/files/1/src/http/handlers/shipments.ts +38 -0
- package/packs/dev-team/skills/codebase-tour/evals/files/1/src/http/routes.ts +7 -0
- package/packs/dev-team/skills/codebase-tour/evals/files/1/src/jobs/export-shipments.ts +22 -0
- package/packs/dev-team/skills/codebase-tour/evals/files/1/src/server.ts +18 -0
- package/packs/dev-team/skills/codebase-tour/evals/files/1/tests/domain/filter.test.ts +16 -0
- package/packs/dev-team/skills/codebase-tour/evals/files/1/tests/http/shipments.test.ts +18 -0
- package/packs/dev-team/skills/codebase-tour/evals/files/1/tsconfig.json +10 -0
- package/packs/dev-team/skills/codebase-tour/evals/files/1/vitest.config.ts +5 -0
- package/packs/dev-team/skills/codebase-tour/evals/files/2/.github/workflows/ci.yml +16 -0
- package/packs/dev-team/skills/codebase-tour/evals/files/2/README.md +16 -0
- package/packs/dev-team/skills/codebase-tour/evals/files/2/noxfile.py +14 -0
- package/packs/dev-team/skills/codebase-tour/evals/files/2/pyproject.toml +22 -0
- package/packs/dev-team/skills/codebase-tour/evals/files/2/src/ledgerline/__init__.py +3 -0
- package/packs/dev-team/skills/codebase-tour/evals/files/2/src/ledgerline/_version.py +4 -0
- package/packs/dev-team/skills/codebase-tour/evals/files/2/src/ledgerline/cli.py +32 -0
- package/packs/dev-team/skills/codebase-tour/evals/files/2/src/ledgerline/detect.py +15 -0
- package/packs/dev-team/skills/codebase-tour/evals/files/2/src/ledgerline/errors.py +2 -0
- package/packs/dev-team/skills/codebase-tour/evals/files/2/src/ledgerline/money.py +14 -0
- package/packs/dev-team/skills/codebase-tour/evals/files/2/src/ledgerline/parsers/__init__.py +8 -0
- package/packs/dev-team/skills/codebase-tour/evals/files/2/src/ledgerline/parsers/acme.py +18 -0
- package/packs/dev-team/skills/codebase-tour/evals/files/2/src/ledgerline/parsers/base.py +24 -0
- package/packs/dev-team/skills/codebase-tour/evals/files/2/src/ledgerline/parsers/harbor.py +13 -0
- package/packs/dev-team/skills/codebase-tour/evals/files/2/tests/fixtures/acme_sample.csv +3 -0
- package/packs/dev-team/skills/codebase-tour/evals/files/2/tests/test_acme.py +23 -0
- package/packs/dev-team/skills/codebase-tour/evals/files/2/tests/test_detect.py +13 -0
- package/packs/dev-team/skills/codebase-tour/evals/files/3/.github/workflows/ci.yml +28 -0
- package/packs/dev-team/skills/codebase-tour/evals/files/3/Makefile +16 -0
- package/packs/dev-team/skills/codebase-tour/evals/files/3/README.md +16 -0
- package/packs/dev-team/skills/codebase-tour/evals/files/3/cmd/tidepool/main.go +24 -0
- package/packs/dev-team/skills/codebase-tour/evals/files/3/go.mod +5 -0
- package/packs/dev-team/skills/codebase-tour/evals/files/3/internal/config/config.go +38 -0
- package/packs/dev-team/skills/codebase-tour/evals/files/3/internal/dispatch/dispatcher.go +52 -0
- package/packs/dev-team/skills/codebase-tour/evals/files/3/internal/dispatch/dispatcher_test.go +42 -0
- package/packs/dev-team/skills/codebase-tour/evals/files/3/internal/dispatch/sender.go +32 -0
- package/packs/dev-team/skills/codebase-tour/evals/files/3/internal/replay/replay.go +31 -0
- package/packs/dev-team/skills/codebase-tour/evals/files/3/internal/store/db.go +35 -0
- package/packs/dev-team/skills/codebase-tour/evals/files/3/internal/store/query.sql +11 -0
- package/packs/dev-team/skills/codebase-tour/evals/files/3/internal/store/query.sql.go +30 -0
- package/packs/dev-team/skills/codebase-tour/evals/files/3/internal/store/schema.sql +6 -0
- package/packs/dev-team/skills/codebase-tour/evals/files/3/internal/store/store_integration_test.go +21 -0
- package/packs/dev-team/skills/codebase-tour/evals/files/3/sqlc.yaml +9 -0
- package/packs/dev-team/skills/codebase-tour/evals/files/4/.env.example +4 -0
- package/packs/dev-team/skills/codebase-tour/evals/files/4/.github/workflows/ci.yml +16 -0
- package/packs/dev-team/skills/codebase-tour/evals/files/4/Makefile +16 -0
- package/packs/dev-team/skills/codebase-tour/evals/files/4/README.md +23 -0
- package/packs/dev-team/skills/codebase-tour/evals/files/4/data/rates.csv +4 -0
- package/packs/dev-team/skills/codebase-tour/evals/files/4/pyproject.toml +19 -0
- package/packs/dev-team/skills/codebase-tour/evals/files/4/scripts/gen_rates.py +29 -0
- package/packs/dev-team/skills/codebase-tour/evals/files/4/src/shipcalc/__init__.py +0 -0
- package/packs/dev-team/skills/codebase-tour/evals/files/4/src/shipcalc/api/__init__.py +0 -0
- package/packs/dev-team/skills/codebase-tour/evals/files/4/src/shipcalc/api/preview.py +14 -0
- package/packs/dev-team/skills/codebase-tour/evals/files/4/src/shipcalc/api/routes.py +36 -0
- package/packs/dev-team/skills/codebase-tour/evals/files/4/src/shipcalc/config.py +15 -0
- package/packs/dev-team/skills/codebase-tour/evals/files/4/src/shipcalc/discounts.py +8 -0
- package/packs/dev-team/skills/codebase-tour/evals/files/4/src/shipcalc/generated/__init__.py +0 -0
- package/packs/dev-team/skills/codebase-tour/evals/files/4/src/shipcalc/generated/rates_table.py +8 -0
- package/packs/dev-team/skills/codebase-tour/evals/files/4/src/shipcalc/main.py +8 -0
- package/packs/dev-team/skills/codebase-tour/evals/files/4/src/shipcalc/shipping.py +18 -0
- package/packs/dev-team/skills/codebase-tour/evals/files/4/src/shipcalc/tax.py +7 -0
- package/packs/dev-team/skills/codebase-tour/evals/files/4/tests/conftest.py +3 -0
- package/packs/dev-team/skills/codebase-tour/evals/files/4/tests/unit/test_discounts.py +11 -0
- package/packs/dev-team/skills/codebase-tour/evals/files/4/tests/unit/test_tax.py +13 -0
- package/packs/dev-team/skills/codebase-tour/evals/files/5/.env.example +3 -0
- package/packs/dev-team/skills/codebase-tour/evals/files/5/.github/workflows/ci.yml +13 -0
- package/packs/dev-team/skills/codebase-tour/evals/files/5/README.md +18 -0
- package/packs/dev-team/skills/codebase-tour/evals/files/5/package.json +13 -0
- package/packs/dev-team/skills/codebase-tour/evals/files/5/packages/api/openapi.json +30 -0
- package/packs/dev-team/skills/codebase-tour/evals/files/5/packages/api/package.json +15 -0
- package/packs/dev-team/skills/codebase-tour/evals/files/5/packages/api/src/db.js +19 -0
- package/packs/dev-team/skills/codebase-tour/evals/files/5/packages/api/src/flags.js +8 -0
- package/packs/dev-team/skills/codebase-tour/evals/files/5/packages/api/src/generated/schema.js +32 -0
- package/packs/dev-team/skills/codebase-tour/evals/files/5/packages/api/src/middleware/validate.js +20 -0
- package/packs/dev-team/skills/codebase-tour/evals/files/5/packages/api/src/routes/bookings.js +15 -0
- package/packs/dev-team/skills/codebase-tour/evals/files/5/packages/api/src/server.js +15 -0
- package/packs/dev-team/skills/codebase-tour/evals/files/5/packages/api/src/services/bookings.js +23 -0
- package/packs/dev-team/skills/codebase-tour/evals/files/5/packages/api/src/services/pricing.js +9 -0
- package/packs/dev-team/skills/codebase-tour/evals/files/5/packages/api/test/pricing.test.js +15 -0
- package/packs/dev-team/skills/codebase-tour/evals/files/5/packages/web/package.json +9 -0
- package/packs/dev-team/skills/codebase-tour/evals/files/5/packages/web/src/bookingForm.js +11 -0
- package/packs/dev-team/skills/codebase-tour/evals/files/5/packages/web/src/format.js +7 -0
- package/packs/dev-team/skills/codebase-tour/evals/files/5/packages/web/test/format.test.js +12 -0
- package/packs/dev-team/skills/codebase-tour/evals/files/5/scripts/gen-schema.mjs +27 -0
- package/packs/dev-team/skills/codebase-tour/examples/refund-window.md +158 -0
- package/packs/dev-team/skills/codebase-tour/manifest.json +47 -0
- package/packs/dev-team/skills/codebase-tour/scripts/repo_facts.py +403 -0
- package/packs/dev-team/skills/codebase-tour/templates/codebase-tour.md +85 -0
- package/packs/dev-team/skills/dependency-upgrade-review/SKILL.md +101 -0
- package/packs/dev-team/skills/dependency-upgrade-review/evals/eval_queries.json +11 -0
- package/packs/dev-team/skills/dependency-upgrade-review/evals/evals.json +77 -0
- package/packs/dev-team/skills/dependency-upgrade-review/evals/files/1/.github/workflows/ci.yml +20 -0
- package/packs/dev-team/skills/dependency-upgrade-review/evals/files/1/pr.diff +87 -0
- package/packs/dev-team/skills/dependency-upgrade-review/evals/files/1/quillmark-CHANGELOG.md +24 -0
- package/packs/dev-team/skills/dependency-upgrade-review/evals/files/1/src/render/comment.js +6 -0
- package/packs/dev-team/skills/dependency-upgrade-review/evals/files/1/src/render/note.js +8 -0
- package/packs/dev-team/skills/dependency-upgrade-review/evals/files/1/test/note.test.js +12 -0
- package/packs/dev-team/skills/dependency-upgrade-review/evals/files/2/.github/workflows/ci.yml +23 -0
- package/packs/dev-team/skills/dependency-upgrade-review/evals/files/2/.github/workflows/release.yml +23 -0
- package/packs/dev-team/skills/dependency-upgrade-review/evals/files/2/pr.diff +32 -0
- package/packs/dev-team/skills/dependency-upgrade-review/evals/files/2/release-notes.md +20 -0
- package/packs/dev-team/skills/dependency-upgrade-review/evals/files/4/.github/workflows/ci.yml +18 -0
- package/packs/dev-team/skills/dependency-upgrade-review/evals/files/4/Dockerfile +8 -0
- package/packs/dev-team/skills/dependency-upgrade-review/evals/files/4/PR.md +36 -0
- package/packs/dev-team/skills/dependency-upgrade-review/evals/files/4/pr.diff +47 -0
- package/packs/dev-team/skills/dependency-upgrade-review/evals/files/4/src/ingest/client.py +24 -0
- package/packs/dev-team/skills/dependency-upgrade-review/evals/files/4/src/ingest/reader.py +32 -0
- package/packs/dev-team/skills/dependency-upgrade-review/evals/files/4/tests/test_reader.py +15 -0
- package/packs/dev-team/skills/dependency-upgrade-review/evals/files/4/upstream/quillstream-CHANGELOG.md +37 -0
- package/packs/dev-team/skills/dependency-upgrade-review/evals/files/5/.github/workflows/ci.yml +22 -0
- package/packs/dev-team/skills/dependency-upgrade-review/evals/files/5/.github/workflows/release.yml +25 -0
- package/packs/dev-team/skills/dependency-upgrade-review/evals/files/5/.nvmrc +1 -0
- package/packs/dev-team/skills/dependency-upgrade-review/evals/files/5/PR-412.md +16 -0
- package/packs/dev-team/skills/dependency-upgrade-review/evals/files/5/PR-413.md +15 -0
- package/packs/dev-team/skills/dependency-upgrade-review/evals/files/5/package.json +21 -0
- package/packs/dev-team/skills/dependency-upgrade-review/evals/files/5/pr-412.diff +26 -0
- package/packs/dev-team/skills/dependency-upgrade-review/evals/files/5/pr-413.diff +84 -0
- package/packs/dev-team/skills/dependency-upgrade-review/evals/files/5/scripts/release.mjs +18 -0
- package/packs/dev-team/skills/dependency-upgrade-review/evals/files/5/upstream/release-notary-CHANGELOG.md +16 -0
- package/packs/dev-team/skills/dependency-upgrade-review/evals/files/5/upstream/setup-toolchain-RELEASES.md +22 -0
- package/packs/dev-team/skills/dependency-upgrade-review/examples/inputs/ledgerline.diff +38 -0
- package/packs/dev-team/skills/dependency-upgrade-review/examples/ledgerline-tidyparse.md +91 -0
- package/packs/dev-team/skills/dependency-upgrade-review/manifest.json +50 -0
- package/packs/dev-team/skills/dependency-upgrade-review/reference.md +99 -0
- package/packs/dev-team/skills/dependency-upgrade-review/scripts/upgrade_diff.py +718 -0
- package/packs/dev-team/skills/refactor-plan/SKILL.md +168 -0
- package/packs/dev-team/skills/refactor-plan/evals/eval_queries.json +46 -0
- package/packs/dev-team/skills/refactor-plan/evals/evals.json +106 -0
- package/packs/dev-team/skills/refactor-plan/evals/files/1/debt-register-row.md +5 -0
- package/packs/dev-team/skills/refactor-plan/evals/files/1/in-flight-prs.md +7 -0
- package/packs/dev-team/skills/refactor-plan/evals/files/1/order_service.py +74 -0
- package/packs/dev-team/skills/refactor-plan/evals/files/1/test-coverage.txt +14 -0
- package/packs/dev-team/skills/refactor-plan/evals/files/3/imports.txt +22 -0
- package/packs/dev-team/skills/refactor-plan/evals/files/3/register.md +3 -0
- package/packs/dev-team/skills/refactor-plan/evals/files/4/docs/open-prs.md +6 -0
- package/packs/dev-team/skills/refactor-plan/evals/files/4/docs/tech-debt-register.md +7 -0
- package/packs/dev-team/skills/refactor-plan/evals/files/4/reports/monthly_revenue.py +44 -0
- package/packs/dev-team/skills/refactor-plan/evals/files/4/src/billing/invoice.py +233 -0
- package/packs/dev-team/skills/refactor-plan/evals/files/4/src/partners/statement.py +29 -0
- package/packs/dev-team/skills/refactor-plan/evals/files/4/tests/billing/test_payments.py +13 -0
- package/packs/dev-team/skills/refactor-plan/evals/files/5/ci-notes.md +5 -0
- package/packs/dev-team/skills/refactor-plan/evals/files/5/open-prs.txt +4 -0
- package/packs/dev-team/skills/refactor-plan/evals/files/5/scripts/nightly-digest.js +36 -0
- package/packs/dev-team/skills/refactor-plan/evals/files/5/src/auth/passwordReset.ts +16 -0
- package/packs/dev-team/skills/refactor-plan/evals/files/5/src/notify/index.ts +2 -0
- package/packs/dev-team/skills/refactor-plan/evals/files/5/src/notify/legacyMailer.ts +149 -0
- package/packs/dev-team/skills/refactor-plan/evals/files/5/src/orders/onOrderShipped.ts +15 -0
- package/packs/dev-team/skills/refactor-plan/evals/files/5/tests/notify/legacyMailer.test.ts +7 -0
- package/packs/dev-team/skills/refactor-plan/evals/files/6/billing-service/src/main/kotlin/billing/DunningJob.kt +29 -0
- package/packs/dev-team/skills/refactor-plan/evals/files/6/billing-service/src/main/kotlin/billing/InvoiceService.kt +157 -0
- package/packs/dev-team/skills/refactor-plan/evals/files/6/billing-service/src/main/kotlin/billing/pdf/InvoicePdfRenderer.kt +36 -0
- package/packs/dev-team/skills/refactor-plan/evals/files/6/billing-service/src/test/kotlin/billing/InvoiceServiceTest.kt +40 -0
- package/packs/dev-team/skills/refactor-plan/evals/files/6/docs/adr-007-persistence.md +26 -0
- package/packs/dev-team/skills/refactor-plan/evals/files/6/docs/open-prs.md +9 -0
- package/packs/dev-team/skills/refactor-plan/evals/files/7/notes/open-work.md +19 -0
- package/packs/dev-team/skills/refactor-plan/evals/files/7/web/src/components/CheckoutPanel.tsx +207 -0
- package/packs/dev-team/skills/refactor-plan/evals/files/7/web/src/components/MiniCart.tsx +33 -0
- package/packs/dev-team/skills/refactor-plan/evals/files/7/web/src/components/__tests__/CheckoutPanel.test.tsx +13 -0
- package/packs/dev-team/skills/refactor-plan/evals/files/7/web/src/pages/OrderReview.tsx +24 -0
- package/packs/dev-team/skills/refactor-plan/examples/search-ranker.md +104 -0
- package/packs/dev-team/skills/refactor-plan/manifest.json +36 -0
- package/packs/dev-team/skills/refactor-plan/reference.md +149 -0
- package/packs/dev-team/skills/refactor-plan/templates/refactor-plan.md +82 -0
- package/packs/dev-team/skills/rollout-plan/SKILL.md +157 -0
- package/packs/dev-team/skills/rollout-plan/evals/eval_queries.json +12 -0
- package/packs/dev-team/skills/rollout-plan/evals/evals.json +109 -0
- package/packs/dev-team/skills/rollout-plan/evals/files/2/metrics-snapshot.txt +11 -0
- package/packs/dev-team/skills/rollout-plan/evals/files/2/pr-description.md +28 -0
- package/packs/dev-team/skills/rollout-plan/evals/files/4/PR_DESCRIPTION.md +24 -0
- package/packs/dev-team/skills/rollout-plan/evals/files/4/metrics_snapshot.txt +7 -0
- package/packs/dev-team/skills/rollout-plan/evals/files/5/docs/tax-engine-v2.md +24 -0
- package/packs/dev-team/skills/rollout-plan/evals/files/6/flags/smart_reminders_v2.yaml +14 -0
- package/packs/dev-team/skills/rollout-plan/evals/files/6/notes/push-pipeline.md +10 -0
- package/packs/dev-team/skills/rollout-plan/evals/files/7/docs/ratelimiter-v2.md +21 -0
- package/packs/dev-team/skills/rollout-plan/evals/files/7/ops/traffic-summary.txt +9 -0
- package/packs/dev-team/skills/rollout-plan/examples/sync-engine-workspaces.md +102 -0
- package/packs/dev-team/skills/rollout-plan/manifest.json +35 -0
- package/packs/dev-team/skills/rollout-plan/reference.md +100 -0
- package/packs/dev-team/skills/rollout-plan/scripts/canary_math.py +261 -0
- package/packs/dev-team/skills/rollout-plan/templates/rollout-plan.md +50 -0
- package/packs/product-manager/loops/discover/LOOP.md +5 -4
- package/packs/product-manager/loops/discover/loop.json +2 -2
- package/packs/product-manager/skills/feedback-synthesis/SKILL.md +182 -0
- package/packs/product-manager/skills/feedback-synthesis/evals/eval_queries.json +46 -0
- package/packs/product-manager/skills/feedback-synthesis/evals/evals.json +76 -0
- package/packs/product-manager/skills/feedback-synthesis/evals/files/1/interview-notes.md +47 -0
- package/packs/product-manager/skills/feedback-synthesis/evals/files/1/nps-verbatims.csv +13 -0
- package/packs/product-manager/skills/feedback-synthesis/evals/files/1/support-tickets.csv +36 -0
- package/packs/product-manager/skills/feedback-synthesis/evals/files/3/churn-exit-survey.csv +11 -0
- package/packs/product-manager/skills/feedback-synthesis/evals/files/3/sales-call-notes.md +55 -0
- package/packs/product-manager/skills/feedback-synthesis/evals/files/4/csat_comments_q3.csv +17 -0
- package/packs/product-manager/skills/feedback-synthesis/evals/files/4/support_tickets_q3.csv +49 -0
- package/packs/product-manager/skills/feedback-synthesis/evals/files/5/app_reviews_last_60_days.csv +32 -0
- package/packs/product-manager/skills/feedback-synthesis/evals/files/5/interview_notes_round1.md +48 -0
- package/packs/product-manager/skills/feedback-synthesis/examples/booking-app-feedback.md +139 -0
- package/packs/product-manager/skills/feedback-synthesis/examples/inputs/booking-app-coded.csv +17 -0
- package/packs/product-manager/skills/feedback-synthesis/manifest.json +53 -0
- package/packs/product-manager/skills/feedback-synthesis/reference.md +89 -0
- package/packs/product-manager/skills/feedback-synthesis/scripts/theme_counts.py +355 -0
- package/packs/product-manager/skills/feedback-synthesis/templates/coding-sheet.csv +6 -0
- package/packs/product-manager/skills/feedback-synthesis/templates/synthesis.md +42 -0
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# Worked example — search ranker with a flag pile
|
|
2
|
+
|
|
3
|
+
## Input given to the skill
|
|
4
|
+
|
|
5
|
+
> TD-07 from our register got the go-ahead. `search/ranker.py` has one 600-line function,
|
|
6
|
+
> `rank(query, docs, **flags)`, driven by 14 boolean flags listed in `RANK_FLAGS` at the top
|
|
7
|
+
> of the file. Register row: "5 of the last 8 search PRs changed `rank()`; INC-540 (Jul,
|
|
8
|
+
> `legacy_tiebreak` reversed the order of equal scores) and INC-552 (Aug, `personalise` is
|
|
9
|
+
> ignored when `boost_recent` is on — not fixed; the web client sends `personalise` only
|
|
10
|
+
> when `boost_recent` is off)." `tests/search/test_ranker.py` covers 4 of the 14 flags. PR
|
|
11
|
+
> #212 "Add `boost_local` flag" is open and edits `rank()`. Next quarter the mobile app needs
|
|
12
|
+
> its own ranking with different weights. Owner: Search team.
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Reply in chat (the default: the user asked how, not for a document)
|
|
17
|
+
|
|
18
|
+
Turn each flag into its own scoring function and build a ranking from a list of them, so the
|
|
19
|
+
mobile ranking is a new list rather than an edit to `rank()`. First step: approval tests for
|
|
20
|
+
every flag in a new test file, this week, because `rank()` has tests for only 4 of 14 flags.
|
|
21
|
+
|
|
22
|
+
**What the code shows**
|
|
23
|
+
- `search/ranker.py:rank` reads all 14 `RANK_FLAGS` in one body; `tests/search/test_ranker.py` covers 4 of them.
|
|
24
|
+
- INC-552 is live behaviour a client depends on: the web client sends `personalise` only when `boost_recent` is off, so the test pins it as-is.
|
|
25
|
+
- #212 is editing `rank()` now; moves wait for it, tests don't.
|
|
26
|
+
|
|
27
|
+
**Steps** (each one PR to main; you can stop after any of them)
|
|
28
|
+
1. [refactor] Approval tests in `tests/search/test_ranker_characterisation.py`: `personalise` + `boost_recent` together (INC-552, as-is), `legacy_tiebreak` on equal scores (INC-540), then each flag alone.
|
|
29
|
+
2. [refactor] After #212 merges: extract the tie-break block into `_tiebreak`, unchanged; it caused INC-540.
|
|
30
|
+
3. [refactor] Add `rank_with_profile(query, docs, profile)`; `rank(**flags)` builds a profile and delegates. The mobile team can start from here.
|
|
31
|
+
4. [refactor] Move the 15 flags (with `boost_local`) into scoring functions, five per PR, three PRs.
|
|
32
|
+
5. [refactor] Move callers to `rank_with_profile`, delete `**flags`, add a CI check that forbids new flags.
|
|
33
|
+
|
|
34
|
+
**Target:** search PRs that edit `rank()`, 5 of the last 8 → the mobile ranking ships without editing it. **Stop point:** every flag is a scoring function (step 4); step 5 is scheduled but optional.
|
|
35
|
+
**Not touched:** INC-552 behaviour, `RANK_FLAGS` names, weights, the API response. Anything found during the work that isn't a step goes to the debt register.
|
|
36
|
+
**To confirm:** does anything outside this repo call `rank()` with flags? It decides whether step 5 deletes `**flags` or keeps a shim.
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## The same plan as a document (when the user asks for one)
|
|
41
|
+
|
|
42
|
+
# Refactor plan: take the scoring rules out of `rank()` so new rankings stop editing it
|
|
43
|
+
|
|
44
|
+
**Target:** search PRs that edit `rank()` — baseline 5 of the last 8 (TD-07) → the mobile
|
|
45
|
+
ranking ships without editing `rank()`.
|
|
46
|
+
**Stop point:** every flag in `RANK_FLAGS` is its own scoring function, and a ranking is
|
|
47
|
+
built from a list of them. Removing `**flags` from `rank()` is scheduled but outside the stop
|
|
48
|
+
point (step 7).
|
|
49
|
+
**First step:** characterisation tests for all 14 flags, then the expand step for a
|
|
50
|
+
profile-based `rank` — first because the mobile ranking next quarter is the work this
|
|
51
|
+
unblocks.
|
|
52
|
+
**Type:** every step is a refactor; ranking output is identical throughout. The mobile
|
|
53
|
+
weights are feature work built on step 3.
|
|
54
|
+
**Not touched:** INC-552 behaviour, `RANK_FLAGS` names, the search API response shape.
|
|
55
|
+
[assumption] `rank()` is called only from inside this repo; see To confirm.
|
|
56
|
+
|
|
57
|
+
## Steps
|
|
58
|
+
|
|
59
|
+
| # | Change | Technique | Verified by | Rollback (live side) | Risk | Size | Owner |
|
|
60
|
+
|---|---|---|---|---|---|---|---|
|
|
61
|
+
| 1 | Approval tests in a new `tests/search/test_ranker_characterisation.py`: `personalise` + `boost_recent` together, `legacy_tiebreak` on equal scores, then each flag alone | characterisation tests | new tests pass on current code | revert PR | none, tests only | M | Search team |
|
|
62
|
+
| — | **Wait for #212 to merge** | — | — | — | — | — | — |
|
|
63
|
+
| 2 | Extract the tie-break block into `_tiebreak(docs, legacy)` in `ranker.py`, unchanged | extract | step 1 tests | revert PR | ordering of equal scores (INC-540) | S | Search team |
|
|
64
|
+
| 3 | Add `rank_with_profile(query, docs, profile)`; `rank(**flags)` builds a profile from the flags and delegates | parallel change (expand) | step 1 tests; `rank` output identical | revert PR; `rank` is the only caller | flag-to-profile mapping misses a flag | M | Search team |
|
|
65
|
+
| 4–6 | Move flags into scoring functions, five, five and five per PR (15 with `boost_local`) | extract, behind step 3 | step 1 tests + #212's tests | revert that batch's PR | interaction between flags in one batch | M each | Search team |
|
|
66
|
+
| 7 | Move callers to `rank_with_profile`; delete `**flags`; CI check forbids new flags | parallel change (contract) | suite + check | revert PR | caller outside the repo | S | Search team |
|
|
67
|
+
|
|
68
|
+
Stop after any step: each leaves `rank()` shorter and better tested. Two entry points exist
|
|
69
|
+
from step 3 to step 7 and nowhere else; step 7 is scheduled for the sprint after step 6.
|
|
70
|
+
|
|
71
|
+
## Step detail (step 3 shown)
|
|
72
|
+
|
|
73
|
+
- **Change:** new `rank_with_profile` in `search/ranker.py`; `rank` keeps its signature and builds a profile from `**flags`.
|
|
74
|
+
- **Technique:** parallel change — callers move over later, so the old entry point stays live.
|
|
75
|
+
- **Verified by:** every step 1 approval test, run through `rank` (unchanged callers).
|
|
76
|
+
- **Rollback:** revert the PR; nothing calls `rank_with_profile` yet.
|
|
77
|
+
- **Risk:** a flag dropped from the profile mapping changes ranking silently; the per-flag approval tests catch it.
|
|
78
|
+
- **Size:** M. **Owner:** Search team.
|
|
79
|
+
- **Clashes:** none once #212 has merged.
|
|
80
|
+
|
|
81
|
+
## Characterisation tests
|
|
82
|
+
|
|
83
|
+
| Function | Pin | Includes current bug? | Lives in |
|
|
84
|
+
|---|---|---|---|
|
|
85
|
+
| `rank` | each of the 14 flags alone over a recorded query set | no | `tests/search/test_ranker_characterisation.py` (new, so it can't conflict with #212) |
|
|
86
|
+
| `rank` | equal scores with `legacy_tiebreak` on and off | no (INC-540 is fixed) | same |
|
|
87
|
+
| `rank` | `personalise` + `boost_recent` together | **yes: personalise ignored, pinned as-is (INC-552)** | same |
|
|
88
|
+
|
|
89
|
+
## Clashes with feature work
|
|
90
|
+
|
|
91
|
+
| In-flight work | Shared files / functions | Decision | Effect on the plan |
|
|
92
|
+
|---|---|---|---|
|
|
93
|
+
| #212 Add `boost_local` | `search/ranker.py::rank` | wait | step 2 starts after #212 merges; `boost_local` joins batch 4–6 |
|
|
94
|
+
| Mobile ranking (next quarter) | `rank` | fold in | the mobile team builds its profile on `rank_with_profile` once step 6 lands |
|
|
95
|
+
|
|
96
|
+
## Scope fence
|
|
97
|
+
|
|
98
|
+
- **Not touched:** search API response shape, `RANK_FLAGS` names, weights.
|
|
99
|
+
- **Pulled out of this plan:** fixing INC-552 is a behaviour change; it is a separate ticket, and the web client workaround has to be removed in the same change.
|
|
100
|
+
- **Rule:** anything found during the work that isn't in a step goes to the debt register as a new item, not into this plan.
|
|
101
|
+
|
|
102
|
+
## To confirm
|
|
103
|
+
|
|
104
|
+
- Does anything outside this repo call `rank()` with flags? It decides whether step 7 can delete `**flags` or must keep a shim.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "refactor-plan",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Plan paying down one piece of technical debt as a refactor that serves the goal it is blocking — characterisation tests on the riskiest behaviour first, then small steps each merged to main with main releasable after every one, pure refactors kept apart from the behaviour changes the goal needs, a scope fence, a stop point, a technique per step (extract, branch by abstraction, parallel change, strangler fig, mikado) and the clashes with in-flight feature work. Use when the user asks for a refactor plan, how to untangle, break up, replace or clean up a module without a rewrite, how to pay down a tech-debt item, \"how do we refactor X without stopping feature work\", \"how do we get off X without a big bang\", or wants a strangler-fig or mikado plan.",
|
|
5
|
+
"entrypoint": "SKILL.md",
|
|
6
|
+
"deps": { "npm": [], "pip": [] },
|
|
7
|
+
"env": { "required": [], "optional": [] },
|
|
8
|
+
"related": [
|
|
9
|
+
"adr-generator",
|
|
10
|
+
"design-doc",
|
|
11
|
+
"feature-implement-loop",
|
|
12
|
+
"migration-plan",
|
|
13
|
+
"pre-merge-review",
|
|
14
|
+
"tech-debt-register",
|
|
15
|
+
"user-story-splitter"
|
|
16
|
+
],
|
|
17
|
+
"handoff": [
|
|
18
|
+
{
|
|
19
|
+
"to": "feature-implement-loop",
|
|
20
|
+
"when": "the plan is agreed and a step is ready to build",
|
|
21
|
+
"purpose": "Implement one step, using its Change and Verified-by fields as the acceptance criteria.",
|
|
22
|
+
"fallback": "If feature-implement-loop is absent, implement the step as one PR, run the characterisation tests and the existing suite before and after, and merge only when both are green with no test edited to pass."
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"to": "tech-debt-register",
|
|
26
|
+
"when": "planning or a step turns up debt outside the scope fence, or the stop point is reached",
|
|
27
|
+
"purpose": "Record new debt as its own item and update the paid-down item's cost and decision.",
|
|
28
|
+
"fallback": "If tech-debt-register is absent, list each new item with where it lives, the evidence of its cost, and a decision (pay now, schedule, accept, watch) at the end of the plan."
|
|
29
|
+
}
|
|
30
|
+
],
|
|
31
|
+
"tags": ["refactoring", "tech-debt", "legacy-code", "planning", "engineering"],
|
|
32
|
+
"model": {
|
|
33
|
+
"tier": "standard",
|
|
34
|
+
"rationale": "Sequencing behaviour-preserving steps follows known patterns (characterise, parallel change, strangle); escalate via ambiguous-input for contested rewrites."
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
# Refactoring techniques: when each fits and what rollback means
|
|
2
|
+
|
|
3
|
+
**Refactor or behaviour change.** A refactor step keeps output identical and is proven by the
|
|
4
|
+
pinned tests passing unchanged. A behaviour change step changes a pinned value on purpose,
|
|
5
|
+
says which, and has its own rollback (usually a flag). A plan names the type of every step and
|
|
6
|
+
never puts both in one PR.
|
|
7
|
+
|
|
8
|
+
Every technique here keeps main releasable between PRs. Pick the smallest one that fits the
|
|
9
|
+
step; a heavier technique than the step needs adds seams nobody removes.
|
|
10
|
+
|
|
11
|
+
## Characterisation tests
|
|
12
|
+
|
|
13
|
+
**Fits:** code with no tests on the paths callers use, before any step touches it.
|
|
14
|
+
(Michael Feathers, *Working Effectively with Legacy Code*.)
|
|
15
|
+
|
|
16
|
+
**Step shape:** one PR that adds tests only. No production code changes, except a seam
|
|
17
|
+
needed to make the code testable (an injected clock, a parameter for a hard-wired client),
|
|
18
|
+
and that seam goes in its own PR first.
|
|
19
|
+
|
|
20
|
+
**What to pin:**
|
|
21
|
+
- The outputs for the inputs callers actually send — read the callers, not the docstring.
|
|
22
|
+
- Boundary inputs: empty, zero, negative, very large, null, duplicate, unsorted.
|
|
23
|
+
- Rounding, ordering, timezone and locale behaviour.
|
|
24
|
+
- Error behaviour: which exception, which error code, what was half-written before it raised.
|
|
25
|
+
- Known bugs, as they behave today. Name the test for what it is
|
|
26
|
+
(`test_tax_rounds_per_line_current_behaviour`) and point it at the register item that
|
|
27
|
+
records the bug.
|
|
28
|
+
|
|
29
|
+
**When output is too wide to assert by hand:** an approval (golden-master) test. Record
|
|
30
|
+
outputs for a set of real-shape inputs, store them next to the test, and diff on every run.
|
|
31
|
+
Scrub timestamps and ids first or the test flaps.
|
|
32
|
+
|
|
33
|
+
**Rollback:** revert the PR. Nothing in production changed.
|
|
34
|
+
|
|
35
|
+
**Open PRs on the same file:** never a reason to wait. The tests touch no production code, so
|
|
36
|
+
they go in a new test file and merge now. If the open PR changes pinned behaviour on purpose,
|
|
37
|
+
its author updates the pinned value in that PR, which makes the change visible in review.
|
|
38
|
+
**Order:** the behaviour behind past incidents and the branches comments warn about first,
|
|
39
|
+
the quiet paths last.
|
|
40
|
+
|
|
41
|
+
**Failure mode:** asserting what the code should do. The test fails against the current code
|
|
42
|
+
and the team deletes it.
|
|
43
|
+
|
|
44
|
+
## Extract / inline / move
|
|
45
|
+
|
|
46
|
+
**Fits:** a cohesive piece (a function, a group of methods sharing state) that can leave its
|
|
47
|
+
current home in one PR without changing callers.
|
|
48
|
+
|
|
49
|
+
**Step shape:** two PRs when the code moves files. PR A moves it unchanged and leaves the old
|
|
50
|
+
name pointing at the new one (re-export, delegating method). PR B changes it. Callers move
|
|
51
|
+
to the new name in later PRs or in the contract step.
|
|
52
|
+
|
|
53
|
+
**Rollback:** revert the PR. A pure move reverts cleanly.
|
|
54
|
+
|
|
55
|
+
**Failure mode:** move and edit in one PR. The reviewer can't see the edit in a 900-line diff.
|
|
56
|
+
|
|
57
|
+
## Branch by abstraction
|
|
58
|
+
|
|
59
|
+
**Fits:** replacing a dependency (a client, a store, a library) that many callers use, when
|
|
60
|
+
the swap takes more than one PR. (Paul Hammant; Martin Fowler's write-up.)
|
|
61
|
+
|
|
62
|
+
**Step shape:**
|
|
63
|
+
1. Introduce an abstraction over the current dependency; route callers through it in
|
|
64
|
+
batches. Behaviour unchanged.
|
|
65
|
+
2. Build the new implementation behind the same abstraction, dark.
|
|
66
|
+
3. Switch which implementation is live with a flag or config, for a slice first.
|
|
67
|
+
4. Remove the old implementation and, if it adds nothing, the abstraction.
|
|
68
|
+
|
|
69
|
+
**Rollback:** before step 3, revert the PR. From step 3, flip the flag back — the plan names
|
|
70
|
+
the flag and which side is live after each step.
|
|
71
|
+
|
|
72
|
+
**Failure mode:** stopping after step 3. Two implementations stay forever.
|
|
73
|
+
|
|
74
|
+
## Parallel change (expand–contract) at code level
|
|
75
|
+
|
|
76
|
+
**Fits:** changing a function signature, an interface or a data shape that many callers
|
|
77
|
+
use. (Danilo Sato's write-up of the pattern on Martin Fowler's site.)
|
|
78
|
+
|
|
79
|
+
**Step shape:**
|
|
80
|
+
1. **Expand** — add the new signature alongside the old; the old delegates to the new.
|
|
81
|
+
2. **Migrate** — move callers in batches, one PR per batch.
|
|
82
|
+
3. **Contract** — remove the old signature once nothing calls it, and add a check (lint
|
|
83
|
+
rule, test, grep in CI) that stops it coming back.
|
|
84
|
+
|
|
85
|
+
**Rollback:** each batch reverts on its own. The old signature stays live until contract.
|
|
86
|
+
|
|
87
|
+
**Failure mode:** no contract step. Schedule it within two steps of the last migrate batch.
|
|
88
|
+
|
|
89
|
+
**Caller contracts:** the same shape removes a side effect a caller relies on (a mutated
|
|
90
|
+
argument, a swallowed error reported as success). Expand: give the caller what it reads
|
|
91
|
+
another way. Migrate: move that caller. Contract: remove the side effect. Removing it is a
|
|
92
|
+
behaviour change and gets its own step; it is never folded into an extract.
|
|
93
|
+
|
|
94
|
+
This is the code-level version. When the change crosses a datastore with a backfill, it is a
|
|
95
|
+
migration, not a refactor, and it gets its own migration plan.
|
|
96
|
+
|
|
97
|
+
## Strangler fig
|
|
98
|
+
|
|
99
|
+
**Fits:** replacing a whole module or service piece by piece, when there is an edge (a
|
|
100
|
+
router, a facade, an entry function) where each call can go to old or new. (Martin Fowler.)
|
|
101
|
+
|
|
102
|
+
**Step shape:**
|
|
103
|
+
1. Put a facade at the edge; everything still goes to the old code.
|
|
104
|
+
2. Move one capability at a time to new code; the facade routes that capability to it.
|
|
105
|
+
3. When the old code serves nothing, delete it.
|
|
106
|
+
|
|
107
|
+
**Rollback:** route the capability back to the old code at the facade. The plan names, per
|
|
108
|
+
step, which capabilities the new code serves.
|
|
109
|
+
|
|
110
|
+
**Failure mode:** using it for something one extract would do. A facade over a single
|
|
111
|
+
function is a seam nobody needs.
|
|
112
|
+
|
|
113
|
+
## Mikado method
|
|
114
|
+
|
|
115
|
+
**Fits:** tangled dependencies where each attempted change breaks on something that must
|
|
116
|
+
change first, and nobody knows the full list. (Ola Ellnestam and Daniel Brolund, *The Mikado
|
|
117
|
+
Method*.)
|
|
118
|
+
|
|
119
|
+
**Step shape:**
|
|
120
|
+
1. Write the goal at the top of a graph ("`billing` imports nothing from `web`").
|
|
121
|
+
2. Try it naively. Note every breakage as a prerequisite node. **Revert.**
|
|
122
|
+
3. Repeat for each prerequisite until reaching leaves that change cleanly.
|
|
123
|
+
4. Ship the leaves first, one PR each, working back up to the goal.
|
|
124
|
+
|
|
125
|
+
**Rollback:** each leaf is one PR; revert it. The exploration commits are never merged.
|
|
126
|
+
|
|
127
|
+
**Failure mode:** keeping the naive attempt and fixing forward. That is the big-bang rewrite
|
|
128
|
+
arrived at by accident.
|
|
129
|
+
|
|
130
|
+
When an import graph or a compiler error list is already in the input, the first pass of the
|
|
131
|
+
graph comes from it: each forbidden edge is a node, and edges with no further dependency are
|
|
132
|
+
the leaves.
|
|
133
|
+
|
|
134
|
+
## Measuring the target
|
|
135
|
+
|
|
136
|
+
Pick one metric, two at most, and take the baseline before step 1.
|
|
137
|
+
|
|
138
|
+
| Metric | Read it from |
|
|
139
|
+
|---|---|
|
|
140
|
+
| Share of feature PRs touching the file | git log over merged PRs in a window |
|
|
141
|
+
| Files touched per typical change | git log for the last N changes in the area |
|
|
142
|
+
| Change lead time for the module | PR open to merge for PRs touching it |
|
|
143
|
+
| Review time for PRs touching it | the code host's PR data |
|
|
144
|
+
| Test run time for the module's suite | CI timings |
|
|
145
|
+
| Defects or incidents traced to it | the incident log or bug tracker |
|
|
146
|
+
| Forbidden imports or direct call sites | a grep or import-linter count, enforced in CI at the end |
|
|
147
|
+
|
|
148
|
+
A count that can be enforced in CI (forbidden imports, direct call sites) makes the best stop
|
|
149
|
+
point: the last step turns the count into a check, so the debt can't come back.
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# Refactor-plan template
|
|
2
|
+
|
|
3
|
+
Two shapes. Use the chat shape unless the user asked for a document (a file, "a proper plan",
|
|
4
|
+
something to take to a meeting) or the plan runs past about ten steps. Never send both at
|
|
5
|
+
full length: when a document is written, the reply is its lead, the findings and the path.
|
|
6
|
+
|
|
7
|
+
## Chat shape (default)
|
|
8
|
+
|
|
9
|
+
```markdown
|
|
10
|
+
{Approach in one sentence: the technique and the end state the goal needs.}
|
|
11
|
+
First step: {the step} — {why it goes first}.
|
|
12
|
+
|
|
13
|
+
**What the code shows**
|
|
14
|
+
- `{file}:{function}` {a caller contract, a lying return value, a copy outside the module, real test coverage, a stale signal}
|
|
15
|
+
- … (three to six bullets, each one that changes the plan)
|
|
16
|
+
|
|
17
|
+
**Steps** (each one PR to main; you can stop after any of them)
|
|
18
|
+
1. [refactor] Characterisation tests for {riskiest behaviour first: …}, in {new test file}. {why}
|
|
19
|
+
2. [refactor] {change} — {why}. {clash or rollback, if it matters}
|
|
20
|
+
3. [behaviour change] {change the goal needs} — after step {n} makes it safe; {caller moved first}; rollback: {flag off / revert}.
|
|
21
|
+
…
|
|
22
|
+
n. {contract: delete the old path; add a check so it can't come back}
|
|
23
|
+
|
|
24
|
+
**Target:** {metric}: {baseline from the files, or "to measure"} → {goal}. **Stop point:** {checkable state}.
|
|
25
|
+
**Not touched:** {fence, one line}. Anything found during the work that isn't a step goes to the debt register.
|
|
26
|
+
**To confirm:** {at most three questions or checks to run}
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Document shape (asked for, or large)
|
|
30
|
+
|
|
31
|
+
```markdown
|
|
32
|
+
# Refactor plan: {the debt, in one line — "split OrderService so order features stop editing one file"}
|
|
33
|
+
|
|
34
|
+
{Approach in one sentence.} **First step:** {step} — first because {the work it unblocks}.
|
|
35
|
+
|
|
36
|
+
**Goal:** {the user's words} → **End state:** {the structure that goal needs}
|
|
37
|
+
**Target:** {metric}: {baseline from the input, or "not measured; step 0 measures it"} → {goal}
|
|
38
|
+
**Stop point:** {the "done enough" line, checkable — and what stays as it is after it}
|
|
39
|
+
**Not touched:** {modules, contracts, behaviours outside the fence}
|
|
40
|
+
[assumption] {anything inferred; one line each}
|
|
41
|
+
|
|
42
|
+
## What the code shows
|
|
43
|
+
|
|
44
|
+
- `{file}:{function}` — {finding, and the step it shapes}
|
|
45
|
+
|
|
46
|
+
## Steps
|
|
47
|
+
|
|
48
|
+
| # | Change | Type | Technique (why) | Verified by | Rollback (live side) | Risk | Size | Owner |
|
|
49
|
+
|---|---|---|---|---|---|---|---|---|
|
|
50
|
+
| 1 | Characterisation tests for {incident-prone behaviour first} in {new test file} | refactor | characterisation tests | pass on current code | revert PR | none, tests only | S | {role} |
|
|
51
|
+
| 2 | Move {function} to {file} unchanged; old name re-exports | refactor | move | step 1 tests + suite | revert PR | import cycles | S | {role} |
|
|
52
|
+
| 3 | Route {callers} through {interface} | refactor | branch by abstraction | step 1 tests | revert PR; old impl live | missed call site | M | {role} |
|
|
53
|
+
| 4 | {behaviour the goal needs} behind flag `{name}` | behaviour change | {technique} | updated pins, reviewed | flag off; old side live | {what} | M | {role} |
|
|
54
|
+
| … | | | | | | | | |
|
|
55
|
+
| n | Contract: delete {old path}; add {CI check} | refactor | parallel change (contract) | suite + check green | revert PR | caller outside the repo | S | {role} |
|
|
56
|
+
|
|
57
|
+
Stop after any step: each leaves the code better than before. Two paths exist from step {x}
|
|
58
|
+
to step {y}; nowhere else.
|
|
59
|
+
|
|
60
|
+
## Characterisation tests
|
|
61
|
+
|
|
62
|
+
| Function | Pin (riskiest first) | Includes current bug? | Lives in |
|
|
63
|
+
|---|---|---|---|
|
|
64
|
+
| `{file}:{function}` | {inputs → outputs, edge cases} | {yes: "{bug}", pinned as-is / no} | {new test file if an open PR edits the existing one} |
|
|
65
|
+
|
|
66
|
+
## Clashes with feature work
|
|
67
|
+
|
|
68
|
+
| In-flight work | Shared `file:function` | Decision | Effect on the plan |
|
|
69
|
+
|---|---|---|---|
|
|
70
|
+
| {PR or feature} | {…} | land first / wait / fold in / through the seam / freeze window | {…} |
|
|
71
|
+
|
|
72
|
+
## Scope fence
|
|
73
|
+
|
|
74
|
+
- **Not touched:** {list}
|
|
75
|
+
- **Pulled out of this plan:** {behaviour changes the goal doesn't need, and where each goes instead}
|
|
76
|
+
- **Copies outside the module:** {in or out, by name; if out, the plan doesn't claim them fixed}
|
|
77
|
+
- **Rule:** anything found during the work that isn't in a step goes to the debt register as a new item, not into this plan.
|
|
78
|
+
|
|
79
|
+
## To confirm
|
|
80
|
+
|
|
81
|
+
- {the facts the plan needs and the input didn't give, as checks to run}
|
|
82
|
+
```
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: rollout-plan
|
|
3
|
+
description: Plan the progressive release of a code change behind a feature flag or canary — the exposure ladder (internal, beta cohort, 1%, 5%, 25%, 50%, 100%, or what the traffic supports), and for each step who is exposed, decision and guardrail metrics with a baseline and a threshold against a control cohort, the minimum traffic and time before the numbers mean anything, a promote / hold / roll back rule set in advance, who watches and what aborts automatically; then what turning the flag off does not undo, and who removes the flag by when. Use when the user asks for a rollout plan, canary plan, ramp schedule, progressive delivery or percentage rollout, "how long should the canary run", "when do we go from 5% to 25%", or how to turn on a flagged feature safely.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# rollout-plan
|
|
7
|
+
|
|
8
|
+
Plans how a code path already behind a flag or canary reaches users: in what steps, judged
|
|
9
|
+
by which numbers, decided by which rule, and undone how. The plan's unit is **exposure of a
|
|
10
|
+
code path to users**. Moving data or changing a schema is a migration and belongs to
|
|
11
|
+
`migration-plan`; whether the change may go live at all is `launch-readiness`'s go/no-go.
|
|
12
|
+
This plan starts once both of those are settled, or names them as open.
|
|
13
|
+
|
|
14
|
+
## How to respond
|
|
15
|
+
|
|
16
|
+
**Four rules come before the steps and outrank them:**
|
|
17
|
+
|
|
18
|
+
- **Answer what was asked, first.** Open with the answer, the decision or the artifact, in plain words. Scores, matrices, frameworks and tags come after it, and anything that doesn't change the answer is cut.
|
|
19
|
+
- **Use only what you were given.** Don't add facts, names, numbers, incidents, history, steps or sections the input doesn't contain. What you need and don't have is left out of the artifact and listed once at the end under "To confirm".
|
|
20
|
+
- **Deliver from what you have.** When the request gives you something to work on, state your assumptions in a line and produce the result. When it gives you nothing to work on, ask for it in one or two plain sentences and say what you will do once you have it.
|
|
21
|
+
- **Write for someone who has never heard of this skill.** No skill names, no paths or scripts from this folder, no internal terms, and nothing about how the run was set up. A next step is one plain sentence at the end that describes the work.
|
|
22
|
+
- **For this skill:** the plan is built around the input's binding constraint, never around a stock ladder. Sample sizes are working, not the answer: state what they mean for the plan in one sentence, with the inputs that produced them.
|
|
23
|
+
|
|
24
|
+
1. **Answer the question in the first two lines.** "Who's right?" gets a name. "Is 1% Monday to 100%
|
|
25
|
+
Thursday OK?" gets yes or no. "How long should the canary run?" gets a duration. Then the one
|
|
26
|
+
fact from the input that settles it, and the shape of the plan.
|
|
27
|
+
✅ *"The payroll lead is right: 1% of employers for an hour sees no pay run at all, so a clean hour proves nothing. Shadow the new calculator on Friday's run, then move named employers through the next two pay cycles."*
|
|
28
|
+
❌ *"Each arm needs 9,412 units (19 days at 1%)."* (A statistic the reader didn't ask for, built on a rate they never gave.)
|
|
29
|
+
|
|
30
|
+
2. **Find the binding constraint before drawing a ladder.** Read the input for the fact that
|
|
31
|
+
decides pacing, and name it in one line. The common ones:
|
|
32
|
+
- **A partner or quota cap** ("stay under 500 new accounts a day"): convert each step's exposure into the capped unit (exposure × volume × take rate), state the take rate as an `[assumption]` if it isn't given, and hold the step that would cross it.
|
|
33
|
+
- **A calendar cycle** (billing run on the 1st, payroll, month-end close, a weekly batch): the evidence arrives on the cycle, not by the hour. Count the cycles left before the deadline and place the steps on them.
|
|
34
|
+
- **A deadline or freeze** ("before the holiday freeze on Dec 1", "before year-end close"): the schedule works back from it. If the evidence the steps need doesn't fit, say so in the opening, not in a footnote.
|
|
35
|
+
- **Segments the flag targets** (tenants, accounts, regions, plans, allow-lists) or a flag that cannot target by %: steps are named cohorts chosen for a reason, not hash buckets.
|
|
36
|
+
- **A result checkable without exposure** (tax, prices, ranking, balances): a shadow run beside the old path, writing nothing a customer or downstream system reads, is the main gate, whether the design already has one or the plan adds it.
|
|
37
|
+
- **Low volume**: when a step sees single-digit counts, percentages and power calculations mean nothing. Judge in counts (zero tolerance, "no more than 2 per run") and widen the step to a segment or a cycle.
|
|
38
|
+
The fixed internal → 1% → 5% → 25% → 50% → 100% ladder is the default **only** when traffic is high and nothing above shapes the pacing.
|
|
39
|
+
|
|
40
|
+
3. **Pin the mechanics.** The code path; the flag (key, where it is evaluated: server, client,
|
|
41
|
+
edge, and what it can target); the **randomisation unit** (user, account, tenant, device,
|
|
42
|
+
request); volume in that unit; the baselines given. Ask at most 2 questions, spent on volume
|
|
43
|
+
and on what the new path writes or sends. A schema change, backfill or dual-write in the same
|
|
44
|
+
change is a migration that finishes its expand phase before step 1; say so in one line.
|
|
45
|
+
|
|
46
|
+
4. **Pick cohorts with reasons.** Every step names who is in it and why:
|
|
47
|
+
- **Internal or test accounts first.** They catch crashes, not rates.
|
|
48
|
+
- **Chosen cohorts beat random ones when the risk sits in a segment.** Put the cases the change exists to fix (the edge cases, the large accounts, the unusual configurations) into an early cohort deliberately, with willing customers, or hold them back deliberately and say why. ❌ a random hash that may contain none of them.
|
|
49
|
+
- **Sticky, nested assignment** for percentage steps: hash on the randomisation unit so the 1% stays inside the 5%. ✅ *"`hash(salt + user_id) mod 1000 < 10`"* — ❌ *"random 1% of requests"* when the flag changes what a user sees.
|
|
50
|
+
- **SLA or contractual-notice customers** stay out before 50% unless they asked to be early; say how they are excluded.
|
|
51
|
+
- **Units in another experiment on the same surface** are excluded, or the comparison is called confounded.
|
|
52
|
+
- **B2B: assign by account or tenant**, so one team never sees two versions.
|
|
53
|
+
|
|
54
|
+
5. **Choose the metrics and write thresholds that can fire.** One decision metric (what the change
|
|
55
|
+
exists to move, or the main correctness or safety rate) plus guardrails: error rate of the touched
|
|
56
|
+
path, p95 of the touched endpoint, saturation of what it leans on (pool, queue, third-party quota),
|
|
57
|
+
one business guardrail. Each carries the baseline from the input and a limit **against the
|
|
58
|
+
concurrent flag-off control**, never against last week.
|
|
59
|
+
- ✅ *"checkout errors, flag-on ≤ flag-off + 0.3 pp (baseline 0.8%)"* — ❌ *"monitor errors"*.
|
|
60
|
+
- **Small counts get count rules.** ✅ *"any unexplained difference over $0.01 between old and new totals holds that account's statement from the send"* — ❌ *"mismatch rate ≤ 0.1%"* on 30 statements.
|
|
61
|
+
- p95 needs 2,000+ requests per arm and p99 10,000+; below that, read the median.
|
|
62
|
+
- A metric with no instrument yet is a precondition for step 1, written once.
|
|
63
|
+
|
|
64
|
+
6. **Size steps from the arithmetic, and say what it means.** For a rate metric on real traffic, run
|
|
65
|
+
`python3 ${CLAUDE_SKILL_DIR}/scripts/canary_math.py --baseline 1% --detect-abs 0.3pp --traffic 2000 --unit users --steps 1,10,50`
|
|
66
|
+
(other IDEs: `python3 scripts/canary_math.py ...` from the skill folder; `--direction down` when a
|
|
67
|
+
fall is the harm, `--control-pct` for a fixed holdback; formula in [`reference.md`](reference.md)).
|
|
68
|
+
- **Put one sentence in the reply, not the table.** ✅ *"At 2,000 users an hour, a 1% step needs about 20 days to see a 0.3 pp rise in errors, so it is a 2-hour crash check and errors are judged at 10% (about 2 days)."* Show the inputs (traffic, baseline, regression size) beside it.
|
|
69
|
+
- **No figure without a source.** Every number in the reply is from the input, from arithmetic on the input, or tagged `[assumption]` in one line. With no baseline or traffic, write the step as a unit count and one line saying what was assumed; never a duration built on an invented rate.
|
|
70
|
+
- **Step minimum = the largest of** the statistical time, one daily peak (24 h when traffic varies by hour), a weekday/weekend cycle where the business has one, and the delay of any late effect (nightly send, billing run).
|
|
71
|
+
- **A step that can't detect anything** is merged, raised, or kept as a crash-only check, said plainly. ✅ *"1% for 2 h: crash check only"* — ❌ *"1% for 24 h to be safe"*.
|
|
72
|
+
- **100% has no concurrent control**: promote on the 50% comparison, or keep a small holdback for a stated time.
|
|
73
|
+
|
|
74
|
+
7. **Put dates on it when the input has a date.** With a deadline, freeze, cycle or "this week",
|
|
75
|
+
each step gets a calendar date counted from today, and the last line of the ladder shows the
|
|
76
|
+
deadline is met or by how much it is missed. Name the slack: ✅ *"one failed cycle pushes 100% past year-end close."*
|
|
77
|
+
|
|
78
|
+
8. **Write each step's rule before it starts.**
|
|
79
|
+
- **Promote** when the minimum time or count is reached and every guardrail is inside its threshold.
|
|
80
|
+
- **Hold** when the sample is short or a metric sits between threshold and abort: extend once, by a stated time or cycle, then roll back.
|
|
81
|
+
- **Roll back** when a guardrail breaches at the planned sample, or an abort fires.
|
|
82
|
+
- **Abort, automated where the tooling allows**: wide, fast triggers. ✅ *"flag-on 5xx > 3× flag-off for 5 min → flag off automatically, page payments on-call"*; ✅ *"unexplained diff on a live statement → account back to the old engine, its unsent statements held"*.
|
|
83
|
+
- **Watcher and override**: the role watching, the sign-off before each promotion where one is needed (finance, tax, a partner contact), and the one role allowed to promote past a breach.
|
|
84
|
+
- **No promotion on peeks.** Promotion is read at the step's end; aborts fire any time.
|
|
85
|
+
|
|
86
|
+
9. **Name what flag-off does not undo.** Who flips the flag and how fast every client stops taking
|
|
87
|
+
the new path (server: seconds; cached client config: its TTL; shipped binary: only a server-side
|
|
88
|
+
switch). Then only the categories this change touches, each with its handling:
|
|
89
|
+
- **Data in a new format or table**: what still reads it after rollback (the renderer, re-runs, credit notes, exports). Verified before step 1; the table can't be dropped while records point at it.
|
|
90
|
+
- **Messages sent** (email, push, webhooks, accounting syncs): no recall; the correction or reissue path exists before the first live step.
|
|
91
|
+
- **Money and third-party records** (charges, agreements, filings, per-call fees): the reversal procedure and who runs it, or "permanent".
|
|
92
|
+
- **Jobs queued, caches written, clients shipped**: when they stop.
|
|
93
|
+
Name the **point of no return**, or rule it out in a line.
|
|
94
|
+
|
|
95
|
+
10. **Schedule the flag's removal.** An owner (the named owner if the input gives one, else a role), a
|
|
96
|
+
date (default 30 days after 100%; for a cycle-driven change, after the first full cycle at 100%
|
|
97
|
+
with no rollback), and what goes: the flag, the dead path, its config and dashboards. Say what
|
|
98
|
+
must stay (a reader for records the old path wrote; the minimum app version for shipped code).
|
|
99
|
+
|
|
100
|
+
11. **Deliver lean.** One plan, shaped by [`templates/rollout-plan.md`](templates/rollout-plan.md):
|
|
101
|
+
the answer, the binding constraint, the dated ladder, the rules, the not-undone list, flag removal.
|
|
102
|
+
Either the plan in the reply or a saved file with a five-line reply, never both at full length.
|
|
103
|
+
"To confirm" holds at most 4 facts, each one that would change a step. Sections that don't apply
|
|
104
|
+
are dropped, not filled with "does not apply".
|
|
105
|
+
|
|
106
|
+
**Non-interactive runs** (subagent, CI, headless): the regression size, take rates, cohort choices and
|
|
107
|
+
step lengths become `[assumption]`s in one line. Missing traffic or baselines are never invented. If
|
|
108
|
+
the change or its gate can't be identified, emit `BLOCKED: need the change being released and the flag
|
|
109
|
+
or canary that gates it`.
|
|
110
|
+
|
|
111
|
+
## Useful references in this skill
|
|
112
|
+
|
|
113
|
+
- [`templates/rollout-plan.md`](templates/rollout-plan.md) — the plan skeleton: answer, constraint, dated ladder, rules, not-undone list, flag removal
|
|
114
|
+
- [`reference.md`](reference.md) — the sample-size formula, the randomisation-unit trap, count rules for small volumes, threshold patterns, the not-undone checklist
|
|
115
|
+
- [`scripts/canary_math.py`](scripts/canary_math.py) — units and hours each percentage step needs; flags steps that can't reach sufficiency (stdlib only)
|
|
116
|
+
- [`examples/sync-engine-workspaces.md`](examples/sync-engine-workspaces.md) — a plan for a sync engine ramped by workspace, where flag-off leaves notes the old engine can't read
|
|
117
|
+
|
|
118
|
+
## Quality bar
|
|
119
|
+
|
|
120
|
+
- **The first two lines answer the user's question** in plain words, with the input fact that settles it.
|
|
121
|
+
- **The binding constraint is named** and the ladder visibly follows it (cap, cycle, deadline, segment, shadow, volume).
|
|
122
|
+
- **Every threshold is testable**: metric, baseline, limit against the flag-off control; counts where volumes are small.
|
|
123
|
+
- **Every number traces** to the input, to arithmetic on it, or to a one-line `[assumption]`.
|
|
124
|
+
- **A deadline in the input gets a dated ladder** that shows whether it is met.
|
|
125
|
+
- **Promote, hold, roll back and abort are written before the step**, with a watcher, a sign-off where one is needed, and an override role.
|
|
126
|
+
- **The not-undone list covers what this change sends, writes or charges**, with handling for each.
|
|
127
|
+
- **The flag has an owner and a removal date.**
|
|
128
|
+
- **The reply is shorter than the plan it replaces would be at full length**; "To confirm" is never longer than the ladder.
|
|
129
|
+
|
|
130
|
+
## When to use this skill
|
|
131
|
+
|
|
132
|
+
- ✅ A merged change behind a flag needs a ramp, canary schedule or cohort plan
|
|
133
|
+
- ✅ "How long does the 1% have to run?", "when do we promote?", "is this ramp right?"
|
|
134
|
+
- ✅ Turning on a risky path (payments, tax, auth, ranking, a new dependency) for real users
|
|
135
|
+
- ✅ A rollout that sends, charges or writes something the flag can't take back
|
|
136
|
+
|
|
137
|
+
## When NOT to use this skill
|
|
138
|
+
|
|
139
|
+
- ❌ Schema changes, backfills, datastore or provider swaps, dual-writes: `migration-plan`
|
|
140
|
+
- ❌ "Are we ready to launch?" go/no-go: `launch-readiness`
|
|
141
|
+
- ❌ SLOs, alerts and dashboards for the service: `observability-plan`
|
|
142
|
+
- ❌ Defining what success means for the feature: `success-metrics`
|
|
143
|
+
- ❌ The on-call procedure for the service as a whole: `runbook-generator`
|
|
144
|
+
- ❌ A change with no flag, canary or staged deploy: there is nothing to ramp; plan the gate first
|
|
145
|
+
|
|
146
|
+
## Anti-patterns to avoid
|
|
147
|
+
|
|
148
|
+
- ❌ **Statistics as the headline.** "11,734 per arm, 26 days" before the user knows whether their plan is right.
|
|
149
|
+
- ❌ **The ritual ladder.** 1% → 5% → 25% → 50% → 100% on a change whose evidence arrives once a month, or under a partner cap it would cross.
|
|
150
|
+
- ❌ **Random buckets where the risk is a segment.** A pricing change hashed by account may never touch the multi-currency accounts it exists to fix.
|
|
151
|
+
- ❌ **Percent thresholds on single-digit counts.** "Failure rate ≤ 0.5%" on 30 statements fires on one failure or never.
|
|
152
|
+
- ❌ **Invented inputs.** An adoption rate, mismatch rate or per-account volume the input never gave, presented as fact.
|
|
153
|
+
- ❌ **No dates under a deadline.** "Step 3: 48 h" when the user named a freeze date.
|
|
154
|
+
- ❌ **Comparing against yesterday** instead of the concurrent flag-off cohort.
|
|
155
|
+
- ❌ **"Rollback: turn the flag off"** when the path sent emails, charged cards or wrote rows old code can't read.
|
|
156
|
+
- ❌ **The padded reply.** A full plan in the message and again in a file, a "To confirm" table longer than the plan.
|
|
157
|
+
- ❌ **The flag that never leaves.** No owner and no date.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
[
|
|
2
|
+
{ "query": "Plan a canary rollout for the new pricing engine — it's behind a feature flag", "should_trigger": true },
|
|
3
|
+
{ "query": "What percentages should we ramp the checkout_v2 flag through, and when do we promote each step?", "should_trigger": true },
|
|
4
|
+
{ "query": "How long does our 1% canary need to run before the error numbers mean anything?", "should_trigger": true },
|
|
5
|
+
{ "query": "Write a progressive delivery plan with rollback criteria for the new recommendations widget", "should_trigger": true },
|
|
6
|
+
{ "query": "We're turning on the new login flow for users next week — how do we do it gradually and safely?", "should_trigger": true },
|
|
7
|
+
{ "query": "Plan the backfill and expand-contract migration for splitting the users table", "should_trigger": false },
|
|
8
|
+
{ "query": "Are we ready to launch the new checkout on Monday? Give me a go/no-go", "should_trigger": false },
|
|
9
|
+
{ "query": "Write the release notes for version 4.2", "should_trigger": false },
|
|
10
|
+
{ "query": "Design SLOs and burn-rate alerts for the payments API", "should_trigger": false },
|
|
11
|
+
{ "query": "How will we know if the new onboarding flow worked? Define the success metrics", "should_trigger": false }
|
|
12
|
+
]
|