skilldrop-cli 0.16.18 → 0.16.20
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/agents/devils-advocate.md +34 -22
- package/bin/skilldrop.js +18 -4
- package/catalogue.json +2 -1
- package/model-routing.json +219 -215
- package/package.json +1 -1
- package/packs/ai-engineering/skills/agent-adoption-stage/evals/evals.json +28 -0
- package/packs/ai-engineering/skills/agent-adoption-stage/evals/files/2/agent-telemetry.csv +7 -0
- package/packs/ai-engineering/skills/agent-adoption-stage/evals/files/2/cto-offsite-memo.md +9 -0
- package/packs/ai-engineering/skills/agent-adoption-stage/evals/files/2/dev-survey-summary.md +9 -0
- package/packs/ai-engineering/skills/agent-adoption-stage/evals/files/2/engineering-policy.md +15 -0
- package/packs/ai-engineering/skills/agent-budget/evals/evals.json +27 -0
- package/packs/ai-engineering/skills/agent-budget/evals/files/3/finance-note.md +10 -0
- package/packs/ai-engineering/skills/agent-budget/evals/files/3/pipeline.md +18 -0
- package/packs/ai-engineering/skills/agent-budget/evals/files/3/price-sheet.md +9 -0
- package/packs/ai-engineering/skills/agent-budget/evals/files/3/september-usage.csv +7 -0
- package/packs/ai-engineering/skills/agent-loop-design/evals/evals.json +28 -0
- package/packs/ai-engineering/skills/agent-loop-design/evals/files/3/design-guide-extract.md +13 -0
- package/packs/ai-engineering/skills/agent-loop-design/evals/files/3/legal-advice.md +9 -0
- package/packs/ai-engineering/skills/agent-loop-design/evals/files/3/request-from-head-of-planning.md +11 -0
- package/packs/ai-engineering/skills/agent-loop-design/evals/files/3/trial-notes.md +13 -0
- package/packs/ai-engineering/skills/agent-threat-model/evals/evals.json +27 -0
- package/packs/ai-engineering/skills/agent-threat-model/evals/files/4/agent-config.yaml +19 -0
- package/packs/ai-engineering/skills/agent-threat-model/evals/files/4/agent-spec.md +17 -0
- package/packs/ai-engineering/skills/agent-threat-model/evals/files/4/previous-review.md +10 -0
- package/packs/ai-engineering/skills/agent-threat-model/evals/files/4/vendor-security-note.md +4 -0
- package/packs/ai-engineering/skills/ai-adoption-rollout/evals/evals.json +28 -0
- package/packs/ai-engineering/skills/ai-adoption-rollout/evals/files/2/cno-brief.md +17 -0
- package/packs/ai-engineering/skills/ai-adoption-rollout/evals/files/2/education-capacity.md +5 -0
- package/packs/ai-engineering/skills/ai-adoption-rollout/evals/files/2/pilot-report.md +14 -0
- package/packs/ai-engineering/skills/ai-adoption-rollout/evals/files/2/survey-summary.md +7 -0
- package/packs/ai-engineering/skills/ai-adoption-rollout/evals/files/2/union-letter.md +8 -0
- package/packs/ai-engineering/skills/ai-readiness-assessment/evals/evals.json +28 -0
- package/packs/ai-engineering/skills/ai-readiness-assessment/evals/files/2/ceo-note.md +5 -0
- package/packs/ai-engineering/skills/ai-readiness-assessment/evals/files/2/governance-status.md +6 -0
- package/packs/ai-engineering/skills/ai-readiness-assessment/evals/files/2/interview-notes.md +18 -0
- package/packs/ai-engineering/skills/ai-readiness-assessment/evals/files/2/it-inventory.md +12 -0
- package/packs/ai-engineering/skills/ai-usage-report/evals/evals.json +27 -0
- package/packs/ai-engineering/skills/ai-usage-report/evals/files/2/draftwell-events.csv +249 -0
- package/packs/ai-engineering/skills/ai-usage-report/evals/files/2/export-note.md +9 -0
- package/packs/ai-engineering/skills/ai-usage-report/evals/files/2/published-assets.csv +44 -0
- package/packs/ai-engineering/skills/ai-usage-report/evals/files/2/team-roster.md +10 -0
- package/packs/ai-engineering/skills/ai-use-case-triage/evals/evals.json +27 -0
- package/packs/ai-engineering/skills/ai-use-case-triage/evals/files/2/data-team.md +5 -0
- package/packs/ai-engineering/skills/ai-use-case-triage/evals/files/2/finance-notes.md +7 -0
- package/packs/ai-engineering/skills/ai-use-case-triage/evals/files/2/ideas.csv +10 -0
- package/packs/ai-engineering/skills/ai-use-case-triage/evals/files/2/legal-note.md +7 -0
- package/packs/ai-engineering/skills/llm-eval-harness/evals/evals.json +28 -0
- package/packs/ai-engineering/skills/llm-eval-harness/evals/files/2/current-tests.md +6 -0
- package/packs/ai-engineering/skills/llm-eval-harness/evals/files/2/feature-brief.md +14 -0
- package/packs/ai-engineering/skills/llm-eval-harness/evals/files/2/incident-log.md +8 -0
- package/packs/ai-engineering/skills/llm-eval-harness/evals/files/2/pm-request.md +10 -0
- package/packs/ai-engineering/skills/subagent-design/evals/evals.json +27 -0
- package/packs/ai-engineering/skills/subagent-design/evals/files/3/current-process.md +11 -0
- package/packs/ai-engineering/skills/subagent-design/evals/files/3/director-proposal.md +6 -0
- package/packs/ai-engineering/skills/subagent-design/evals/files/3/scoring-rubric.md +9 -0
- package/packs/ai-engineering/skills/subagent-design/evals/files/3/trial-and-round-notes.md +11 -0
- package/packs/api-builder/skills/eval-harness-generator/evals/evals.json +27 -0
- package/packs/api-builder/skills/eval-harness-generator/evals/files/2/SKILL.md +35 -0
- package/packs/api-builder/skills/prompt-caching-advisor/evals/evals.json +25 -0
- package/packs/api-builder/skills/prompt-caching-advisor/evals/files/2/build_request.py +40 -0
- package/packs/api-builder/skills/prompt-caching-advisor/evals/files/2/notes.md +10 -0
- package/packs/api-builder/skills/token-budget-estimator/evals/evals.json +27 -0
- package/packs/api-builder/skills/token-budget-estimator/evals/files/2/pilot-usage.csv +17 -0
- package/packs/api-builder/skills/token-budget-estimator/evals/files/2/workflow.md +42 -0
- package/packs/career/pack.json +1 -1
- package/packs/career/skills/job-fit-analysis/SKILL.md +2 -1
- package/packs/career/skills/job-fit-analysis/manifest.json +3 -2
- package/packs/career/skills/role-shortlist/SKILL.md +107 -0
- package/packs/career/skills/role-shortlist/evals/eval_queries.json +34 -0
- package/packs/career/skills/role-shortlist/evals/evals.json +75 -0
- package/packs/career/skills/role-shortlist/evals/files/1/careers-index.html +22 -0
- package/packs/career/skills/role-shortlist/evals/files/1/cv.md +44 -0
- package/packs/career/skills/role-shortlist/evals/files/1/postings/clinical-coding-officer-bramwick.md +23 -0
- package/packs/career/skills/role-shortlist/evals/files/1/postings/data-and-performance-analyst.html +28 -0
- package/packs/career/skills/role-shortlist/evals/files/1/postings/estates-maintenance-electrician-bramwick.md +20 -0
- package/packs/career/skills/role-shortlist/evals/files/1/postings/healthcare-support-worker-nights-corrin-bay.md +20 -0
- package/packs/career/skills/role-shortlist/evals/files/1/postings/medical-secretary-cardiology-elsford.html +30 -0
- package/packs/career/skills/role-shortlist/evals/files/1/postings/outpatient-booking-team-leader-bramwick.md +23 -0
- package/packs/career/skills/role-shortlist/evals/files/1/postings/patient-pathway-coordinator-bramwick.md +30 -0
- package/packs/career/skills/role-shortlist/evals/files/1/postings/patient-pathway-coordinator-elsford.md +30 -0
- package/packs/career/skills/role-shortlist/evals/files/1/postings/reception-and-admin-assistant-corrin-bay.md +21 -0
- package/packs/career/skills/role-shortlist/evals/files/1/postings/senior-care-coordinator.md +25 -0
- package/packs/career/skills/role-shortlist/evals/files/2/cv.md +33 -0
- package/packs/career/skills/role-shortlist/evals/files/2/job-board-export.csv +11 -0
- package/packs/career/skills/role-shortlist/evals/files/2/postings/FJ-10422.md +26 -0
- package/packs/career/skills/role-shortlist/evals/files/2/postings/FJ-10431.md +25 -0
- package/packs/career/skills/role-shortlist/evals/files/2/postings/FJ-10455.md +27 -0
- package/packs/career/skills/role-shortlist/evals/files/2/postings/FJ-10468.md +25 -0
- package/packs/career/skills/role-shortlist/evals/files/2/postings/FJ-10476.md +25 -0
- package/packs/career/skills/role-shortlist/evals/files/2/postings/FJ-10490.md +24 -0
- package/packs/career/skills/role-shortlist/evals/files/2/postings/FJ-10587.md +26 -0
- package/packs/career/skills/role-shortlist/evals/files/4/brackwater-careers.html +118 -0
- package/packs/career/skills/role-shortlist/evals/files/4/mere-castell-cv.md +44 -0
- package/packs/career/skills/role-shortlist/evals/files/4/tradesboard-export.csv +6 -0
- package/packs/career/skills/role-shortlist/manifest.json +38 -0
- package/packs/converters/skills/file-to-markdown/evals/evals.json +27 -0
- package/packs/converters/skills/file-to-markdown/evals/files/3/harbourline-shift-policy.docx +0 -0
- package/packs/converters/skills/file-to-markdown/evals/files/4/seed-swap-2026.csv +10 -0
- package/packs/converters/skills/md-to-docx/evals/evals.json +28 -0
- package/packs/converters/skills/md-to-docx/evals/files/3/brand.json +12 -0
- package/packs/converters/skills/md-to-docx/evals/files/3/img/team-photo.png +0 -0
- package/packs/converters/skills/md-to-docx/evals/files/3/tender-response.md +51 -0
- package/packs/converters/skills/md-to-html/evals/evals.json +26 -0
- package/packs/converters/skills/md-to-html/evals/files/2/brand.json +13 -0
- package/packs/converters/skills/md-to-html/evals/files/2/img/kick-net.png +0 -0
- package/packs/converters/skills/md-to-html/evals/files/2/river-survey-guide.md +53 -0
- package/packs/converters/skills/md-to-html/evals/files/3/nanas-lentil-soup.md +21 -0
- package/packs/converters/skills/md-to-html/evals/files/3/pot.png +0 -0
- package/packs/converters/skills/md-to-xlsx/evals/evals.json +26 -0
- package/packs/converters/skills/md-to-xlsx/evals/files/3/depot-costs-q3.md +20 -0
- package/packs/converters/skills/md-to-xlsx/evals/files/4/hollowmere-5k-results.json +7 -0
- package/packs/converters/skills/mermaid-render/evals/evals.json +26 -0
- package/packs/converters/skills/mermaid-render/evals/files/2/dispensing-diagrams.md +39 -0
- package/packs/core/skills/brief-intake/evals/evals.json +27 -0
- package/packs/core/skills/brief-intake/evals/files/2/ops-meeting-notes.md +18 -0
- package/packs/core/skills/brief-intake/evals/files/2/thread.txt +40 -0
- package/packs/core/skills/council-review/evals/evals.json +28 -0
- package/packs/core/skills/council-review/evals/files/2/compliance-note.md +11 -0
- package/packs/core/skills/council-review/evals/files/2/link-outages-q3.csv +11 -0
- package/packs/core/skills/council-review/evals/files/2/proposal.md +39 -0
- package/packs/core/skills/council-review/evals/files/2/vendor-letter.txt +12 -0
- package/packs/core/skills/doc-critique/evals/evals.json +27 -0
- package/packs/core/skills/doc-critique/evals/files/2/exec-summary.md +43 -0
- package/packs/core/skills/output-hygiene/evals/evals.json +27 -0
- package/packs/core/skills/output-hygiene/evals/files/3/newsletter.md +17 -0
- package/packs/data-analytics/skills/dashboard-spec/evals/evals.json +27 -0
- package/packs/data-analytics/skills/dashboard-spec/evals/files/2/data-inventory.md +16 -0
- package/packs/data-analytics/skills/dashboard-spec/evals/files/2/request-email.md +28 -0
- package/packs/data-analytics/skills/sql-review/evals/evals.json +27 -0
- package/packs/data-analytics/skills/sql-review/evals/files/2/context.md +10 -0
- package/packs/data-analytics/skills/sql-review/evals/files/2/monthly_donor_revenue.sql +16 -0
- package/packs/data-analytics/skills/sql-review/evals/files/2/schema.md +20 -0
- package/packs/design/skills/brand-kit/evals/evals.json +26 -0
- package/packs/design/skills/brand-kit/evals/files/2/brand-guidelines.md +32 -0
- package/packs/design/skills/brand-kit/evals/files/2/contact-sheet.txt +6 -0
- package/packs/design/skills/brand-kit/evals/files/2/logo.svg +5 -0
- package/packs/design/skills/brand-kit/evals/files/2/website-colours.css +9 -0
- package/packs/design/skills/brand-kit/evals/files/3/muddy-paws-logo.png +0 -0
- package/packs/design/skills/deck-builder/evals/evals.json +27 -0
- package/packs/design/skills/deck-builder/evals/files/4/ashby-vale-board-notes.md +36 -0
- package/packs/design/skills/marketing-flyer/evals/evals.json +27 -0
- package/packs/design/skills/marketing-flyer/evals/files/2/brand.json +14 -0
- package/packs/design/skills/marketing-flyer/evals/files/2/larchfield-logo.svg +4 -0
- package/packs/design/skills/marketing-flyer/evals/files/2/manager-email.txt +6 -0
- package/packs/design/skills/marketing-flyer/evals/files/2/open-day-brief.md +15 -0
- package/packs/design/skills/slide-outliner/evals/evals.json +26 -0
- package/packs/design/skills/slide-outliner/evals/files/2/uv-dosing-design-notes.md +38 -0
- package/packs/dev-team/skills/accessibility-audit/evals/evals.json +27 -0
- package/packs/dev-team/skills/accessibility-audit/evals/files/2/booking.html +79 -0
- package/packs/dev-team/skills/accessibility-audit/evals/files/2/scan-report.txt +23 -0
- package/packs/dev-team/skills/accessibility-audit/evals/files/2/styles.css +56 -0
- package/packs/dev-team/skills/agents-md-generator/evals/evals.json +27 -0
- package/packs/dev-team/skills/agents-md-generator/evals/files/5/.github/workflows/ci.yml +12 -0
- package/packs/dev-team/skills/agents-md-generator/evals/files/5/CONTRIBUTING.md +20 -0
- package/packs/dev-team/skills/agents-md-generator/evals/files/5/README.md +15 -0
- package/packs/dev-team/skills/agents-md-generator/evals/files/5/data/raw/README.md +7 -0
- package/packs/dev-team/skills/agents-md-generator/evals/files/5/pyproject.toml +23 -0
- package/packs/dev-team/skills/agents-md-generator/evals/files/5/scripts/deploy.sh +6 -0
- package/packs/dev-team/skills/agents-md-generator/evals/files/5/src/sensor_ingest/__init__.py +1 -0
- package/packs/dev-team/skills/agents-md-generator/evals/files/5/src/sensor_ingest/parse.py +20 -0
- package/packs/dev-team/skills/agents-md-generator/evals/files/5/tests/test_gateway.py +10 -0
- package/packs/dev-team/skills/agents-md-generator/evals/files/5/tests/test_parse.py +7 -0
- package/packs/dev-team/skills/agents-md-generator/evals/files/6/clean_marks.R +5 -0
- package/packs/dev-team/skills/agents-md-generator/evals/files/6/grades_final.csv +6 -0
- package/packs/dev-team/skills/agents-md-generator/evals/files/6/make_report.R +7 -0
- package/packs/dev-team/skills/agents-md-generator/evals/files/6/notes.txt +8 -0
- package/packs/dev-team/skills/agents-md-generator/evals/files/6/raw_marks.csv +6 -0
- package/packs/dev-team/skills/bug-triage/evals/evals.json +27 -0
- package/packs/dev-team/skills/bug-triage/evals/files/2/payments-log.csv +15 -0
- package/packs/dev-team/skills/bug-triage/evals/files/2/slack-thread.txt +16 -0
- package/packs/dev-team/skills/bug-triage/evals/files/2/ticket.txt +10 -0
- package/packs/dev-team/skills/devils-advocate/SKILL.md +65 -124
- package/packs/dev-team/skills/devils-advocate/evals/evals.json +27 -0
- package/packs/dev-team/skills/devils-advocate/evals/files/6/PR_DESCRIPTION.md +20 -0
- package/packs/dev-team/skills/devils-advocate/evals/files/6/redeem.py +102 -0
- package/packs/dev-team/skills/devils-advocate/evals/files/6/test_redeem.py +54 -0
- package/packs/dev-team/skills/devils-advocate/examples/rate-limiter-review.md +36 -16
- package/packs/dev-team/skills/devils-advocate/manifest.json +3 -3
- package/packs/dev-team/skills/feature-implement-loop/evals/evals.json +27 -0
- package/packs/dev-team/skills/feature-implement-loop/evals/files/2/TICKET.md +20 -0
- package/packs/dev-team/skills/feature-implement-loop/evals/files/2/closures.csv +5 -0
- package/packs/dev-team/skills/feature-implement-loop/evals/files/2/desk.py +11 -0
- package/packs/dev-team/skills/feature-implement-loop/evals/files/2/fees.py +14 -0
- package/packs/dev-team/skills/feature-implement-loop/evals/files/2/tests/test_fees.py +26 -0
- package/packs/dev-team/skills/feature-implement-loop/evals/files/3/away.txt +5 -0
- package/packs/dev-team/skills/feature-implement-loop/evals/files/3/members.txt +8 -0
- package/packs/dev-team/skills/feature-implement-loop/evals/files/3/rota.py +12 -0
- package/packs/dev-team/skills/launch-readiness/evals/evals.json +27 -0
- package/packs/dev-team/skills/launch-readiness/evals/files/2/launch-brief.md +32 -0
- package/packs/dev-team/skills/launch-readiness/evals/files/2/load-test-summary.txt +11 -0
- package/packs/dev-team/skills/launch-readiness/evals/files/2/privacy-signoff.txt +9 -0
- package/packs/dev-team/skills/launch-readiness/evals/files/2/sms-template.txt +2 -0
- package/packs/dev-team/skills/migration-plan/evals/evals.json +27 -0
- package/packs/dev-team/skills/migration-plan/evals/files/2/context.md +27 -0
- package/packs/dev-team/skills/migration-plan/evals/files/2/deferral-codes.csv +8 -0
- package/packs/dev-team/skills/migration-plan/evals/files/2/field-mapping.csv +11 -0
- package/packs/dev-team/skills/migration-plan/evals/files/2/trial-import-summary.txt +9 -0
- package/packs/dev-team/skills/pr-description-writer/evals/evals.json +27 -0
- package/packs/dev-team/skills/pr-description-writer/evals/files/4/changes.diff +64 -0
- package/packs/dev-team/skills/pr-description-writer/evals/files/4/commits.txt +2 -0
- package/packs/dev-team/skills/pre-merge-review/evals/evals.json +28 -0
- package/packs/dev-team/skills/pre-merge-review/evals/files/3/CHANGE.md +12 -0
- package/packs/dev-team/skills/pre-merge-review/evals/files/3/CONTRIBUTING.md +5 -0
- package/packs/dev-team/skills/pre-merge-review/evals/files/3/change.diff +53 -0
- package/packs/dev-team/skills/pre-merge-review/evals/files/3/clinic_dosing/__init__.py +1 -0
- package/packs/dev-team/skills/pre-merge-review/evals/files/3/clinic_dosing/dosing.py +30 -0
- package/packs/dev-team/skills/pre-merge-review/evals/files/3/clinic_dosing/drugs.py +6 -0
- package/packs/dev-team/skills/pre-merge-review/evals/files/3/tests/__init__.py +0 -0
- package/packs/dev-team/skills/pre-merge-review/evals/files/3/tests/test_dosing.py +29 -0
- package/packs/dev-team/skills/sonar-onboard/evals/evals.json +27 -0
- package/packs/dev-team/skills/sonar-onboard/evals/files/2/.github/workflows/ci.yml +10 -0
- package/packs/dev-team/skills/sonar-onboard/evals/files/2/.gitlab-ci.yml +32 -0
- package/packs/dev-team/skills/sonar-onboard/evals/files/2/README.md +11 -0
- package/packs/dev-team/skills/sonar-onboard/evals/files/2/pyproject.toml +16 -0
- package/packs/dev-team/skills/sonar-onboard/evals/files/2/src/wayline/__init__.py +1 -0
- package/packs/dev-team/skills/sonar-onboard/evals/files/2/src/wayline/_generated/__init__.py +0 -0
- package/packs/dev-team/skills/sonar-onboard/evals/files/2/src/wayline/_generated/stops_pb2.py +4 -0
- package/packs/dev-team/skills/sonar-onboard/evals/files/2/src/wayline/timetable.py +12 -0
- package/packs/dev-team/skills/sonar-onboard/evals/files/2/src/wayline/vendor/__init__.py +0 -0
- package/packs/dev-team/skills/sonar-onboard/evals/files/2/src/wayline/vendor/gtfs_lite/__init__.py +3 -0
- package/packs/dev-team/skills/sonar-onboard/evals/files/2/tests/test_timetable.py +14 -0
- package/packs/dev-team/skills/sonar-onboard/evals/files/2/web/package.json +20 -0
- package/packs/dev-team/skills/sonar-onboard/evals/files/2/web/src/board.test.ts +9 -0
- package/packs/dev-team/skills/sonar-onboard/evals/files/2/web/src/board.ts +8 -0
- package/packs/dev-team/skills/sonar-review/evals/evals.json +27 -0
- package/packs/dev-team/skills/sonar-review/evals/files/2/changed-files.txt +5 -0
- package/packs/dev-team/skills/sonar-review/evals/files/2/hotspots.json +7 -0
- package/packs/dev-team/skills/sonar-review/evals/files/2/issues.json +26 -0
- package/packs/dev-team/skills/sonar-review/evals/files/2/quality-gate.json +19 -0
- package/packs/dev-team/skills/tech-debt-register/evals/evals.json +28 -0
- package/packs/dev-team/skills/tech-debt-register/evals/files/4/incidents.csv +7 -0
- package/packs/dev-team/skills/tech-debt-register/evals/files/4/retro-notes.md +21 -0
- package/packs/dev-team/skills/test-plan-generator/evals/evals.json +27 -0
- package/packs/dev-team/skills/test-plan-generator/evals/files/2/permit-renewal-spec.md +40 -0
- package/packs/dev-team/skills/user-story-splitter/evals/evals.json +27 -0
- package/packs/dev-team/skills/user-story-splitter/evals/files/2/prd-shift-swap.md +38 -0
- package/packs/experience-design/skills/content-design/evals/evals.json +27 -0
- package/packs/experience-design/skills/content-design/evals/files/2/contact-centre-notes.md +18 -0
- package/packs/experience-design/skills/content-design/evals/files/2/current-page.md +17 -0
- package/packs/experience-design/skills/content-design/evals/files/2/policy-extract.md +17 -0
- package/packs/experience-design/skills/design-system-spec/evals/evals.json +27 -0
- package/packs/experience-design/skills/design-system-spec/evals/files/2/designer-notes.md +23 -0
- package/packs/experience-design/skills/design-system-spec/evals/files/2/eng-thread.md +11 -0
- package/packs/experience-design/skills/design-system-spec/evals/files/2/tokens.json +27 -0
- package/packs/experience-design/skills/information-architecture/evals/evals.json +27 -0
- package/packs/experience-design/skills/information-architecture/evals/files/2/page-inventory.csv +28 -0
- package/packs/experience-design/skills/information-architecture/evals/files/2/search-log.csv +16 -0
- package/packs/experience-design/skills/information-architecture/evals/files/2/stakeholder-notes.md +13 -0
- package/packs/experience-design/skills/service-blueprint/evals/evals.json +27 -0
- package/packs/experience-design/skills/service-blueprint/evals/files/2/complaints-summary.md +14 -0
- package/packs/experience-design/skills/service-blueprint/evals/files/2/process-notes.md +15 -0
- package/packs/experience-design/skills/service-blueprint/evals/files/2/timings.csv +5 -0
- package/packs/experience-design/skills/ux-writing/evals/evals.json +27 -0
- package/packs/experience-design/skills/ux-writing/evals/files/2/brand-notes.md +8 -0
- package/packs/experience-design/skills/ux-writing/evals/files/2/error-codes.csv +6 -0
- package/packs/experience-design/skills/ux-writing/evals/files/2/screens.md +26 -0
- package/packs/grc/skills/dpia/evals/evals.json +27 -0
- package/packs/grc/skills/dpia/evals/files/2/legal-notes.md +6 -0
- package/packs/grc/skills/dpia/evals/files/2/project-brief.md +17 -0
- package/packs/grc/skills/dpia/evals/files/2/vendor-factsheet.md +8 -0
- package/packs/grc/skills/risk-register/evals/evals.json +27 -0
- package/packs/grc/skills/risk-register/evals/files/2/committee-notes.md +8 -0
- package/packs/grc/skills/risk-register/evals/files/2/risks-draft.csv +9 -0
- package/packs/grc/skills/risk-register/evals/files/2/scoring-scale.md +12 -0
- package/packs/grc/skills/security-questionnaire-response/evals/evals.json +27 -0
- package/packs/grc/skills/security-questionnaire-response/evals/files/4/evidence/business-continuity-plan.md +7 -0
- package/packs/grc/skills/security-questionnaire-response/evals/files/4/evidence/information-security-policy.md +10 -0
- package/packs/grc/skills/security-questionnaire-response/evals/files/4/evidence/pentest-summary-2026.md +10 -0
- package/packs/grc/skills/security-questionnaire-response/evals/files/4/evidence/staff-handbook-extract.md +3 -0
- package/packs/grc/skills/security-questionnaire-response/evals/files/4/instructions.md +10 -0
- package/packs/grc/skills/security-questionnaire-response/evals/files/4/questionnaire.csv +11 -0
- package/packs/grc/skills/soc2-evidence-map/evals/evals.json +27 -0
- package/packs/grc/skills/soc2-evidence-map/evals/files/2/audit-scope.md +10 -0
- package/packs/grc/skills/soc2-evidence-map/evals/files/2/controls.csv +13 -0
- package/packs/grc/skills/soc2-evidence-map/evals/files/2/tooling-notes.md +8 -0
- package/packs/infra-as-code/skills/terraform-module/evals/evals.json +27 -0
- package/packs/infra-as-code/skills/terraform-module/evals/files/3/requirements.md +23 -0
- package/packs/infra-as-code/skills/terraform-plan-review/evals/evals.json +27 -0
- package/packs/infra-as-code/skills/terraform-plan-review/evals/files/3/plan.txt +87 -0
- package/packs/product-manager/skills/business-case/evals/evals.json +27 -0
- package/packs/product-manager/skills/business-case/evals/files/2/hollin_vale_inputs.md +46 -0
- package/packs/product-manager/skills/okr-cascade/evals/evals.json +27 -0
- package/packs/product-manager/skills/okr-cascade/evals/files/3/quillfield_2027.md +45 -0
- package/packs/product-manager/skills/prd-draft/evals/evals.json +27 -0
- package/packs/product-manager/skills/prd-draft/evals/files/2/brief.md +7 -0
- package/packs/product-manager/skills/prd-draft/evals/files/2/call_center_notes.md +7 -0
- package/packs/product-manager/skills/prd-draft/evals/files/2/meter_facts.md +6 -0
- package/packs/product-manager/skills/prfaq/evals/evals.json +26 -0
- package/packs/product-manager/skills/prfaq/evals/files/3/drone_notes.md +25 -0
- package/packs/product-manager/skills/requirements-interview/evals/evals.json +27 -0
- package/packs/product-manager/skills/requirements-interview/evals/files/2/project_brief.md +29 -0
- package/packs/product-manager/skills/strategy-analysis/evals/evals.json +27 -0
- package/packs/product-manager/skills/strategy-analysis/evals/files/4/market_notes.md +21 -0
- package/packs/product-manager/skills/success-metrics/evals/evals.json +28 -0
- package/packs/product-manager/skills/success-metrics/evals/files/3/brief.md +21 -0
- package/packs/product-manager/skills/user-journey-map/evals/evals.json +26 -0
- package/packs/product-manager/skills/user-journey-map/evals/files/3/research_notes.md +30 -0
- package/packs/research/skills/hypothesis-comparison/evals/evals.json +27 -0
- package/packs/research/skills/hypothesis-comparison/evals/files/2/season_notes.md +29 -0
- package/packs/research/skills/research-plan/evals/evals.json +28 -0
- package/packs/research/skills/research-plan/evals/files/3/request.md +19 -0
- package/packs/skill-engineering/skills/skill-author/evals/evals.json +27 -0
- package/packs/skill-engineering/skills/skill-author/evals/files/2/constraints.md +7 -0
- package/packs/skill-engineering/skills/skill-author/evals/files/2/example-discharge.md +22 -0
- package/packs/skill-engineering/skills/skill-author/evals/files/2/process.md +15 -0
- package/packs/skill-engineering/skills/skill-review/evals/evals.json +27 -0
- package/packs/skill-engineering/skills/skill-review/evals/files/2/invoice-chaser/SKILL.md +20 -0
- package/packs/skill-engineering/skills/skill-review/evals/files/2/invoice-chaser/evals/evals.json +12 -0
- package/packs/skill-engineering/skills/skill-review/evals/files/2/invoice-chaser/scripts/send_reminders.py +46 -0
- package/packs/solution-architect/skills/adr-generator/evals/evals.json +27 -0
- package/packs/solution-architect/skills/adr-generator/evals/files/2/integration-notes.md +23 -0
- package/packs/solution-architect/skills/adr-generator/evals/files/2/larchway-quote.txt +10 -0
- package/packs/solution-architect/skills/api-contract-draft/evals/evals.json +27 -0
- package/packs/solution-architect/skills/api-contract-draft/evals/files/2/booking-brief.md +18 -0
- package/packs/solution-architect/skills/api-contract-draft/evals/files/2/facilities-policy-excerpt.md +7 -0
- package/packs/solution-architect/skills/architecture-diagrams/evals/evals.json +27 -0
- package/packs/solution-architect/skills/architecture-diagrams/evals/files/2/telemetry-overview.md +23 -0
- package/packs/solution-architect/skills/data-contract/evals/evals.json +27 -0
- package/packs/solution-architect/skills/data-contract/evals/files/2/grain_deliveries.sql +15 -0
- package/packs/solution-architect/skills/data-contract/evals/files/2/publisher-notes.md +14 -0
- package/packs/solution-architect/skills/data-contract/evals/files/2/sample_rows.csv +6 -0
- package/packs/solution-architect/skills/figma-diagrams/evals/evals.json +26 -0
- package/packs/solution-architect/skills/figma-diagrams/evals/files/2/signage-file.json +373 -0
- package/packs/solution-architect/skills/nfr-spec/evals/evals.json +27 -0
- package/packs/solution-architect/skills/nfr-spec/evals/files/2/vehicle-booking-brief.md +21 -0
- package/packs/solution-architect/skills/reverse-architecture/evals/evals.json +28 -0
- package/packs/solution-architect/skills/reverse-architecture/evals/files/2/README.md +11 -0
- package/packs/solution-architect/skills/reverse-architecture/evals/files/2/docker-compose.yml +42 -0
- package/packs/solution-architect/skills/reverse-architecture/evals/files/2/env.example +3 -0
- package/packs/solution-architect/skills/reverse-architecture/evals/files/2/nginx.conf +15 -0
- package/packs/solution-architect/skills/reverse-architecture/evals/files/2/worker-requirements.txt +4 -0
- package/packs/solution-architect/skills/threat-model/evals/evals.json +28 -0
- package/packs/solution-architect/skills/threat-model/evals/files/2/permits-design.md +23 -0
- package/packs/sre-oncall/skills/capacity-cost-model/evals/evals.json +28 -0
- package/packs/sre-oncall/skills/capacity-cost-model/evals/files/2/brief.md +22 -0
- package/packs/sre-oncall/skills/capacity-cost-model/evals/files/2/price-sheet.md +13 -0
- package/packs/sre-oncall/skills/capacity-cost-model/evals/files/2/rollout-plan.md +27 -0
- package/packs/sre-oncall/skills/cloud-cost-review/evals/evals.json +28 -0
- package/packs/sre-oncall/skills/cloud-cost-review/evals/files/3/cost-export-2026-09.csv +25 -0
- package/packs/sre-oncall/skills/cloud-cost-review/evals/files/3/notes.md +11 -0
- package/packs/sre-oncall/skills/cloud-cost-review/evals/files/3/utilisation-2026-09.csv +12 -0
- package/packs/sre-oncall/skills/delivery-metrics-report/evals/evals.json +27 -0
- package/packs/sre-oncall/skills/delivery-metrics-report/evals/files/3/deploys.csv +68 -0
- package/packs/sre-oncall/skills/delivery-metrics-report/evals/files/3/incidents.csv +7 -0
- package/packs/sre-oncall/skills/delivery-metrics-report/evals/files/3/prs.csv +46 -0
- package/packs/sre-oncall/skills/incident-comms/evals/evals.json +27 -0
- package/packs/sre-oncall/skills/incident-comms/evals/files/2/comms-rules.md +6 -0
- package/packs/sre-oncall/skills/incident-comms/evals/files/2/incident-channel.txt +17 -0
- package/packs/sre-oncall/skills/incident-comms/evals/files/2/last-status-update.md +5 -0
- package/packs/sre-oncall/skills/observability-plan/evals/evals.json +27 -0
- package/packs/sre-oncall/skills/observability-plan/evals/files/2/constraints.md +9 -0
- package/packs/sre-oncall/skills/observability-plan/evals/files/2/current-alerts.csv +10 -0
- package/packs/sre-oncall/skills/observability-plan/evals/files/2/service-overview.md +27 -0
- package/packs/sre-oncall/skills/runbook-generator/evals/evals.json +27 -0
- package/packs/sre-oncall/skills/runbook-generator/evals/files/2/deploy.sh +14 -0
- package/packs/sre-oncall/skills/runbook-generator/evals/files/2/handover-chat.txt +8 -0
- package/packs/sre-oncall/skills/runbook-generator/evals/files/2/service-notes.md +26 -0
- package/packs/sre-oncall/skills/runbook-generator/evals/files/2/standing-order.md +3 -0
- package/packs/stakeholder-comms/skills/audience-profile/evals/evals.json +26 -0
- package/packs/stakeholder-comms/skills/decision-log/evals/evals.json +26 -0
- package/packs/stakeholder-comms/skills/decision-log/evals/files/2/committee-meeting-2026-09-03.txt +23 -0
- package/packs/stakeholder-comms/skills/decision-log/evals/files/2/followup-thread.txt +6 -0
- package/packs/stakeholder-comms/skills/exec-summary/evals/evals.json +26 -0
- package/packs/stakeholder-comms/skills/exec-summary/evals/files/2/library-branches-options-paper.md +67 -0
- package/packs/trackers/skills/backlog-triage/evals/evals.json +27 -0
- package/packs/trackers/skills/backlog-triage/evals/files/3/parks-backlog.csv +19 -0
- package/packs/trackers/skills/team-status-report/evals/evals.json +28 -0
- package/packs/trackers/skills/team-status-report/evals/files/3/larkspur-week-42.csv +15 -0
- package/packs/trackers/skills/tracker-brief-sync/evals/evals.json +28 -0
- package/packs/trackers/skills/tracker-brief-sync/evals/files/3/brief-home-delivery.md +44 -0
- package/packs/dev-team/skills/devils-advocate/templates/challenge.md +0 -118
|
@@ -1,151 +1,92 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: devils-advocate
|
|
3
|
-
description: Adversarial review of
|
|
3
|
+
description: Adversarial review of a code change before it merges — the real bugs, most consequential first, each checked against the code before it is reported, with a fix that works. A passing test or clean local run counts as evidence only once you've said whether it would have caught the bug; when it couldn't, the review says why it passed and names the test that would have failed. Sized to the ask ("top 3", "skip the nits", "just security"); invents nothing about the codebase. Use right after a feature is declared done — before merging, before declaring "done", before moving to the next ticket.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# devils-advocate
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
- ❌ For greenfield exploration / spikes — adversarial review wastes effort on code that will be thrown away
|
|
23
|
-
- ❌ As a substitute for actual code review by a human — this is a *pre-pass*, not a replacement
|
|
8
|
+
**The rule: a green test is not evidence until you've said why it would have failed with the
|
|
9
|
+
bug.** Changes arrive with "tests pass" or "ran it locally, fine". The agent's habit is to
|
|
10
|
+
take that as support, or to ignore it. Both miss the most useful thing a reviewer can say:
|
|
11
|
+
*why* the test passed anyway. A fixture that happens to dodge the bug, a table that was empty
|
|
12
|
+
on the laptop, a test that runs the job once when the bug only shows on the second run, an
|
|
13
|
+
assertion that one bug cancels out. For each bug you find, say whether the cited tests or run
|
|
14
|
+
could have caught it, and if not, why it passed and the one test that would have failed:
|
|
15
|
+
✅ *"`test_refill` passes because it waits 2 s against a rate of 1/s, so it never sees the
|
|
16
|
+
partial-token case. Assert `allow()` is false at 0.5 s."*
|
|
17
|
+
|
|
18
|
+
A second rule: **report only what survives checking.** Before a finding goes in, trace it
|
|
19
|
+
through the code. Does an earlier guard already handle the case? Is the query actually
|
|
20
|
+
parameterised? Does the stated business rule make this behaviour correct? Does your own fix
|
|
21
|
+
do what you say it does? A false finding costs the reader more than a missed nit.
|
|
24
22
|
|
|
25
23
|
## How to respond
|
|
26
24
|
|
|
27
|
-
**
|
|
28
|
-
|
|
29
|
-
- **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.
|
|
30
|
-
- **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".
|
|
31
|
-
- **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.
|
|
32
|
-
- **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.
|
|
33
|
-
- **For this skill:** With no code or document to review, ask for it in one sentence and name the three or four things you will check in it, taken from what the user said it does.
|
|
34
|
-
|
|
35
|
-
1. **Identify the change under review.** In order of preference:
|
|
36
|
-
- The agent / user has just shown you a diff — review that diff.
|
|
37
|
-
- Git working tree has unstaged changes — `git diff` is the scope.
|
|
38
|
-
- User points to specific files / a PR / a recent commit — that's the scope.
|
|
39
|
-
- User says "the feature I just built" with no diff — ask which files, or run `git diff HEAD~1` if a commit was just made.
|
|
40
|
-
|
|
41
|
-
**Don't review the whole repo.** The scope is the *just-generated* code. Touched files only.
|
|
42
|
-
|
|
43
|
-
2. **Classify the code shape.** What you challenge depends on what was built. Map to one or more:
|
|
44
|
-
|
|
45
|
-
| Shape | Lens emphasis |
|
|
46
|
-
|---|---|
|
|
47
|
-
| HTTP / RPC endpoint, API handler | edge-cases (input validation, auth), adversarial (rate limit, error mapping) |
|
|
48
|
-
| Async handler / queue consumer | edge-cases (retries, idempotency, poison messages), future-proofing (ordering) |
|
|
49
|
-
| DB migration / schema change | future-proofing (rollback, online-safe), adversarial (locking, big-table risk) |
|
|
50
|
-
| State machine / workflow | edge-cases (impossible transitions, concurrent updates) |
|
|
51
|
-
| Parser / data transformation | edge-cases (malformed, encoding, huge inputs) |
|
|
52
|
-
| CLI tool / script | edge-cases (missing flags, env, partial runs) |
|
|
53
|
-
| UI component | edge-cases (loading/empty/error states, a11y) |
|
|
54
|
-
| Background job / cron | edge-cases (overlap, missed runs, partial completion) |
|
|
55
|
-
| Library / pure function | edge-cases (boundary inputs), future-proofing (API stability) |
|
|
56
|
-
| Config / infra change | adversarial (blast radius), future-proofing (drift) |
|
|
57
|
-
|
|
58
|
-
Multiple shapes is common — a PR can be "endpoint + migration + UI." Apply each lens to the right slice.
|
|
59
|
-
|
|
60
|
-
3. **Sweep the four lenses in order.** Each lens has its own checklist — read it, apply it, write findings. Don't merge lenses; each surfaces a different category of risk.
|
|
61
|
-
|
|
62
|
-
| Lens | File | Asks |
|
|
63
|
-
|---|---|---|
|
|
64
|
-
| 1. Edge cases | [`lenses/edge-cases.md`](lenses/edge-cases.md) | What inputs / states / timings break this? |
|
|
65
|
-
| 2. Future-proofing | [`lenses/future-proofing.md`](lenses/future-proofing.md) | What assumptions won't survive the next 6 months? |
|
|
66
|
-
| 3. Adversarial review | [`lenses/adversarial-review.md`](lenses/adversarial-review.md) | What would a staff engineer push back on in review? |
|
|
67
|
-
| 4. Test coverage | [`lenses/test-coverage.md`](lenses/test-coverage.md) | Which new code paths have no test, and which scenarios are missing tests? |
|
|
68
|
-
|
|
69
|
-
4. **Categorize every finding by severity:**
|
|
70
|
-
|
|
71
|
-
| Severity | Meaning |
|
|
72
|
-
|---|---|
|
|
73
|
-
| 🟥 **Blocker** | This will ship a bug or a security issue. Examples: SQL injection, unhandled `null`, race condition on shared state, migration that locks a 10M-row table, missing auth check, infinite retry loop. |
|
|
74
|
-
| 🟧 **Major** | This will hurt within months. Examples: hard-coded limits that 2× growth will hit, no idempotency on a retryable operation, error swallowed silently, no observability for a load-bearing path, magic constants. |
|
|
75
|
-
| 🟨 **Minor** | Good craft. Examples: missing edge-case test, suboptimal log level, naming that won't age well, a comment that explains *what* instead of *why*. |
|
|
76
|
-
| ⚪ **Nit** | Style, formatting, micro-preference. Optional. |
|
|
77
|
-
|
|
78
|
-
When in doubt between two severities, **downgrade**. Inflated severity makes the report unreadable. If your output has more blockers than major findings, you're inflating.
|
|
79
|
-
|
|
80
|
-
5. **For every finding, write four parts:**
|
|
81
|
-
|
|
82
|
-
- **Where:** `file:line` (e.g. `src/api/checkout.ts:47`) — and quote 5–15 chars of the offending line.
|
|
83
|
-
- **What's wrong:** one sentence — which lens fired and why.
|
|
84
|
-
- **Concrete scenario:** ✅ "When `discount=null` reaches line 47, `applyDiscount` throws `TypeError`." — ❌ "Doesn't handle null." A scenario the first pass can reproduce.
|
|
85
|
-
- **Fix:** ✅ "Add `if (discount == null) return 0;` at the top of `applyDiscount` — or thread the null through to skip the line item entirely (preferred — see how `applyTax` handles it at line 102)." — ❌ "Add null handling."
|
|
86
|
-
|
|
87
|
-
6. **Open with a verdict, not the findings.** First thing the user sees:
|
|
88
|
-
|
|
89
|
-
```
|
|
90
|
-
Verdict: SHIP WITH FIXES — 2 blockers, 5 major, 4 minor, 3 nits.
|
|
91
|
-
|
|
92
|
-
The endpoint validates auth and the happy path but doesn't bound the request body (blocker — DoS via 1GB payload), and the new migration adds a NOT NULL column without a default on a table the migration script doesn't backfill (blocker — will fail on first deploy). Of the 5 major findings, 3 are around idempotency on the retry path. Test coverage misses the new error branches at api/checkout.ts:71–88 entirely.
|
|
93
|
-
```
|
|
25
|
+
1. **Answer the question first.** Open with a plain answer to what they asked, usually
|
|
26
|
+
whether it's safe to merge, and the worst problem in one sentence.
|
|
94
27
|
|
|
95
|
-
|
|
28
|
+
2. **Review the change, not the repo.** The diff, the files they named, or `git diff` of the
|
|
29
|
+
working tree. Read the tests that came with it.
|
|
96
30
|
|
|
97
|
-
|
|
31
|
+
3. **Find what would bite, most consequential first.** Wrong results, data loss, security
|
|
32
|
+
holes, failures on real data that the dev setup hides, retries and concurrency, then
|
|
33
|
+
fragility. For a large or unfamiliar change, the checklists in
|
|
34
|
+
[`lenses/edge-cases.md`](lenses/edge-cases.md),
|
|
35
|
+
[`lenses/future-proofing.md`](lenses/future-proofing.md),
|
|
36
|
+
[`lenses/adversarial-review.md`](lenses/adversarial-review.md) and
|
|
37
|
+
[`lenses/test-coverage.md`](lenses/test-coverage.md) are a sweep to run privately; they are
|
|
38
|
+
not sections of the reply.
|
|
98
39
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
40
|
+
4. **Each finding:** where (`file:line`), what goes wrong in one concrete scenario ("when
|
|
41
|
+
`tz` is `None`, line 47 raises `TypeError`"), and the fix. When the fix is small,
|
|
42
|
+
give it as code they can paste. When the change is a script or migration, give the
|
|
43
|
+
corrected version.
|
|
102
44
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
45
|
+
5. **Say what the tests prove.** Apply the rule to every test or run the user cites, and name
|
|
46
|
+
the missing tests as scenarios: ✅ *"No test runs the job twice; add one asserting the reminder
|
|
47
|
+
email is sent once."*
|
|
106
48
|
|
|
107
|
-
|
|
49
|
+
6. **Size to the ask.** "Top 3" gets three. "Skip the nits" gets none. "Just security" gets
|
|
50
|
+
security. Without a limit, report what would actually bite; leave style out unless asked.
|
|
51
|
+
No severity counts, verdict labels or praise sections unless they asked.
|
|
108
52
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
### 4. Test coverage
|
|
53
|
+
7. **Invent nothing about the codebase.** No row counts, traffic, run times, callers,
|
|
54
|
+
column contents or config you weren't shown. Where a finding depends on one, say what to
|
|
55
|
+
check: "if `orders` already has rows in production, this migration fails".
|
|
113
56
|
|
|
114
|
-
|
|
115
|
-
|
|
57
|
+
8. **Deliver from what you have.** With code to review, review it. With nothing, ask for the
|
|
58
|
+
diff in one sentence and name the three or four things you'll check in it.
|
|
116
59
|
|
|
117
|
-
|
|
118
|
-
1. Fix blockers
|
|
119
|
-
2. Add the missing tests in §4 (they double as regression catches)
|
|
120
|
-
3. Major findings in priority order
|
|
121
|
-
```
|
|
60
|
+
## Useful references in this skill
|
|
122
61
|
|
|
123
|
-
|
|
62
|
+
- [`examples/rate-limiter-review.md`](examples/rate-limiter-review.md) — a change with "tests pass", and a review that says why they passed
|
|
124
63
|
|
|
125
64
|
## Quality bar
|
|
126
65
|
|
|
127
|
-
- **Every
|
|
128
|
-
|
|
129
|
-
- **
|
|
130
|
-
|
|
131
|
-
- **
|
|
132
|
-
- **
|
|
66
|
+
- **Every cited test or run is judged:** could it have caught each bug, and if not, why did it
|
|
67
|
+
pass and what test would fail.
|
|
68
|
+
- **Every finding survives a trace through the code and the stated rules.** No false
|
|
69
|
+
positives.
|
|
70
|
+
- **The answer to "can this merge?" comes first;** findings are ordered by consequence.
|
|
71
|
+
- **Fixes work,** and are code when the change is small.
|
|
72
|
+
- **The scope asked for is respected.**
|
|
73
|
+
- **Nothing about the codebase is invented.**
|
|
133
74
|
|
|
134
75
|
## When NOT to use this skill
|
|
135
76
|
|
|
136
|
-
- ❌ For greenfield prototype
|
|
137
|
-
- ❌ For one-line
|
|
138
|
-
- ❌ As a substitute for human code review — it's a pre-pass
|
|
139
|
-
- ❌ For *evaluating the choice of approach* before code exists. That's a design conversation
|
|
140
|
-
- ❌ For
|
|
77
|
+
- ❌ For greenfield prototype or spike code that's about to be thrown away.
|
|
78
|
+
- ❌ For one-line or trivial changes.
|
|
79
|
+
- ❌ As a substitute for human code review — it's a pre-pass.
|
|
80
|
+
- ❌ For *evaluating the choice of approach* before code exists. That's a design conversation: `design-doc`, then `doc-critique`.
|
|
81
|
+
- ❌ For code in a language you can't reason about line by line. Refuse rather than fabricate findings.
|
|
141
82
|
|
|
142
83
|
## Anti-patterns to avoid
|
|
143
84
|
|
|
144
|
-
- ❌ **
|
|
145
|
-
- ❌ **
|
|
146
|
-
|
|
147
|
-
- ❌ **
|
|
148
|
-
- ❌ **
|
|
149
|
-
- ❌ **
|
|
150
|
-
- ❌ **
|
|
151
|
-
- ❌ **
|
|
85
|
+
- ❌ **"Tests pass" taken at face value,** or ignored. Say what they prove.
|
|
86
|
+
- ❌ **The plausible false positive.** SQL injection in a parameterised query; a bug in
|
|
87
|
+
behaviour the business rule asks for; a CRLF issue text mode already handles.
|
|
88
|
+
- ❌ **Padding to fill sections.** Marginal findings are where the errors are.
|
|
89
|
+
- ❌ **A count that disagrees with the list.** Don't print counts.
|
|
90
|
+
- ❌ **Describing a fix instead of giving it** when it fits in a few lines.
|
|
91
|
+
- ❌ **Facts about production you weren't given.** Row counts, lock times, what a column holds.
|
|
92
|
+
- ❌ **Redesigning.** Stay on what's missing given this approach.
|
|
@@ -72,6 +72,33 @@
|
|
|
72
72
|
"It does not state the row count of orders, how long the migration will take, or what customers.region contains as fact",
|
|
73
73
|
"It leads with three main concerns as asked, and anything beyond the three is kept to a brief mention"
|
|
74
74
|
]
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
"id": 6,
|
|
78
|
+
"held_out": "2026-10-17",
|
|
79
|
+
"prompt": "I'm the reviewer on PR #412 for the Fernlocket loyalty service and it's meant to merge this afternoon. The change is in redeem.py, the tests are in test_redeem.py, and the author's description and test run are in PR_DESCRIPTION.md. Find what's wrong with it before it ships. Give me the three issues that matter most, most serious first, and skip style and naming nits entirely.",
|
|
80
|
+
"assertions": [
|
|
81
|
+
"It finds that quantity is only checked against the upper limit, so a negative quantity gives a negative cost that passes the balance check and adds points to the member's balance, and puts this among its top issues",
|
|
82
|
+
"It finds that the points are deducted and committed before fulfillment.ship() is called, so a failed shipment leaves the member charged with no refund, and that the \"Please try again\" message invites a second deduction for the same reward",
|
|
83
|
+
"It does not report the f-string query in list_redemptions as SQL injection, since the ORDER BY text can only come from the fixed SORT_COLUMNS mapping and the values are bound parameters, and it does not report the balance read-then-update as a double-spend race, since the row is locked with FOR UPDATE inside the same transaction",
|
|
84
|
+
"It says test_fulfillment_failure_returns_502 would pass with the bug in place, because it checks only the status and call count and never the member's balance, so the 6-passed run does not show that #398 is fixed",
|
|
85
|
+
"Its fixes would work: quantity must be required to be at least 1 (a tighter upper limit is not enough), and on a failed shipment the points must be rolled back or credited back, or not committed until the shipment succeeds; logging the failure or changing the status code alone does not count",
|
|
86
|
+
"It does not state as fact anything the files don't show: how fulfillment.ship() or the transaction helper behave beyond what the code and comments say, whether the fulfillment service rejects a negative quantity, how many members or redemptions there are, or what else calls redeem()",
|
|
87
|
+
"It leads with three issues, most serious first, and makes no style or naming comments; anything beyond the three is at most a brief mention"
|
|
88
|
+
]
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
"id": 7,
|
|
92
|
+
"held_out": "2026-10-17",
|
|
93
|
+
"prompt": "hey quick one, can you check this before I push? it sends the weekly digest to all subscribers, max 8 at a time. we're on go 1.23. go test ./... is green and I ran it on staging against a handful of test subscribers, all delivered fine. just tell me if anything's actually broken, I don't care about style\n\n```go\nfunc SendAll(ctx context.Context, subs []Subscriber, d Digest) error {\n\tsem := make(chan struct{}, 8) // at most 8 pushes in flight\n\tvar wg sync.WaitGroup\n\tvar errs []error\n\n\tfor _, s := range subs {\n\t\twg.Add(1)\n\t\tsem <- struct{}{}\n\t\tgo func() {\n\t\t\tdefer wg.Done()\n\t\t\tdefer func() { <-sem }()\n\t\t\tif err := push(ctx, s, d); err != nil {\n\t\t\t\terrs = append(errs, fmt.Errorf(\"subscriber %s: %w\", s.ID, err))\n\t\t\t}\n\t\t}()\n\t}\n\n\twg.Wait()\n\treturn errors.Join(errs...)\n}\n```",
|
|
94
|
+
"assertions": [
|
|
95
|
+
"It finds that the goroutines append to the shared errs slice with no lock, a data race that can lose errors or corrupt the slice when more than one push fails",
|
|
96
|
+
"It does not flag the closure capturing the loop variable s as a bug, since on Go 1.22 and later each loop iteration gets its own s",
|
|
97
|
+
"It says the green test run and the staging run don't show the code is safe: with every push succeeding the append never runs, and a plain go test without -race wouldn't report the race anyway",
|
|
98
|
+
"Its fix would work: a mutex around the append, or a per-subscriber slot in a slice sized to len(subs); if it suggests errgroup it says that this changes the behaviour (only the first error comes back, and with a group context the rest of the sends are cancelled)",
|
|
99
|
+
"It does not state as fact what push() does inside (retries, timeouts, how it uses ctx), how many subscribers there are, or that this has already failed in production",
|
|
100
|
+
"It keeps to what's actually broken, as asked: a short reply with no style or naming suggestions, not padded out with a long list of minor or hypothetical concerns"
|
|
101
|
+
]
|
|
75
102
|
}
|
|
76
103
|
]
|
|
77
104
|
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
## Synchronous reward redemption (#412)
|
|
2
|
+
|
|
3
|
+
Members currently wait for the nightly batch to find out whether a redemption went through.
|
|
4
|
+
This moves redemption onto a synchronous endpoint so they get an answer straight away.
|
|
5
|
+
|
|
6
|
+
- `redeem()` backs `POST /members/{member_id}/redemptions`
|
|
7
|
+
- `list_redemptions()` backs the history screen, with a `sort` option
|
|
8
|
+
- The balance row is locked with `SELECT ... FOR UPDATE`, so a double tap on "Redeem" can't spend the same points twice
|
|
9
|
+
- A failed shipment returns 502 and asks the member to try again; this closes #398 and is covered by `test_fulfillment_failure_returns_502`
|
|
10
|
+
- Per-order cap of 10 so nobody can clear out a reward's stock in one request
|
|
11
|
+
|
|
12
|
+
### Test run
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
$ pytest tests/test_redeem.py -q
|
|
16
|
+
...... [100%]
|
|
17
|
+
6 passed in 0.41s
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Ready for review. I'd like to get this into Thursday's release.
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"""Reward redemption for the Fernlocket loyalty programme.
|
|
2
|
+
|
|
3
|
+
PR #412: move redemption off the nightly batch onto a synchronous endpoint.
|
|
4
|
+
"""
|
|
5
|
+
import logging
|
|
6
|
+
import uuid
|
|
7
|
+
|
|
8
|
+
from .db import transaction
|
|
9
|
+
from .fulfillment import FulfillmentClient, FulfillmentError
|
|
10
|
+
|
|
11
|
+
log = logging.getLogger(__name__)
|
|
12
|
+
|
|
13
|
+
MAX_PER_ORDER = 10
|
|
14
|
+
|
|
15
|
+
SORT_COLUMNS = {
|
|
16
|
+
"newest": "created_at DESC",
|
|
17
|
+
"oldest": "created_at ASC",
|
|
18
|
+
"points": "points_spent DESC",
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class RedemptionError(Exception):
|
|
23
|
+
def __init__(self, status: int, message: str):
|
|
24
|
+
super().__init__(message)
|
|
25
|
+
self.status = status
|
|
26
|
+
self.message = message
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def redeem(conn, fulfillment: FulfillmentClient, member_id: str, payload: dict) -> dict:
|
|
30
|
+
"""Spend a member's points on a reward and ship it.
|
|
31
|
+
|
|
32
|
+
Called by POST /members/{member_id}/redemptions with the JSON body as `payload`.
|
|
33
|
+
"""
|
|
34
|
+
reward_id = payload["reward_id"]
|
|
35
|
+
quantity = int(payload.get("quantity", 1))
|
|
36
|
+
if quantity > MAX_PER_ORDER:
|
|
37
|
+
raise RedemptionError(400, f"at most {MAX_PER_ORDER} of a reward per order")
|
|
38
|
+
|
|
39
|
+
with transaction(conn) as cur:
|
|
40
|
+
cur.execute(
|
|
41
|
+
"SELECT points_cost, active FROM rewards WHERE id = %s",
|
|
42
|
+
(reward_id,),
|
|
43
|
+
)
|
|
44
|
+
row = cur.fetchone()
|
|
45
|
+
if row is None or not row[1]:
|
|
46
|
+
raise RedemptionError(404, "reward not available")
|
|
47
|
+
cost = row[0] * quantity
|
|
48
|
+
|
|
49
|
+
# Lock the member's balance row so two taps on "Redeem" can't both spend it.
|
|
50
|
+
cur.execute(
|
|
51
|
+
"SELECT balance FROM point_accounts WHERE member_id = %s FOR UPDATE",
|
|
52
|
+
(member_id,),
|
|
53
|
+
)
|
|
54
|
+
balance = cur.fetchone()[0]
|
|
55
|
+
if balance < cost:
|
|
56
|
+
raise RedemptionError(409, "not enough points")
|
|
57
|
+
|
|
58
|
+
redemption_id = str(uuid.uuid4())
|
|
59
|
+
cur.execute(
|
|
60
|
+
"UPDATE point_accounts SET balance = %s WHERE member_id = %s",
|
|
61
|
+
(balance - cost, member_id),
|
|
62
|
+
)
|
|
63
|
+
cur.execute(
|
|
64
|
+
"INSERT INTO redemptions (id, member_id, reward_id, quantity, points_spent, status)"
|
|
65
|
+
" VALUES (%s, %s, %s, %s, %s, 'pending')",
|
|
66
|
+
(redemption_id, member_id, reward_id, quantity, cost),
|
|
67
|
+
)
|
|
68
|
+
# transaction commits on leaving the block
|
|
69
|
+
|
|
70
|
+
try:
|
|
71
|
+
fulfillment.ship(redemption_id, reward_id, quantity)
|
|
72
|
+
except FulfillmentError:
|
|
73
|
+
log.exception("fulfillment failed for redemption %s", redemption_id)
|
|
74
|
+
raise RedemptionError(502, "We couldn't send your reward. Please try again.")
|
|
75
|
+
|
|
76
|
+
with transaction(conn) as cur:
|
|
77
|
+
cur.execute(
|
|
78
|
+
"UPDATE redemptions SET status = 'shipped' WHERE id = %s",
|
|
79
|
+
(redemption_id,),
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
return {
|
|
83
|
+
"redemption_id": redemption_id,
|
|
84
|
+
"points_spent": cost,
|
|
85
|
+
"balance": balance - cost,
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def list_redemptions(conn, member_id: str, sort: str = "newest", limit: int = 20) -> list:
|
|
90
|
+
"""Redemption history for the member's account screen."""
|
|
91
|
+
order_by = SORT_COLUMNS.get(sort)
|
|
92
|
+
if order_by is None:
|
|
93
|
+
raise RedemptionError(400, "unknown sort")
|
|
94
|
+
limit = max(1, min(int(limit), 100))
|
|
95
|
+
|
|
96
|
+
with transaction(conn) as cur:
|
|
97
|
+
cur.execute(
|
|
98
|
+
"SELECT id, reward_id, quantity, points_spent, status, created_at"
|
|
99
|
+
f" FROM redemptions WHERE member_id = %s ORDER BY {order_by} LIMIT %s",
|
|
100
|
+
(member_id, limit),
|
|
101
|
+
)
|
|
102
|
+
return cur.fetchall()
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
|
|
3
|
+
from rewards.fulfillment import FulfillmentError
|
|
4
|
+
from rewards.redeem import RedemptionError, list_redemptions, redeem
|
|
5
|
+
from tests.fakes import FakeFulfillment, make_db
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@pytest.fixture
|
|
9
|
+
def db():
|
|
10
|
+
conn = make_db()
|
|
11
|
+
conn.seed_member("m-100", balance=500)
|
|
12
|
+
conn.seed_reward("r-mug", points_cost=120, active=True)
|
|
13
|
+
conn.seed_reward("r-old-tote", points_cost=50, active=False)
|
|
14
|
+
return conn
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def test_happy_path(db):
|
|
18
|
+
result = redeem(db, FakeFulfillment(), "m-100", {"reward_id": "r-mug", "quantity": 2})
|
|
19
|
+
assert result["points_spent"] == 240
|
|
20
|
+
assert db.balance("m-100") == 260
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def test_not_enough_points(db):
|
|
24
|
+
with pytest.raises(RedemptionError) as err:
|
|
25
|
+
redeem(db, FakeFulfillment(), "m-100", {"reward_id": "r-mug", "quantity": 5})
|
|
26
|
+
assert err.value.status == 409
|
|
27
|
+
assert db.balance("m-100") == 500
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def test_inactive_reward(db):
|
|
31
|
+
with pytest.raises(RedemptionError) as err:
|
|
32
|
+
redeem(db, FakeFulfillment(), "m-100", {"reward_id": "r-old-tote"})
|
|
33
|
+
assert err.value.status == 404
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def test_quantity_limit(db):
|
|
37
|
+
with pytest.raises(RedemptionError) as err:
|
|
38
|
+
redeem(db, FakeFulfillment(), "m-100", {"reward_id": "r-mug", "quantity": 11})
|
|
39
|
+
assert err.value.status == 400
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def test_fulfillment_failure_returns_502(db):
|
|
43
|
+
"""Regression for #398: a failed shipment must not cost the member their points."""
|
|
44
|
+
fulfillment = FakeFulfillment(fail_with=FulfillmentError("carrier timeout"))
|
|
45
|
+
with pytest.raises(RedemptionError) as err:
|
|
46
|
+
redeem(db, fulfillment, "m-100", {"reward_id": "r-mug", "quantity": 1})
|
|
47
|
+
assert err.value.status == 502
|
|
48
|
+
assert fulfillment.calls == 1
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def test_history_rejects_unknown_sort(db):
|
|
52
|
+
with pytest.raises(RedemptionError) as err:
|
|
53
|
+
list_redemptions(db, "m-100", sort="points; DROP TABLE redemptions")
|
|
54
|
+
assert err.value.status == 400
|
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
# Worked example —
|
|
1
|
+
# Worked example — review of a rate limiter that "passes its tests"
|
|
2
2
|
|
|
3
|
-
Input → output oracle for `devils-advocate`. Shows the
|
|
4
|
-
|
|
5
|
-
generic "looks good, consider tests" pass.
|
|
3
|
+
Input → output oracle for `devils-advocate`. Shows the answer first, findings that survive a
|
|
4
|
+
trace through the code, and the cited tests judged for what they could and could not catch.
|
|
6
5
|
|
|
7
|
-
## Input
|
|
6
|
+
## Input
|
|
8
7
|
|
|
9
8
|
> "Implemented the per-user rate limiter, tests pass, ready for review."
|
|
10
9
|
|
|
@@ -23,24 +22,45 @@ def allow(user_id: str, rate: float, burst: float) -> bool:
|
|
|
23
22
|
return False
|
|
24
23
|
```
|
|
25
24
|
|
|
25
|
+
```python
|
|
26
|
+
# test_ratelimit.py
|
|
27
|
+
def test_burst_then_deny():
|
|
28
|
+
for _ in range(3):
|
|
29
|
+
assert allow("u1", rate=1, burst=3)
|
|
30
|
+
assert not allow("u1", rate=1, burst=3)
|
|
31
|
+
```
|
|
32
|
+
|
|
26
33
|
## Output (the review)
|
|
27
34
|
|
|
28
|
-
**
|
|
35
|
+
**Not ready to merge.** Under concurrent requests for one user it admits more than the limit,
|
|
36
|
+
and the test can't see that.
|
|
37
|
+
|
|
38
|
+
1. **Race on the shared dict** — `ratelimit.py:6-10`. Two requests for the same user both read
|
|
39
|
+
`tokens=1.0`, both pass, both write back `0.0`, so parallel bursts get through at up to
|
|
40
|
+
twice the intended rate. Fix: hold a per-user lock around the read-modify-write:
|
|
29
41
|
|
|
30
|
-
|
|
42
|
+
```python
|
|
43
|
+
_locks = collections.defaultdict(threading.Lock)
|
|
31
44
|
|
|
32
|
-
|
|
45
|
+
def allow(user_id, rate, burst):
|
|
46
|
+
with _locks[user_id]:
|
|
47
|
+
... # existing body unchanged
|
|
48
|
+
```
|
|
33
49
|
|
|
34
|
-
|
|
50
|
+
2. **Memory grows without bound** — `ratelimit.py:2`. Nothing ever leaves `_buckets`, so every
|
|
51
|
+
distinct `user_id` stays for the life of the process. Fix: a `cachetools.TTLCache` in place
|
|
52
|
+
of the dict, with a TTL longer than `burst / rate`.
|
|
35
53
|
|
|
36
|
-
|
|
54
|
+
**What the test proves.** `test_burst_then_deny` runs on one thread, so it passes with the
|
|
55
|
+
race in place. It also never waits, so refill is untested. Tests that would fail today:
|
|
37
56
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
-
|
|
41
|
-
- Eviction: memory stays bounded under 10k distinct ids.
|
|
42
|
-
- Boundary: `tokens` sitting at exactly `1.0` admits; at `0.999…` denies.
|
|
57
|
+
- Ten threads calling `allow("u1", rate=1, burst=3)` at once; assert at most three return
|
|
58
|
+
`True`.
|
|
59
|
+
- After draining the bucket, `allow` is false at 0.5 s and true at 1.0 s (with a patched clock).
|
|
43
60
|
|
|
44
61
|
## Why this passes the quality bar
|
|
45
62
|
|
|
46
|
-
|
|
63
|
+
The merge answer comes first. Each finding names the line, a concrete failure and a fix as
|
|
64
|
+
code. The cited test is judged for what it could catch, and the missing tests are scenarios.
|
|
65
|
+
The free first burst for a new user was left out: it is a product choice, not a bug in this
|
|
66
|
+
change.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "devils-advocate",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Adversarial review of
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Adversarial review of a code change before it merges — the real bugs, most consequential first, each checked against the code before it is reported, with a fix that works. A passing test or clean local run counts as evidence only once you've said whether it would have caught the bug; when it couldn't, the review says why it passed and names the test that would have failed. Sized to the ask (\"top 3\", \"skip the nits\", \"just security\"); invents nothing about the codebase. Use right after a feature is declared done — before merging, before declaring \"done\", before moving to the next ticket.",
|
|
5
5
|
"entrypoint": "SKILL.md",
|
|
6
6
|
"deps": { "npm": [], "pip": [] },
|
|
7
7
|
"env": { "required": [], "optional": [] },
|
|
@@ -10,5 +10,5 @@
|
|
|
10
10
|
{ "event": "pre-commit-review", "action": "devils-advocate", "description": "adversarially review staged changes before they land" }
|
|
11
11
|
],
|
|
12
12
|
"tags": ["review", "code-review", "adversarial", "edge-cases", "future-proofing", "test-coverage", "dev-workflow"],
|
|
13
|
-
"model": { "tier": "heavy", "rationale": "Adversarial code review
|
|
13
|
+
"model": { "tier": "heavy", "rationale": "Adversarial code review that must trace each finding and judge what the tests prove. Cheaper models miss edge cases and report false positives. Never downgrade." }
|
|
14
14
|
}
|
|
@@ -14,6 +14,33 @@
|
|
|
14
14
|
"Final report has a status (VERIFIED, VERIFIED WITH OPEN ITEMS, or BLOCKED), an acceptance-criteria table mapping each criterion to a test, manual-verify, or open item, an honest round log, and open items",
|
|
15
15
|
"Does not add criteria the user did not give without echoing them back first"
|
|
16
16
|
]
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"id": 2,
|
|
20
|
+
"held_out": "2026-10-18",
|
|
21
|
+
"prompt": "Please implement LIB-88 (TICKET.md) in this repo - the code is fees.py and desk.py, closures.csv holds the closure dates, and the tests are in tests/test_fees.py. Tests run with `python3 -m unittest discover -s tests`. When you're done, give me a short summary: what you built, what you checked, and anything I need to decide.",
|
|
22
|
+
"assertions": [
|
|
23
|
+
"Implements the fee calculation with tests, runs `python3 -m unittest discover -s tests`, and reports the real result; it does not claim tests pass without having run them",
|
|
24
|
+
"Flags that the ticket's worked example is wrong: 8 March is a Sunday and 9 March is a closure day, so 3 chargeable days and $0.75, not $1.25; no test expects $1.25",
|
|
25
|
+
"Notices that the existing MAX_FEE of $15.00 in fees.py conflicts with the ticket's $10.00 cap and says which one it applied",
|
|
26
|
+
"Has tests with specific expected amounts for the junior-card rule, the cap at the lower of replacement cost and $10.00, and the Sunday and closure exclusions",
|
|
27
|
+
"Does not invent a daily rate for magazines; a magazine is rejected or raised as a decision for the user",
|
|
28
|
+
"Uses only the standard library, and the existing tests and desk.checkin still work",
|
|
29
|
+
"The closing summary is short (a few lines plus any decisions needed)"
|
|
30
|
+
]
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"id": 3,
|
|
34
|
+
"held_out": "2026-10-18",
|
|
35
|
+
"prompt": "hi, I do the tea and setup rota for our community choir with rota.py (members.txt is everyone). can you make it skip people who are away? they're in away.txt, I typed it in a hurry. just make it work and tell me in a couple of lines what you changed, I'm not a programmer",
|
|
36
|
+
"assertions": [
|
|
37
|
+
"After the change, running rota.py puts nobody on a date when away.txt says they are away, even though away.txt uses different capitalisation and extra spaces",
|
|
38
|
+
"Handles or clearly points out that Edith Marsh is back for 5 Dec, rather than silently leaving her off every date or treating '(back for the 5 dec one)' as part of her name",
|
|
39
|
+
"The header line and the name that is not a member (Ruth Ng) cause no error and do not appear in the rota",
|
|
40
|
+
"The script is actually run and the output keeps the same 'date: name & name' format",
|
|
41
|
+
"The reply to the user is a couple of plain-language lines without programming jargon",
|
|
42
|
+
"Does not add features that were not asked for (no new options, dependencies or file formats)"
|
|
43
|
+
]
|
|
17
44
|
}
|
|
18
45
|
]
|
|
19
46
|
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# LIB-88: Late fees for overdue items
|
|
2
|
+
|
|
3
|
+
As a circulation desk volunteer, I want the system to work out the late fee when an item is
|
|
4
|
+
checked in, so every patron is charged the same way.
|
|
5
|
+
|
|
6
|
+
Constraint: the desk laptops are locked down. Python 3.10, standard library only - nothing can
|
|
7
|
+
be pip-installed.
|
|
8
|
+
|
|
9
|
+
## Acceptance criteria
|
|
10
|
+
|
|
11
|
+
1. Fee = daily rate x chargeable days late. Daily rates: book $0.25, DVD $1.00.
|
|
12
|
+
2. Sundays, and the dates listed in closures.csv, are not chargeable days.
|
|
13
|
+
3. An item returned on or before its due date is charged $0.00.
|
|
14
|
+
4. The fee for one item is capped at its replacement cost or $10.00, whichever is lower.
|
|
15
|
+
5. Patrons with a junior card (card type "junior") are never charged.
|
|
16
|
+
|
|
17
|
+
## Worked example (from the desk)
|
|
18
|
+
|
|
19
|
+
A book due Friday 2026-03-06 and returned Wednesday 2026-03-11 is 5 days late:
|
|
20
|
+
5 x $0.25 = $1.25.
|