@mmerterden/multi-agent-pipeline 12.6.0 → 12.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +209 -0
- package/README.md +18 -18
- package/docs/FIGMA_PIPELINE.md +34 -34
- package/docs/adr/0001-three-model-triage.md +12 -12
- package/docs/adr/0002-instruction-driven-flag.md +5 -5
- package/docs/adr/0003-unified-shared-skills.md +5 -5
- package/docs/adr/0004-zero-dependency-philosophy.md +5 -5
- package/docs/adr/0005-lazy-phase-docs.md +2 -2
- package/docs/adr/0006-skills-core-external-split.md +6 -6
- package/docs/adr/0007-multi-tool-adapter-framework.md +19 -19
- package/docs/adr/0008-installer-modularization-and-secret-leak-defense.md +19 -19
- package/docs/adr/README.md +1 -1
- package/docs/best-practices.md +3 -3
- package/docs/features.md +28 -28
- package/docs/performance.md +16 -16
- package/docs/recovery-guide.md +39 -39
- package/index.js +4 -4
- package/install/_common.mjs +53 -11
- package/install/_copilot-instructions.mjs +2 -2
- package/install/_dev-only-files.mjs +126 -6
- package/install/_platform-filter.mjs +1 -1
- package/install/_telemetry.mjs +1 -1
- package/install/claude.mjs +20 -13
- package/install/copilot.mjs +11 -23
- package/install/index.mjs +7 -15
- package/install/templates/copilot-instructions.md +54 -54
- package/install.js +1 -1
- package/package.json +29 -11
- package/pipeline/commands/multi-agent/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/analysis/SKILL.md +2 -2
- package/pipeline/commands/multi-agent/analysis-resolve/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/autopilot/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/build-optimize/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/create-jira/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/design-check/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/dev/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/dev-autopilot/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/dev-local/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/dev-local-autopilot/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/diff-explain/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/finish/SKILL.md +6 -6
- package/pipeline/commands/multi-agent/forget/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/garbage-collect/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/help/SKILL.md +3 -3
- package/pipeline/commands/multi-agent/issue/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/jira/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/kill/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/language/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/local/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/local-autopilot/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/log/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/manual-test/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/prune-logs/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/purge/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/refactor/SKILL.md +16 -8
- package/pipeline/commands/multi-agent/resume/SKILL.md +2 -2
- package/pipeline/commands/multi-agent/review/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/review-issue/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/review-jira/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/routines/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/save/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/scan/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/search/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/setup/SKILL.md +2 -2
- package/pipeline/commands/multi-agent/stack/SKILL.md +3 -3
- package/pipeline/commands/multi-agent/status/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/sync/SKILL.md +5 -5
- package/pipeline/commands/multi-agent/test/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/uninstall/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/update/SKILL.md +3 -3
- package/pipeline/lib/account-resolver.sh +1 -1
- package/pipeline/lib/channels-multi-repo.sh +1 -1
- package/pipeline/lib/context-link-extractor.sh +1 -1
- package/pipeline/lib/credential-store.sh +21 -1
- package/pipeline/lib/fetch-confluence.sh +1 -1
- package/pipeline/lib/fetch-crashlytics.sh +1 -1
- package/pipeline/lib/fetch-fortify.sh +1 -1
- package/pipeline/lib/fetch-graylog.sh +1 -1
- package/pipeline/lib/fetch-swagger.sh +1 -1
- package/pipeline/lib/issue-fetcher.sh +1 -1
- package/pipeline/lib/multi-repo-pipeline.sh +1 -1
- package/pipeline/lib/repo-cache.sh +1 -1
- package/pipeline/lib/submodule-detector.sh +1 -1
- package/pipeline/multi-agent-refs/_account-picker.md +1 -1
- package/pipeline/multi-agent-refs/_dev-context.md +1 -1
- package/pipeline/multi-agent-refs/_repo-picker.md +1 -1
- package/pipeline/multi-agent-refs/component-dispatch.md +1 -1
- package/pipeline/multi-agent-refs/component-generation.md +121 -0
- package/pipeline/multi-agent-refs/cross-cli-contract.md +1 -1
- package/pipeline/multi-agent-refs/features/model-fallback.md +2 -2
- package/pipeline/multi-agent-refs/phases/operations.md +28 -0
- package/pipeline/multi-agent-refs/phases/phase-0-init.md +1 -0
- package/pipeline/multi-agent-refs/phases/phase-2-planning.md +1 -1
- package/pipeline/multi-agent-refs/phases/phase-3-dev.md +1 -2
- package/pipeline/multi-agent-refs/phases/phase-4-review.md +50 -5
- package/pipeline/preferences-template.json +5 -11
- package/pipeline/schemas/agent-state.schema.json +39 -9
- package/pipeline/schemas/analysis-output.schema.json +18 -4
- package/pipeline/schemas/analysis-spec.schema.json +120 -32
- package/pipeline/schemas/clarify-output.schema.json +15 -5
- package/pipeline/schemas/design-check-config.schema.json +32 -11
- package/pipeline/schemas/dev-critic-output.schema.json +20 -5
- package/pipeline/schemas/figma-project-config.schema.json +42 -10
- package/pipeline/schemas/learnings-ledger.schema.json +10 -2
- package/pipeline/schemas/migrations/figma-config-1.0.0-to-2.0.0.mjs +1 -4
- package/pipeline/schemas/migrations/prefs-2.0.0-to-2.1.0.mjs +24 -7
- package/pipeline/schemas/plan-todos.schema.json +6 -3
- package/pipeline/schemas/planning-output.schema.json +5 -1
- package/pipeline/schemas/prefs.schema.json +97 -229
- package/pipeline/schemas/test-gap.schema.json +5 -5
- package/pipeline/schemas/token-budget.json +8 -8
- package/pipeline/schemas/triage-corpus.schema.json +1 -1
- package/pipeline/scripts/_smoke-root.sh +61 -0
- package/pipeline/scripts/aggregate-metrics.mjs +18 -6
- package/pipeline/scripts/audit-log.sh +25 -0
- package/pipeline/scripts/build-skills-index.mjs +6 -2
- package/pipeline/scripts/build-stack-plugins.mjs +142 -39
- package/pipeline/scripts/check-derived-drift.mjs +196 -0
- package/pipeline/scripts/classify-plan-safety.mjs +20 -7
- package/pipeline/scripts/cost-budget-check.mjs +2 -1
- package/pipeline/scripts/cost-table.json +1 -1
- package/pipeline/scripts/diff-explain.mjs +7 -3
- package/pipeline/scripts/diff-risk-score.mjs +13 -3
- package/pipeline/scripts/evidence-gate.mjs +7 -2
- package/pipeline/scripts/gen-mode-dispatch.mjs +38 -21
- package/pipeline/scripts/gen-skills-index.mjs +18 -3
- package/pipeline/scripts/learning-curve.mjs +13 -3
- package/pipeline/scripts/learnings-ledger.mjs +103 -36
- package/pipeline/scripts/localize-commands.mjs +6 -1
- package/pipeline/scripts/match-skills.mjs +15 -4
- package/pipeline/scripts/migrate-prefs.mjs +33 -16
- package/pipeline/scripts/phase-tracker.sh +3 -1
- package/pipeline/scripts/repo-map.mjs +110 -64
- package/pipeline/scripts/review-scope.mjs +7 -1
- package/pipeline/scripts/routine-registry.mjs +4 -9
- package/pipeline/scripts/run-aggregator.mjs +11 -5
- package/pipeline/scripts/run-metrics.mjs +13 -8
- package/pipeline/scripts/smoke-cross-cli-behavior.sh +21 -7
- package/pipeline/scripts/test-gap-scan.mjs +44 -12
- package/pipeline/scripts/test-integrity-gate.mjs +5 -1
- package/pipeline/scripts/token-budget-report.mjs +44 -21
- package/pipeline/scripts/triage-memory.mjs +126 -34
- package/pipeline/scripts/uninstall.mjs +74 -30
- package/pipeline/scripts/validate-analysis-doc.mjs +15 -5
- package/pipeline/scripts/validate-diff-risk.mjs +32 -18
- package/pipeline/scripts/validate-test-gap.mjs +17 -7
- package/pipeline/scripts/validate-triage.mjs +17 -5
- package/pipeline/scripts/write-state.mjs +32 -9
- package/pipeline/skills/.skills-index.json +91 -91
- package/pipeline/skills/shared/README.md +57 -57
- package/pipeline/skills/shared/core/apple-archive-compliance/SKILL.md +1 -1
- package/pipeline/skills/shared/core/google-play-compliance/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent/SKILL.md +26 -279
- package/pipeline/skills/shared/core/multi-agent-analysis/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-analysis-resolve/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-autopilot/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-build-optimize/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-create-jira/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-design-check/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-dev/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-dev-autopilot/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-dev-local/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-dev-local-autopilot/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-diff-explain/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-finish/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-forget/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-garbage-collect/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-help/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-issue/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-jira/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-kill/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-language/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-local/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-local-autopilot/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-log/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-manual-test/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-prune-logs/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-purge/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-refactor/SKILL.md +16 -8
- package/pipeline/skills/shared/core/multi-agent-resume/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-review/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-review-issue/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-review-jira/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-routines/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-save/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-scan/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-search/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-setup/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-stack/SKILL.md +3 -3
- package/pipeline/skills/shared/core/multi-agent-status/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-sync/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-test/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-uninstall/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-update/SKILL.md +1 -1
- package/pipeline/skills/shared/external/accessibility-compliance-accessibility-audit/SKILL.md +1 -1
- package/pipeline/skills/shared/external/agent-introspection-debugging/SKILL.md +4 -4
- package/pipeline/skills/shared/external/agentflow/SKILL.md +1 -1
- package/pipeline/skills/shared/external/android-jetpack-compose-expert/SKILL.md +1 -1
- package/pipeline/skills/shared/external/android_ui_verification/SKILL.md +1 -1
- package/pipeline/skills/shared/external/api-patterns/SKILL.md +1 -1
- package/pipeline/skills/shared/external/api-security-best-practices/SKILL.md +1 -1
- package/pipeline/skills/shared/external/app-store-changelog/SKILL.md +1 -1
- package/pipeline/skills/shared/external/backlog/BACKLOG.md +1 -1
- package/pipeline/skills/shared/external/backlog/SKILL.md +12 -12
- package/pipeline/skills/shared/external/ci-cd-pipelines/SKILL.md +1 -1
- package/pipeline/skills/shared/external/context-compression/SKILL.md +1 -1
- package/pipeline/skills/shared/external/council/SKILL.md +3 -3
- package/pipeline/skills/shared/external/css-modern/SKILL.md +1 -1
- package/pipeline/skills/shared/external/database-patterns/SKILL.md +1 -1
- package/pipeline/skills/shared/external/debugging-strategies/SKILL.md +1 -1
- package/pipeline/skills/shared/external/docker-expert/SKILL.md +1 -1
- package/pipeline/skills/shared/external/fastapi-pro/SKILL.md +1 -1
- package/pipeline/skills/shared/external/firebase/SKILL.md +1 -1
- package/pipeline/skills/shared/external/github-actions-templates/SKILL.md +1 -1
- package/pipeline/skills/shared/external/help-skills/SKILL.md +1 -1
- package/pipeline/skills/shared/external/hig-components-content/SKILL.md +1 -1
- package/pipeline/skills/shared/external/hig-components-layout/SKILL.md +1 -1
- package/pipeline/skills/shared/external/hig-components-status/SKILL.md +1 -1
- package/pipeline/skills/shared/external/hig-components-system/SKILL.md +1 -1
- package/pipeline/skills/shared/external/hig-foundations/SKILL.md +1 -1
- package/pipeline/skills/shared/external/hig-inputs/SKILL.md +1 -1
- package/pipeline/skills/shared/external/hig-patterns/SKILL.md +1 -1
- package/pipeline/skills/shared/external/hig-platforms/SKILL.md +1 -1
- package/pipeline/skills/shared/external/hig-technologies/SKILL.md +1 -1
- package/pipeline/skills/shared/external/html-semantic/SKILL.md +1 -1
- package/pipeline/skills/shared/external/humanizer/SKILL.md +1 -1
- package/pipeline/skills/shared/external/ios-debugger-agent/SKILL.md +1 -1
- package/pipeline/skills/shared/external/ios-developer/SKILL.md +1 -1
- package/pipeline/skills/shared/external/kotlin-coroutines-expert/SKILL.md +1 -1
- package/pipeline/skills/shared/external/macos-menubar-tuist-app/SKILL.md +1 -1
- package/pipeline/skills/shared/external/macos-spm-app-packaging/SKILL.md +1 -1
- package/pipeline/skills/shared/external/monorepo-architect/SKILL.md +1 -1
- package/pipeline/skills/shared/external/nextjs-app-router/SKILL.md +1 -1
- package/pipeline/skills/shared/external/nodejs-backend-patterns/SKILL.md +1 -1
- package/pipeline/skills/shared/external/observability-engineer/SKILL.md +1 -1
- package/pipeline/skills/shared/external/python-patterns/SKILL.md +1 -1
- package/pipeline/skills/shared/external/react-best-practices/SKILL.md +1 -1
- package/pipeline/skills/shared/external/rest-api-design/SKILL.md +1 -1
- package/pipeline/skills/shared/external/search-first/SKILL.md +2 -2
- package/pipeline/skills/shared/external/skill-creator/SKILL.md +12 -12
- package/pipeline/skills/shared/external/skill-creator/audit.md +21 -21
- package/pipeline/skills/shared/external/skill-creator/checklist.md +3 -3
- package/pipeline/skills/shared/external/skill-creator/examples.md +10 -10
- package/pipeline/skills/shared/external/skill-creator/label-check.md +17 -17
- package/pipeline/skills/shared/external/skill-creator/scripts/audit-panel.js +86 -50
- package/pipeline/skills/shared/external/skill-creator/template.md +9 -9
- package/pipeline/skills/shared/external/swift-concurrency-expert/SKILL.md +1 -1
- package/pipeline/skills/shared/external/swiftui-performance-audit/SKILL.md +1 -1
- package/pipeline/skills/shared/external/swiftui-ui-patterns/SKILL.md +1 -1
- package/pipeline/skills/shared/external/swiftui-view-refactor/SKILL.md +1 -1
- package/pipeline/skills/shared/external/tailwind-css/SKILL.md +1 -1
- package/pipeline/skills/shared/external/testing-backend/SKILL.md +1 -1
- package/pipeline/skills/shared/external/typescript-patterns/SKILL.md +1 -1
- package/pipeline/skills/shared/external/vue-composition/SKILL.md +1 -1
- package/pipeline/skills/shared/external/web-accessibility/SKILL.md +1 -1
- package/pipeline/skills/shared/external/web-performance/SKILL.md +1 -1
- package/pipeline/skills/shared/external/web-testing/SKILL.md +1 -1
- package/pipeline/skills/shared/external/xcode-build-benchmark/schemas/build-benchmark.schema.json +9 -49
- package/pipeline/skills/skills-index.md +57 -57
- package/pipeline/eval/golden-tasks/01-ios-bugfix-darkmode/expected/phase-1-analysis.json +0 -25
- package/pipeline/eval/golden-tasks/01-ios-bugfix-darkmode/expected/phase-2-plan.json +0 -30
- package/pipeline/eval/golden-tasks/01-ios-bugfix-darkmode/expected/phase-4-review.json +0 -20
- package/pipeline/eval/golden-tasks/01-ios-bugfix-darkmode/expected/phase-4-triage.json +0 -15
- package/pipeline/eval/golden-tasks/01-ios-bugfix-darkmode/metadata.json +0 -14
- package/pipeline/eval/golden-tasks/01-ios-bugfix-darkmode/task.json +0 -12
- package/pipeline/eval/golden-tasks/02-android-feature-compose/expected/phase-1-analysis.json +0 -29
- package/pipeline/eval/golden-tasks/02-android-feature-compose/expected/phase-2-plan.json +0 -43
- package/pipeline/eval/golden-tasks/02-android-feature-compose/expected/phase-4-review.json +0 -35
- package/pipeline/eval/golden-tasks/02-android-feature-compose/expected/phase-4-triage.json +0 -35
- package/pipeline/eval/golden-tasks/02-android-feature-compose/metadata.json +0 -14
- package/pipeline/eval/golden-tasks/02-android-feature-compose/task.json +0 -12
- package/pipeline/eval/golden-tasks/03-backend-python-ratelimit/expected/phase-1-analysis.json +0 -29
- package/pipeline/eval/golden-tasks/03-backend-python-ratelimit/expected/phase-2-plan.json +0 -42
- package/pipeline/eval/golden-tasks/03-backend-python-ratelimit/expected/phase-4-review.json +0 -20
- package/pipeline/eval/golden-tasks/03-backend-python-ratelimit/expected/phase-4-triage.json +0 -15
- package/pipeline/eval/golden-tasks/03-backend-python-ratelimit/metadata.json +0 -14
- package/pipeline/eval/golden-tasks/03-backend-python-ratelimit/task.json +0 -12
- package/pipeline/eval/golden-tasks/04-frontend-next-hydration/expected/phase-1-analysis.json +0 -29
- package/pipeline/eval/golden-tasks/04-frontend-next-hydration/expected/phase-2-plan.json +0 -40
- package/pipeline/eval/golden-tasks/04-frontend-next-hydration/expected/phase-4-review.json +0 -20
- package/pipeline/eval/golden-tasks/04-frontend-next-hydration/expected/phase-4-triage.json +0 -15
- package/pipeline/eval/golden-tasks/04-frontend-next-hydration/metadata.json +0 -14
- package/pipeline/eval/golden-tasks/04-frontend-next-hydration/task.json +0 -12
- package/pipeline/eval/golden-tasks/05-ios-security-keychain/expected/phase-1-analysis.json +0 -29
- package/pipeline/eval/golden-tasks/05-ios-security-keychain/expected/phase-2-plan.json +0 -42
- package/pipeline/eval/golden-tasks/05-ios-security-keychain/expected/phase-4-review.json +0 -28
- package/pipeline/eval/golden-tasks/05-ios-security-keychain/expected/phase-4-triage.json +0 -27
- package/pipeline/eval/golden-tasks/05-ios-security-keychain/metadata.json +0 -14
- package/pipeline/eval/golden-tasks/05-ios-security-keychain/task.json +0 -12
- package/pipeline/eval/golden-tasks/06-android-refactor-usecase/expected/phase-1-analysis.json +0 -29
- package/pipeline/eval/golden-tasks/06-android-refactor-usecase/expected/phase-2-plan.json +0 -41
- package/pipeline/eval/golden-tasks/06-android-refactor-usecase/expected/phase-4-review.json +0 -12
- package/pipeline/eval/golden-tasks/06-android-refactor-usecase/expected/phase-4-triage.json +0 -6
- package/pipeline/eval/golden-tasks/06-android-refactor-usecase/metadata.json +0 -14
- package/pipeline/eval/golden-tasks/06-android-refactor-usecase/task.json +0 -12
- package/pipeline/eval/golden-tasks/07-backend-node-idempotency/expected/phase-1-analysis.json +0 -29
- package/pipeline/eval/golden-tasks/07-backend-node-idempotency/expected/phase-2-plan.json +0 -42
- package/pipeline/eval/golden-tasks/07-backend-node-idempotency/expected/phase-4-review.json +0 -28
- package/pipeline/eval/golden-tasks/07-backend-node-idempotency/expected/phase-4-triage.json +0 -27
- package/pipeline/eval/golden-tasks/07-backend-node-idempotency/metadata.json +0 -14
- package/pipeline/eval/golden-tasks/07-backend-node-idempotency/task.json +0 -12
- package/pipeline/eval/golden-tasks/08-ios-auth-consensus-unverified/expected/phase-1-analysis.json +0 -25
- package/pipeline/eval/golden-tasks/08-ios-auth-consensus-unverified/expected/phase-2-plan.json +0 -31
- package/pipeline/eval/golden-tasks/08-ios-auth-consensus-unverified/expected/phase-4-review.json +0 -12
- package/pipeline/eval/golden-tasks/08-ios-auth-consensus-unverified/expected/phase-4-triage.json +0 -18
- package/pipeline/eval/golden-tasks/08-ios-auth-consensus-unverified/metadata.json +0 -14
- package/pipeline/eval/golden-tasks/08-ios-auth-consensus-unverified/task.json +0 -12
- package/pipeline/eval/golden-tasks/README.md +0 -65
- package/pipeline/eval/intent-cases.json +0 -40
- package/pipeline/eval/run-metrics-fixture.json +0 -46
- package/pipeline/eval/triage/01-empty-findings/expected.json +0 -6
- package/pipeline/eval/triage/01-empty-findings/input.json +0 -5
- package/pipeline/eval/triage/01-empty-findings/notes.md +0 -7
- package/pipeline/eval/triage/02-real-blocker/expected.json +0 -15
- package/pipeline/eval/triage/02-real-blocker/input.json +0 -14
- package/pipeline/eval/triage/02-real-blocker/notes.md +0 -7
- package/pipeline/eval/triage/03-out-of-scope-defer/expected.json +0 -18
- package/pipeline/eval/triage/03-out-of-scope-defer/input.json +0 -14
- package/pipeline/eval/triage/03-out-of-scope-defer/notes.md +0 -10
- package/pipeline/eval/triage/04-false-positive-reject/expected.json +0 -18
- package/pipeline/eval/triage/04-false-positive-reject/input.json +0 -14
- package/pipeline/eval/triage/04-false-positive-reject/notes.md +0 -10
- package/pipeline/eval/triage/05-mixed-classification/expected.json +0 -43
- package/pipeline/eval/triage/05-mixed-classification/input.json +0 -38
- package/pipeline/eval/triage/05-mixed-classification/notes.md +0 -17
- package/pipeline/eval/triage/06-severity-mismatch/expected.json +0 -15
- package/pipeline/eval/triage/06-severity-mismatch/input.json +0 -14
- package/pipeline/eval/triage/06-severity-mismatch/notes.md +0 -9
- package/pipeline/eval/triage/07-duplicate-reviewers/expected.json +0 -27
- package/pipeline/eval/triage/07-duplicate-reviewers/input.json +0 -22
- package/pipeline/eval/triage/07-duplicate-reviewers/notes.md +0 -9
- package/pipeline/eval/triage/08-style-misclassified/expected.json +0 -18
- package/pipeline/eval/triage/08-style-misclassified/input.json +0 -14
- package/pipeline/eval/triage/08-style-misclassified/notes.md +0 -9
- package/pipeline/eval/triage/09-cascading-finding/expected.json +0 -23
- package/pipeline/eval/triage/09-cascading-finding/input.json +0 -22
- package/pipeline/eval/triage/09-cascading-finding/notes.md +0 -9
- package/pipeline/eval/triage/10-deferred-crossref/expected.json +0 -18
- package/pipeline/eval/triage/10-deferred-crossref/input.json +0 -14
- package/pipeline/eval/triage/10-deferred-crossref/notes.md +0 -9
- package/pipeline/eval/triage/11-vercel-token-leak-blocker/expected.json +0 -27
- package/pipeline/eval/triage/11-vercel-token-leak-blocker/input.json +0 -22
- package/pipeline/eval/triage/11-vercel-token-leak-blocker/notes.md +0 -14
- package/pipeline/eval/triage/README.md +0 -54
- package/pipeline/scripts/benchmark-phase-0.sh +0 -128
- package/pipeline/scripts/check-md-links.mjs +0 -84
- package/pipeline/scripts/eval-golden-tasks-live.mjs +0 -297
- package/pipeline/scripts/eval-golden-tasks.mjs +0 -212
- package/pipeline/scripts/eval-intent.mjs +0 -103
- package/pipeline/scripts/eval-mine-corpus.mjs +0 -201
- package/pipeline/scripts/eval-triage.mjs +0 -171
- package/pipeline/scripts/fixtures/diff-risk-android.diff +0 -40
- package/pipeline/scripts/fixtures/diff-risk-ios.diff +0 -48
- package/pipeline/scripts/fixtures/diff-risk-test-removal.diff +0 -40
- package/pipeline/scripts/fixtures/install-layout.tsv +0 -19
- package/pipeline/scripts/fixtures/pack-expected-count.txt +0 -1
- package/pipeline/scripts/fixtures/test-gap-node.diff +0 -30
- package/pipeline/scripts/fixtures/test-gap-python.diff +0 -32
- package/pipeline/scripts/lint-mcp-refs.mjs +0 -207
- package/pipeline/scripts/lint-skills.mjs +0 -143
- package/pipeline/scripts/run-smokes.mjs +0 -76
- package/pipeline/scripts/smoke-add-detail.sh +0 -137
- package/pipeline/scripts/smoke-agent-guard.sh +0 -74
- package/pipeline/scripts/smoke-agent-log-cost.sh +0 -262
- package/pipeline/scripts/smoke-agent-model-routing.sh +0 -87
- package/pipeline/scripts/smoke-ask-choice.sh +0 -42
- package/pipeline/scripts/smoke-autopilot-circuit-breaker.sh +0 -36
- package/pipeline/scripts/smoke-bitbucket-contract.sh +0 -255
- package/pipeline/scripts/smoke-changelog-version.sh +0 -47
- package/pipeline/scripts/smoke-channels-approval-gate.sh +0 -60
- package/pipeline/scripts/smoke-channels-flow.sh +0 -130
- package/pipeline/scripts/smoke-ci-workflows.sh +0 -88
- package/pipeline/scripts/smoke-clarify.sh +0 -148
- package/pipeline/scripts/smoke-command-inventory.sh +0 -81
- package/pipeline/scripts/smoke-commands-skills-parity.sh +0 -87
- package/pipeline/scripts/smoke-community-gates.sh +0 -75
- package/pipeline/scripts/smoke-compliance-skills.sh +0 -119
- package/pipeline/scripts/smoke-config-hygiene.sh +0 -58
- package/pipeline/scripts/smoke-cost-budget.sh +0 -70
- package/pipeline/scripts/smoke-cost-summary.sh +0 -139
- package/pipeline/scripts/smoke-cross-phase-cohesion.sh +0 -128
- package/pipeline/scripts/smoke-description-tr.sh +0 -82
- package/pipeline/scripts/smoke-dev-critic.sh +0 -144
- package/pipeline/scripts/smoke-diff-explain.sh +0 -147
- package/pipeline/scripts/smoke-diff-risk.sh +0 -190
- package/pipeline/scripts/smoke-dynamic-skill-loading.sh +0 -160
- package/pipeline/scripts/smoke-eval-live.sh +0 -136
- package/pipeline/scripts/smoke-evidence-gate.sh +0 -93
- package/pipeline/scripts/smoke-extract-conventions.sh +0 -163
- package/pipeline/scripts/smoke-fetchers-offline.sh +0 -448
- package/pipeline/scripts/smoke-figma-dispatch.sh +0 -112
- package/pipeline/scripts/smoke-gate-hooks.sh +0 -74
- package/pipeline/scripts/smoke-gc-tmp.sh +0 -130
- package/pipeline/scripts/smoke-gc-worktrees.sh +0 -125
- package/pipeline/scripts/smoke-generate-issue.sh +0 -120
- package/pipeline/scripts/smoke-handoff-contract.sh +0 -92
- package/pipeline/scripts/smoke-identity-isolation.sh +0 -70
- package/pipeline/scripts/smoke-install-layout.sh +0 -248
- package/pipeline/scripts/smoke-intent-guard.sh +0 -86
- package/pipeline/scripts/smoke-issue-comment-template.sh +0 -86
- package/pipeline/scripts/smoke-issue-jira-triad.sh +0 -120
- package/pipeline/scripts/smoke-keychain.sh +0 -158
- package/pipeline/scripts/smoke-language-axis.sh +0 -109
- package/pipeline/scripts/smoke-learning-curve.sh +0 -61
- package/pipeline/scripts/smoke-learnings-ledger.sh +0 -86
- package/pipeline/scripts/smoke-lib-scripts.sh +0 -448
- package/pipeline/scripts/smoke-mcp-gate.sh +0 -68
- package/pipeline/scripts/smoke-md-links.sh +0 -8
- package/pipeline/scripts/smoke-md2confluence.sh +0 -126
- package/pipeline/scripts/smoke-metrics-cache-ratio.sh +0 -72
- package/pipeline/scripts/smoke-migrate-state.sh +0 -102
- package/pipeline/scripts/smoke-mode-dispatch-drift.sh +0 -161
- package/pipeline/scripts/smoke-model-fallback.sh +0 -89
- package/pipeline/scripts/smoke-multi-repo-integration.sh +0 -116
- package/pipeline/scripts/smoke-multi-repo-worktree.sh +0 -61
- package/pipeline/scripts/smoke-no-mcp-in-dev-phases.sh +0 -115
- package/pipeline/scripts/smoke-no-token-prompt.sh +0 -85
- package/pipeline/scripts/smoke-pack-contents.sh +0 -140
- package/pipeline/scripts/smoke-pat-audit.sh +0 -128
- package/pipeline/scripts/smoke-per-repo-memory.sh +0 -156
- package/pipeline/scripts/smoke-phase-0-multi-repo.sh +0 -170
- package/pipeline/scripts/smoke-phase-6-multi.sh +0 -79
- package/pipeline/scripts/smoke-phase-banner.sh +0 -101
- package/pipeline/scripts/smoke-phase-tracker.sh +0 -324
- package/pipeline/scripts/smoke-phase0-bridge-contract.sh +0 -241
- package/pipeline/scripts/smoke-phase4-gates.sh +0 -45
- package/pipeline/scripts/smoke-phase4-triage.sh +0 -229
- package/pipeline/scripts/smoke-plan-approval-gate.sh +0 -71
- package/pipeline/scripts/smoke-plan-safety.sh +0 -139
- package/pipeline/scripts/smoke-plan-todos.sh +0 -196
- package/pipeline/scripts/smoke-plugin-validate.sh +0 -64
- package/pipeline/scripts/smoke-pr-review-actions.sh +0 -152
- package/pipeline/scripts/smoke-pre-commit.sh +0 -170
- package/pipeline/scripts/smoke-pref-migration.sh +0 -226
- package/pipeline/scripts/smoke-prefs-language.sh +0 -134
- package/pipeline/scripts/smoke-progress-contract.sh +0 -127
- package/pipeline/scripts/smoke-prune-logs.sh +0 -137
- package/pipeline/scripts/smoke-purge.sh +0 -138
- package/pipeline/scripts/smoke-push-retry.sh +0 -75
- package/pipeline/scripts/smoke-repo-map.sh +0 -300
- package/pipeline/scripts/smoke-review-readiness.sh +0 -92
- package/pipeline/scripts/smoke-review-watch.sh +0 -146
- package/pipeline/scripts/smoke-routines.sh +0 -84
- package/pipeline/scripts/smoke-run-aggregator.sh +0 -216
- package/pipeline/scripts/smoke-run-metrics.sh +0 -50
- package/pipeline/scripts/smoke-search.sh +0 -187
- package/pipeline/scripts/smoke-shadow-git.sh +0 -224
- package/pipeline/scripts/smoke-skill-authoring.sh +0 -137
- package/pipeline/scripts/smoke-skill-language.sh +0 -83
- package/pipeline/scripts/smoke-skill-manifest.sh +0 -138
- package/pipeline/scripts/smoke-skill-scan.sh +0 -198
- package/pipeline/scripts/smoke-source-parity.sh +0 -85
- package/pipeline/scripts/smoke-subagent-validators.sh +0 -108
- package/pipeline/scripts/smoke-sync-parity.sh +0 -92
- package/pipeline/scripts/smoke-tasklist-ordering.sh +0 -112
- package/pipeline/scripts/smoke-telemetry.sh +0 -147
- package/pipeline/scripts/smoke-test-gap.sh +0 -183
- package/pipeline/scripts/smoke-token-budget.sh +0 -67
- package/pipeline/scripts/smoke-token-preflight.sh +0 -82
- package/pipeline/scripts/smoke-tracker-contract.sh +0 -191
- package/pipeline/scripts/smoke-tracker-tokens-invocation.sh +0 -73
- package/pipeline/scripts/smoke-triage-memory.sh +0 -174
- package/pipeline/scripts/smoke-update-check.sh +0 -135
- package/pipeline/scripts/smoke-url-enrichment.sh +0 -70
- package/pipeline/scripts/smoke-validate-analysis-doc.sh +0 -161
- package/pipeline/scripts/smoke-validator-contradiction.sh +0 -67
- package/pipeline/scripts/smoke-validator-gates.sh +0 -164
- package/pipeline/scripts/smoke-vercel-deploy-redact.sh +0 -129
- package/pipeline/scripts/smoke-verify-by-test.sh +0 -148
- package/pipeline/scripts/smoke-wiki-integration.sh +0 -122
- package/pipeline/scripts/smoke-work-summary.sh +0 -163
- package/pipeline/scripts/smoke-workflow-audit.sh +0 -69
- package/pipeline/scripts/smoke-worktree-path-convention.sh +0 -86
- package/pipeline/scripts/smoke-wrapper-preservation.sh +0 -68
- package/pipeline/scripts/smoke-write-state.sh +0 -115
- package/pipeline/scripts/sync-parity-check.sh +0 -135
- package/pipeline/scripts/test-gap-rules/android.json +0 -25
- package/pipeline/scripts/test-gap-rules/ios.json +0 -29
- package/pipeline/scripts/test-gap-rules/node.json +0 -17
- package/pipeline/scripts/test-gap-rules/python.json +0 -19
- package/pipeline/scripts/validate-schemas.mjs +0 -88
|
@@ -1,201 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// eval-mine-corpus.mjs - grow the triage eval set from real production runs.
|
|
3
|
-
//
|
|
4
|
-
// The pipeline already records every Phase 4 triage decision to the per-repo
|
|
5
|
-
// `~/.claude/memory/multi-agent/<repo-slug>/triage-corpus.jsonl` (one line per finding:
|
|
6
|
-
// severity/file/line/issue/fix/reviewer + the classification the run gave it,
|
|
7
|
-
// plus an optional human `reason`/`reviewer`). Hand-authoring eval fixtures is
|
|
8
|
-
// the bottleneck; this closes the loop by turning those real decisions into
|
|
9
|
-
// CANDIDATE regression fixtures in the exact shape `eval-triage.mjs` consumes.
|
|
10
|
-
//
|
|
11
|
-
// It is review-gated on purpose: candidates land in
|
|
12
|
-
// `pipeline/eval/triage-candidates/<task_id>/` (which eval-triage does NOT
|
|
13
|
-
// walk), each validated against the triage schema. A maintainer reviews them,
|
|
14
|
-
// fills scope/diffSummary, and moves the good ones into
|
|
15
|
-
// `pipeline/eval/triage/<NN>-<name>/` to adopt them.
|
|
16
|
-
//
|
|
17
|
-
// Highest-signal cases (a human left a `reason` or `reviewer`) are emitted
|
|
18
|
-
// first and marked, since those encode a deliberate triage judgement.
|
|
19
|
-
//
|
|
20
|
-
// Usage:
|
|
21
|
-
// node pipeline/scripts/eval-mine-corpus.mjs # default corpus path
|
|
22
|
-
// node pipeline/scripts/eval-mine-corpus.mjs --corpus <path>
|
|
23
|
-
// node pipeline/scripts/eval-mine-corpus.mjs --limit 20 # cap candidates
|
|
24
|
-
// node pipeline/scripts/eval-mine-corpus.mjs --dry-run # report, write nothing
|
|
25
|
-
//
|
|
26
|
-
// Exit codes: 0 ok (including "nothing to mine"), 2 usage/setup error.
|
|
27
|
-
|
|
28
|
-
import { readdirSync, readFileSync, writeFileSync, mkdirSync, existsSync } from "node:fs";
|
|
29
|
-
import { dirname, join, resolve } from "node:path";
|
|
30
|
-
import { fileURLToPath } from "node:url";
|
|
31
|
-
import { homedir } from "node:os";
|
|
32
|
-
import { spawnSync } from "node:child_process";
|
|
33
|
-
|
|
34
|
-
const here = dirname(fileURLToPath(import.meta.url));
|
|
35
|
-
const root = resolve(here, "..", "..");
|
|
36
|
-
const evalTriageDir = join(root, "pipeline", "eval", "triage");
|
|
37
|
-
const candidatesDir = join(root, "pipeline", "eval", "triage-candidates");
|
|
38
|
-
const validator = join(root, "pipeline", "scripts", "validate-triage.mjs");
|
|
39
|
-
|
|
40
|
-
const args = process.argv.slice(2);
|
|
41
|
-
const opts = { corpus: null, limit: Infinity, dryRun: false, repoSlug: null };
|
|
42
|
-
for (let i = 0; i < args.length; i++) {
|
|
43
|
-
if (args[i] === "--corpus") opts.corpus = args[++i];
|
|
44
|
-
else if (args[i] === "--repo-slug") opts.repoSlug = args[++i];
|
|
45
|
-
else if (args[i] === "--limit") opts.limit = Number(args[++i]) || Infinity;
|
|
46
|
-
else if (args[i] === "--dry-run") opts.dryRun = true;
|
|
47
|
-
else if (args[i] === "-h" || args[i] === "--help") {
|
|
48
|
-
console.log("Usage: eval-mine-corpus.mjs [--corpus <path>] [--repo-slug <slug>] [--limit N] [--dry-run]");
|
|
49
|
-
process.exit(0);
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
// Resolve the corpus path the SAME way triage-memory.mjs writes it:
|
|
54
|
-
// ~/.claude/memory/multi-agent/<repo-slug>/triage-corpus.jsonl (per repo).
|
|
55
|
-
// (The old default read ~/.claude/logs/.../triage-corpus.jsonl - a flat path
|
|
56
|
-
// nothing writes to - so without --corpus it always found nothing.)
|
|
57
|
-
function resolveRepoSlug() {
|
|
58
|
-
if (opts.repoSlug) return String(opts.repoSlug);
|
|
59
|
-
if (process.env.MULTI_AGENT_REPO_SLUG) return process.env.MULTI_AGENT_REPO_SLUG;
|
|
60
|
-
const r = spawnSync("git", ["rev-parse", "--show-toplevel"], { encoding: "utf-8" });
|
|
61
|
-
if (r.status === 0 && r.stdout.trim()) return r.stdout.trim().split("/").pop();
|
|
62
|
-
return "default";
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
const corpusPath =
|
|
66
|
-
opts.corpus ||
|
|
67
|
-
join(homedir(), ".claude", "memory", "multi-agent", resolveRepoSlug(), "triage-corpus.jsonl");
|
|
68
|
-
|
|
69
|
-
if (!existsSync(corpusPath)) {
|
|
70
|
-
console.log(`eval-mine-corpus: no corpus at ${corpusPath} - nothing to mine.`);
|
|
71
|
-
process.exit(0);
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
// Parse the JSONL corpus, skipping blank/malformed lines (never crash on data).
|
|
75
|
-
const rows = [];
|
|
76
|
-
for (const line of readFileSync(corpusPath, "utf-8").split("\n")) {
|
|
77
|
-
const s = line.trim();
|
|
78
|
-
if (!s) continue;
|
|
79
|
-
try {
|
|
80
|
-
const r = JSON.parse(s);
|
|
81
|
-
if (r && r.task_id && r.issue && r.classification) rows.push(r);
|
|
82
|
-
} catch {
|
|
83
|
-
/* skip malformed line */
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
if (rows.length === 0) {
|
|
88
|
-
console.log("eval-mine-corpus: corpus has no usable rows - nothing to mine.");
|
|
89
|
-
process.exit(0);
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
// task_ids that already have an adopted fixture: skip so we only propose new cases.
|
|
93
|
-
const adopted = new Set();
|
|
94
|
-
if (existsSync(evalTriageDir)) {
|
|
95
|
-
for (const name of readdirSync(evalTriageDir)) {
|
|
96
|
-
const inputPath = join(evalTriageDir, name, "input.json");
|
|
97
|
-
if (existsSync(inputPath)) {
|
|
98
|
-
try {
|
|
99
|
-
const meta = JSON.parse(readFileSync(join(evalTriageDir, name, "meta.json"), "utf-8"));
|
|
100
|
-
if (meta.task_id) adopted.add(meta.task_id);
|
|
101
|
-
} catch {
|
|
102
|
-
/* no meta.json -> can't dedup by task_id, that's fine */
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
// Group corpus rows by task_id.
|
|
109
|
-
const byTask = new Map();
|
|
110
|
-
for (const r of rows) {
|
|
111
|
-
if (!byTask.has(r.task_id)) byTask.set(r.task_id, []);
|
|
112
|
-
byTask.get(r.task_id).push(r);
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
const toFinding = (r) => ({
|
|
116
|
-
severity: r.severity,
|
|
117
|
-
file: r.file,
|
|
118
|
-
// Triage contract requires an integer line >= 0; corpus may carry null for a
|
|
119
|
-
// file-level finding, so coerce to 0 (the schema's file-level sentinel).
|
|
120
|
-
line: Number.isInteger(r.line) ? r.line : 0,
|
|
121
|
-
issue: r.issue,
|
|
122
|
-
fix: r.fix ?? null,
|
|
123
|
-
reviewer: r.reviewer ?? null,
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
// Build a candidate per task. Highest-signal first (has a human reason/reviewer).
|
|
127
|
-
const candidates = [];
|
|
128
|
-
for (const [taskId, findings] of byTask) {
|
|
129
|
-
if (adopted.has(taskId)) continue;
|
|
130
|
-
const rawFindings = findings.map(toFinding);
|
|
131
|
-
const accepted = [];
|
|
132
|
-
const deferred = [];
|
|
133
|
-
const rejected = [];
|
|
134
|
-
for (const r of findings) {
|
|
135
|
-
if (r.classification === "accepted") accepted.push(toFinding(r));
|
|
136
|
-
else if (r.classification === "deferred")
|
|
137
|
-
deferred.push({ finding: toFinding(r), reason: r.reason || "TODO: reason (from corpus)" });
|
|
138
|
-
else if (r.classification === "rejected")
|
|
139
|
-
rejected.push({ finding: toFinding(r), reason: r.reason || "TODO: reason (from corpus)" });
|
|
140
|
-
}
|
|
141
|
-
const hasHumanSignal = findings.some((r) => r.reason || r.reviewer);
|
|
142
|
-
const title = findings.find((f) => f.task_title)?.task_title || taskId;
|
|
143
|
-
candidates.push({
|
|
144
|
-
taskId,
|
|
145
|
-
hasHumanSignal,
|
|
146
|
-
input: {
|
|
147
|
-
rawFindings,
|
|
148
|
-
// Corpus does not record scope/diff; a maintainer fills these before adopting.
|
|
149
|
-
scope: `TODO: scope for ${title}`,
|
|
150
|
-
diffSummary: "TODO: diff summary",
|
|
151
|
-
},
|
|
152
|
-
expected: {
|
|
153
|
-
accepted,
|
|
154
|
-
deferred,
|
|
155
|
-
rejected,
|
|
156
|
-
// Blockers loop back to dev, so a run with an accepted blocker is not approved.
|
|
157
|
-
approved: accepted.every((f) => f.severity !== "blocking"),
|
|
158
|
-
},
|
|
159
|
-
});
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
candidates.sort((a, b) => Number(b.hasHumanSignal) - Number(a.hasHumanSignal));
|
|
163
|
-
const selected = candidates.slice(0, opts.limit);
|
|
164
|
-
|
|
165
|
-
if (selected.length === 0) {
|
|
166
|
-
console.log("eval-mine-corpus: no new task_ids to propose (all already adopted).");
|
|
167
|
-
process.exit(0);
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
let written = 0;
|
|
171
|
-
let invalid = 0;
|
|
172
|
-
for (const c of selected) {
|
|
173
|
-
const safe = c.taskId.replace(/[^A-Za-z0-9._-]/g, "_");
|
|
174
|
-
const dir = join(candidatesDir, safe);
|
|
175
|
-
const tag = c.hasHumanSignal ? " [human-signal]" : "";
|
|
176
|
-
if (opts.dryRun) {
|
|
177
|
-
console.log(` would emit candidate: ${safe} (${c.input.rawFindings.length} findings)${tag}`);
|
|
178
|
-
continue;
|
|
179
|
-
}
|
|
180
|
-
mkdirSync(dir, { recursive: true });
|
|
181
|
-
writeFileSync(join(dir, "input.json"), JSON.stringify(c.input, null, 2) + "\n");
|
|
182
|
-
writeFileSync(join(dir, "expected.json"), JSON.stringify(c.expected, null, 2) + "\n");
|
|
183
|
-
writeFileSync(
|
|
184
|
-
join(dir, "meta.json"),
|
|
185
|
-
JSON.stringify({ task_id: c.taskId, source: "eval-mine-corpus", hasHumanSignal: c.hasHumanSignal }, null, 2) + "\n",
|
|
186
|
-
);
|
|
187
|
-
// Validate the emitted expected.json against the triage contract; flag if bad.
|
|
188
|
-
const res = spawnSync("node", [validator, join(dir, "expected.json")], { encoding: "utf-8" });
|
|
189
|
-
const ok = res.status === 0;
|
|
190
|
-
if (!ok) invalid++;
|
|
191
|
-
console.log(` emitted: triage-candidates/${safe}${tag} ${ok ? "(schema OK)" : "(SCHEMA FAIL - review)"}`);
|
|
192
|
-
written++;
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
if (!opts.dryRun) {
|
|
196
|
-
console.log("");
|
|
197
|
-
console.log(`eval-mine-corpus: ${written} candidate(s) written to pipeline/eval/triage-candidates/`);
|
|
198
|
-
if (invalid > 0) console.log(` ${invalid} failed schema validation - fix before adopting.`);
|
|
199
|
-
console.log(" Review each, fill scope/diffSummary, then move good ones into");
|
|
200
|
-
console.log(" pipeline/eval/triage/<NN>-<name>/ to add them to the eval suite.");
|
|
201
|
-
}
|
|
@@ -1,171 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// eval-triage.mjs - semantic regression check for Phase 4 triage.
|
|
3
|
-
//
|
|
4
|
-
// Walks pipeline/eval/triage/<NN>-<name>/ subdirectories. For each case:
|
|
5
|
-
// 1. Reads input.json (rawFindings + scope + diffSummary)
|
|
6
|
-
// 2. Reads expected.json (the spec-defined triage output)
|
|
7
|
-
// 3. Runs validate-triage.mjs against expected.json - must exit cleanly
|
|
8
|
-
// 4. Asserts every rawFinding in input is accounted for in expected
|
|
9
|
-
// (sum of accepted + deferred[].finding + rejected[].finding == rawFindings)
|
|
10
|
-
// 5. Asserts no inventions: every classified finding traces back to a
|
|
11
|
-
// raw finding by {file, line, issue} match.
|
|
12
|
-
//
|
|
13
|
-
// What this catches:
|
|
14
|
-
// - Spec drift: someone changes phase-4-review.md section3 in a way that contradicts
|
|
15
|
-
// a fixture without updating the fixture.
|
|
16
|
-
// - Schema drift: validate-triage.mjs starts rejecting fixtures that used
|
|
17
|
-
// to pass.
|
|
18
|
-
// - Coverage gap: input has N findings but expected only classifies M < N
|
|
19
|
-
// (a real triage agent doing the same would silently lose findings).
|
|
20
|
-
//
|
|
21
|
-
// What this does NOT catch:
|
|
22
|
-
// - Whether a real triage agent would actually produce the expected output.
|
|
23
|
-
// That requires API keys + nondeterminism. Use a separate eval harness
|
|
24
|
-
// for that. This is a CONTRACT regression test, not a model-quality eval.
|
|
25
|
-
//
|
|
26
|
-
// Usage:
|
|
27
|
-
// node pipeline/scripts/eval-triage.mjs # all cases
|
|
28
|
-
// node pipeline/scripts/eval-triage.mjs --case 03-... # one case
|
|
29
|
-
// node pipeline/scripts/eval-triage.mjs --json # CI-friendly output
|
|
30
|
-
//
|
|
31
|
-
// Exit codes: 0 all pass, 1 one or more fail, 2 usage/setup error.
|
|
32
|
-
|
|
33
|
-
import { readdirSync, readFileSync, statSync } from "node:fs";
|
|
34
|
-
import { dirname, join, resolve } from "node:path";
|
|
35
|
-
import { fileURLToPath } from "node:url";
|
|
36
|
-
import { spawnSync } from "node:child_process";
|
|
37
|
-
|
|
38
|
-
const here = dirname(fileURLToPath(import.meta.url));
|
|
39
|
-
const root = resolve(here, "..", "..");
|
|
40
|
-
const evalDir = join(root, "pipeline", "eval", "triage");
|
|
41
|
-
const validator = join(root, "pipeline", "scripts", "validate-triage.mjs");
|
|
42
|
-
|
|
43
|
-
const args = process.argv.slice(2);
|
|
44
|
-
const opts = { json: false, case: null };
|
|
45
|
-
for (let i = 0; i < args.length; i++) {
|
|
46
|
-
if (args[i] === "--json") opts.json = true;
|
|
47
|
-
else if (args[i] === "--case") opts.case = args[++i];
|
|
48
|
-
else if (args[i] === "-h" || args[i] === "--help") {
|
|
49
|
-
console.log("Usage: eval-triage.mjs [--case <name>] [--json]");
|
|
50
|
-
process.exit(0);
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
function listCases() {
|
|
55
|
-
return readdirSync(evalDir)
|
|
56
|
-
.filter((f) => f !== "README.md" && statSync(join(evalDir, f)).isDirectory())
|
|
57
|
-
.sort();
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// Compare two finding records by stable-key triple.
|
|
61
|
-
function findingKey(f) {
|
|
62
|
-
return `${f.file}::${f.line}::${f.issue}`;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
function runCase(name) {
|
|
66
|
-
const dir = join(evalDir, name);
|
|
67
|
-
const inputPath = join(dir, "input.json");
|
|
68
|
-
const expectedPath = join(dir, "expected.json");
|
|
69
|
-
const errors = [];
|
|
70
|
-
|
|
71
|
-
let input, expected;
|
|
72
|
-
try {
|
|
73
|
-
input = JSON.parse(readFileSync(inputPath, "utf-8"));
|
|
74
|
-
} catch (e) {
|
|
75
|
-
return { name, ok: false, errors: [`input.json: ${e.message}`] };
|
|
76
|
-
}
|
|
77
|
-
try {
|
|
78
|
-
expected = JSON.parse(readFileSync(expectedPath, "utf-8"));
|
|
79
|
-
} catch (e) {
|
|
80
|
-
return { name, ok: false, errors: [`expected.json: ${e.message}`] };
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
// 1. Structural sanity via validate-triage.mjs.
|
|
84
|
-
const result = spawnSync("node", [validator, expectedPath], {
|
|
85
|
-
encoding: "utf-8",
|
|
86
|
-
});
|
|
87
|
-
// Validator exit codes: 0 valid+clean, 1 invalid, 2 over-rejection,
|
|
88
|
-
// 3 contradiction-corrected. For fixtures we expect 0 or 2 (over-rejection
|
|
89
|
-
// is structurally valid but caller must pause).
|
|
90
|
-
if (result.status !== 0 && result.status !== 2) {
|
|
91
|
-
errors.push(
|
|
92
|
-
`validator failed (exit ${result.status}); fixture must be either clean (0) or over-rejection (2). stderr: ${result.stderr.trim()}`,
|
|
93
|
-
);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
// 2. Coverage: every rawFinding in input must appear once in expected.
|
|
97
|
-
const raw = input.rawFindings || [];
|
|
98
|
-
const rawKeys = new Set(raw.map(findingKey));
|
|
99
|
-
const seen = new Set();
|
|
100
|
-
|
|
101
|
-
function consume(f, where) {
|
|
102
|
-
const k = findingKey(f);
|
|
103
|
-
if (!rawKeys.has(k)) {
|
|
104
|
-
errors.push(`${where}: invented finding not in input ({${k}})`);
|
|
105
|
-
return;
|
|
106
|
-
}
|
|
107
|
-
if (seen.has(k)) {
|
|
108
|
-
errors.push(`${where}: duplicate classification of finding ({${k}})`);
|
|
109
|
-
return;
|
|
110
|
-
}
|
|
111
|
-
seen.add(k);
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
(expected.accepted || []).forEach((f) => consume(f, "accepted"));
|
|
115
|
-
(expected.deferred || []).forEach((d) => consume(d.finding, "deferred"));
|
|
116
|
-
(expected.rejected || []).forEach((d) => consume(d.finding, "rejected"));
|
|
117
|
-
|
|
118
|
-
// Anything in raw but not classified?
|
|
119
|
-
for (const r of raw) {
|
|
120
|
-
if (!seen.has(findingKey(r))) {
|
|
121
|
-
errors.push(`unclassified raw finding (lost): ${findingKey(r)}`);
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
// 3. Approved consistency: approved===true iff no accepted blocker.
|
|
126
|
-
const hasBlocker = (expected.accepted || []).some((f) => f.severity === "blocking");
|
|
127
|
-
if (expected.approved === true && hasBlocker) {
|
|
128
|
-
errors.push("approved=true but accepted contains a blocking finding");
|
|
129
|
-
}
|
|
130
|
-
if (expected.approved === false && !hasBlocker) {
|
|
131
|
-
errors.push("approved=false but accepted has no blocking finding");
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
return {
|
|
135
|
-
name,
|
|
136
|
-
ok: errors.length === 0,
|
|
137
|
-
errors,
|
|
138
|
-
rawCount: raw.length,
|
|
139
|
-
accepted: (expected.accepted || []).length,
|
|
140
|
-
deferred: (expected.deferred || []).length,
|
|
141
|
-
rejected: (expected.rejected || []).length,
|
|
142
|
-
approved: expected.approved,
|
|
143
|
-
validatorExit: result.status,
|
|
144
|
-
};
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
const cases = opts.case ? [opts.case] : listCases();
|
|
148
|
-
if (cases.length === 0) {
|
|
149
|
-
console.error("No cases found under pipeline/eval/triage/");
|
|
150
|
-
process.exit(2);
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
const results = cases.map(runCase);
|
|
154
|
-
const failed = results.filter((r) => !r.ok);
|
|
155
|
-
|
|
156
|
-
if (opts.json) {
|
|
157
|
-
console.log(JSON.stringify({ total: results.length, failed: failed.length, results }, null, 2));
|
|
158
|
-
} else {
|
|
159
|
-
for (const r of results) {
|
|
160
|
-
const mark = r.ok ? "✓" : "✗";
|
|
161
|
-
const tally = `raw=${r.rawCount} acc=${r.accepted} def=${r.deferred} rej=${r.rejected} approved=${r.approved} validator=${r.validatorExit}`;
|
|
162
|
-
console.log(`${mark} ${r.name.padEnd(34)} ${tally}`);
|
|
163
|
-
if (!r.ok) {
|
|
164
|
-
for (const e of r.errors) console.log(` ${e}`);
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
console.log("");
|
|
168
|
-
console.log(`══ eval-triage: ${results.length - failed.length}/${results.length} passed ══`);
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
process.exit(failed.length === 0 ? 0 : 1);
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
diff --git a/app/src/main/java/com/example/auth/AuthRepository.kt b/app/src/main/java/com/example/auth/AuthRepository.kt
|
|
2
|
-
index aaa..bbb 100644
|
|
3
|
-
--- a/app/src/main/java/com/example/auth/AuthRepository.kt
|
|
4
|
-
+++ b/app/src/main/java/com/example/auth/AuthRepository.kt
|
|
5
|
-
@@ -10,0 +11,12 @@ class AuthRepository @Inject constructor(
|
|
6
|
-
+ open fun login(username: String, password: String): Result<Token> {
|
|
7
|
-
+ if (username.isBlank() || password.isBlank()) return Result.failure(IllegalArgumentException())
|
|
8
|
-
+ return try {
|
|
9
|
-
+ val response = api.login(username, password)
|
|
10
|
-
+ if (response.isSuccessful) Result.success(response.body()!!)
|
|
11
|
-
+ else Result.failure(HttpException(response))
|
|
12
|
-
+ } catch (e: IOException) {
|
|
13
|
-
+ Result.failure(e)
|
|
14
|
-
+ }
|
|
15
|
-
+ }
|
|
16
|
-
+
|
|
17
|
-
+ open fun logout() { tokenStore.clear() }
|
|
18
|
-
diff --git a/app/src/main/java/com/example/ui/HomeScreen.kt b/app/src/main/java/com/example/ui/HomeScreen.kt
|
|
19
|
-
index ccc..ddd 100644
|
|
20
|
-
--- a/app/src/main/java/com/example/ui/HomeScreen.kt
|
|
21
|
-
+++ b/app/src/main/java/com/example/ui/HomeScreen.kt
|
|
22
|
-
@@ -20,0 +21,4 @@ fun HomeScreen(viewModel: HomeViewModel) {
|
|
23
|
-
+ @Composable fun ItemRow(item: Item) {
|
|
24
|
-
+ Text(item.title, modifier = Modifier.padding(8.dp))
|
|
25
|
-
+ }
|
|
26
|
-
+
|
|
27
|
-
diff --git a/app/src/main/java/com/example/db/migrations/Migration_3_to_4.kt b/app/src/main/java/com/example/db/migrations/Migration_3_to_4.kt
|
|
28
|
-
new file mode 100644
|
|
29
|
-
index 000..eee
|
|
30
|
-
--- /dev/null
|
|
31
|
-
+++ b/app/src/main/java/com/example/db/migrations/Migration_3_to_4.kt
|
|
32
|
-
@@ -0,0 +1,8 @@
|
|
33
|
-
+val MIGRATION_3_TO_4 = object : Migration(3, 4) {
|
|
34
|
-
+ override fun migrate(database: SupportSQLiteDatabase) {
|
|
35
|
-
+ database.execSQL("ALTER TABLE user ADD COLUMN created_at INTEGER NOT NULL DEFAULT 0")
|
|
36
|
-
+ if (database.version < 4) {
|
|
37
|
-
+ database.execSQL("CREATE INDEX idx_user_created ON user(created_at)")
|
|
38
|
-
+ }
|
|
39
|
-
+ }
|
|
40
|
-
+}
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
diff --git a/MyApp/Sources/Auth/KeychainStore.swift b/MyApp/Sources/Auth/KeychainStore.swift
|
|
2
|
-
index 1111111..2222222 100644
|
|
3
|
-
--- a/MyApp/Sources/Auth/KeychainStore.swift
|
|
4
|
-
+++ b/MyApp/Sources/Auth/KeychainStore.swift
|
|
5
|
-
@@ -10,0 +11,18 @@ public final class KeychainStore {
|
|
6
|
-
+ public func save(token: String, account: String) throws {
|
|
7
|
-
+ guard !token.isEmpty else { throw KeychainError.emptyToken }
|
|
8
|
-
+ let query: [String: Any] = [
|
|
9
|
-
+ kSecClass as String: kSecClassGenericPassword,
|
|
10
|
-
+ kSecAttrAccount as String: account,
|
|
11
|
-
+ kSecValueData as String: Data(token.utf8)
|
|
12
|
-
+ ]
|
|
13
|
-
+ let status = SecItemAdd(query as CFDictionary, nil)
|
|
14
|
-
+ if status == errSecDuplicateItem {
|
|
15
|
-
+ let attrs: [String: Any] = [kSecValueData as String: Data(token.utf8)]
|
|
16
|
-
+ let updateStatus = SecItemUpdate(query as CFDictionary, attrs as CFDictionary)
|
|
17
|
-
+ guard updateStatus == errSecSuccess else { throw KeychainError.update(updateStatus) }
|
|
18
|
-
+ } else if status != errSecSuccess {
|
|
19
|
-
+ throw KeychainError.add(status)
|
|
20
|
-
+ }
|
|
21
|
-
+ }
|
|
22
|
-
+
|
|
23
|
-
+ public func load(account: String) throws -> String { /* impl */ }
|
|
24
|
-
diff --git a/MyApp/Sources/Views/SettingsView.swift b/MyApp/Sources/Views/SettingsView.swift
|
|
25
|
-
index 3333333..4444444 100644
|
|
26
|
-
--- a/MyApp/Sources/Views/SettingsView.swift
|
|
27
|
-
+++ b/MyApp/Sources/Views/SettingsView.swift
|
|
28
|
-
@@ -42,0 +43,3 @@ struct SettingsView: View {
|
|
29
|
-
+ Text("Hello")
|
|
30
|
-
+ .padding()
|
|
31
|
-
+ .foregroundColor(.secondary)
|
|
32
|
-
diff --git a/MyApp/Sources/Networking/ApiClient.swift b/MyApp/Sources/Networking/ApiClient.swift
|
|
33
|
-
index 5555555..6666666 100644
|
|
34
|
-
--- a/MyApp/Sources/Networking/ApiClient.swift
|
|
35
|
-
+++ b/MyApp/Sources/Networking/ApiClient.swift
|
|
36
|
-
@@ -22,0 +23,5 @@ public class ApiClient {
|
|
37
|
-
+ public func fetch<T: Decodable>(_ url: URL) async throws -> T {
|
|
38
|
-
+ let (data, _) = try await URLSession.shared.data(from: url)
|
|
39
|
-
+ if data.isEmpty { throw ApiError.empty }
|
|
40
|
-
+ return try JSONDecoder().decode(T.self, from: data)
|
|
41
|
-
+ }
|
|
42
|
-
diff --git a/MyAppTests/SettingsViewTests.swift b/MyAppTests/SettingsViewTests.swift
|
|
43
|
-
index 7777777..8888888 100644
|
|
44
|
-
--- a/MyAppTests/SettingsViewTests.swift
|
|
45
|
-
+++ b/MyAppTests/SettingsViewTests.swift
|
|
46
|
-
@@ -10,0 +11,2 @@ final class SettingsViewTests: XCTestCase {
|
|
47
|
-
+ func testRenders() throws { /* impl */ }
|
|
48
|
-
+
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
diff --git a/MyAppTests/LoginViewModelTests.swift b/MyAppTests/LoginViewModelTests.swift
|
|
2
|
-
index 1111111..2222222 100644
|
|
3
|
-
--- a/MyAppTests/LoginViewModelTests.swift
|
|
4
|
-
+++ b/MyAppTests/LoginViewModelTests.swift
|
|
5
|
-
@@ -10,30 +10,20 @@ final class LoginViewModelTests: XCTestCase {
|
|
6
|
-
func testLoginWithValidCredentials_Succeeds() {
|
|
7
|
-
let sut = LoginViewModel(service: MockAuthService())
|
|
8
|
-
+ sut.retryPolicy = .none
|
|
9
|
-
sut.login(email: "user@example.com", password: "correct")
|
|
10
|
-
+ XCTAssertTrue(sut.isAuthenticated)
|
|
11
|
-
}
|
|
12
|
-
-
|
|
13
|
-
- func testLoginWithInvalidEmail_ShowsError() {
|
|
14
|
-
- let sut = LoginViewModel(service: MockAuthService())
|
|
15
|
-
- sut.login(email: "not-an-email", password: "irrelevant")
|
|
16
|
-
- XCTAssertEqual(sut.errorMessage, "Invalid email")
|
|
17
|
-
- }
|
|
18
|
-
-
|
|
19
|
-
- func testLoginWithExpiredToken_Rejects() {
|
|
20
|
-
- let sut = LoginViewModel(service: MockAuthService(tokenState: .expired))
|
|
21
|
-
- sut.login(email: "user@example.com", password: "correct")
|
|
22
|
-
- XCTAssertFalse(sut.isAuthenticated)
|
|
23
|
-
- }
|
|
24
|
-
-
|
|
25
|
-
- func testLogout_ClearsSession() {
|
|
26
|
-
- let sut = LoginViewModel(service: MockAuthService())
|
|
27
|
-
- sut.logout()
|
|
28
|
-
- XCTAssertNil(sut.session)
|
|
29
|
-
- }
|
|
30
|
-
}
|
|
31
|
-
diff --git a/MyApp/Sources/Auth/LoginViewModel.swift b/MyApp/Sources/Auth/LoginViewModel.swift
|
|
32
|
-
index 3333333..4444444 100644
|
|
33
|
-
--- a/MyApp/Sources/Auth/LoginViewModel.swift
|
|
34
|
-
+++ b/MyApp/Sources/Auth/LoginViewModel.swift
|
|
35
|
-
@@ -20,6 +20,8 @@ final class LoginViewModel {
|
|
36
|
-
func login(email: String, password: String) {
|
|
37
|
-
+ guard email.contains("@") else { return }
|
|
38
|
-
+ service.authenticate(email: email, password: password)
|
|
39
|
-
}
|
|
40
|
-
}
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
.claude/.pipeline-version 1
|
|
2
|
-
.claude/CLAUDE.md 1
|
|
3
|
-
.claude/agents 8
|
|
4
|
-
.claude/commands 48
|
|
5
|
-
.claude/lib 27
|
|
6
|
-
.claude/multi-agent-preferences.json 1
|
|
7
|
-
.claude/multi-agent-refs 52
|
|
8
|
-
.claude/rules 12
|
|
9
|
-
.claude/schemas 25
|
|
10
|
-
.claude/scripts 201
|
|
11
|
-
.claude/settings.json 1
|
|
12
|
-
.claude/skills 428
|
|
13
|
-
.copilot/.pipeline-version 1
|
|
14
|
-
.copilot/agents 8
|
|
15
|
-
.copilot/copilot-instructions.md 1
|
|
16
|
-
.copilot/lib 27
|
|
17
|
-
.copilot/schemas 25
|
|
18
|
-
.copilot/scripts 201
|
|
19
|
-
.copilot/skills 471
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
943
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
diff --git a/src/lib/parser.ts b/src/lib/parser.ts
|
|
2
|
-
index aaaa..bbbb 100644
|
|
3
|
-
--- a/src/lib/parser.ts
|
|
4
|
-
+++ b/src/lib/parser.ts
|
|
5
|
-
@@ -10,0 +11,8 @@
|
|
6
|
-
+export function tokenize(input: string): string[] {
|
|
7
|
-
+ return input.split(/\s+/);
|
|
8
|
-
+}
|
|
9
|
-
+
|
|
10
|
-
+export class Parser {
|
|
11
|
-
+ parse(tokens: string[]): unknown { return tokens; }
|
|
12
|
-
+}
|
|
13
|
-
+
|
|
14
|
-
diff --git a/src/lib/format.ts b/src/lib/format.ts
|
|
15
|
-
index cccc..dddd 100644
|
|
16
|
-
--- a/src/lib/format.ts
|
|
17
|
-
+++ b/src/lib/format.ts
|
|
18
|
-
@@ -5,0 +6,2 @@
|
|
19
|
-
+export const formatNumber = (n: number): string => n.toFixed(2);
|
|
20
|
-
+
|
|
21
|
-
diff --git a/src/lib/__tests__/parser.test.ts b/src/lib/__tests__/parser.test.ts
|
|
22
|
-
new file mode 100644
|
|
23
|
-
index 000..eeee
|
|
24
|
-
--- /dev/null
|
|
25
|
-
+++ b/src/lib/__tests__/parser.test.ts
|
|
26
|
-
@@ -0,0 +1,4 @@
|
|
27
|
-
+import { tokenize } from "../parser";
|
|
28
|
-
+test("tokenize splits whitespace", () => {
|
|
29
|
-
+ expect(tokenize("a b c")).toEqual(["a","b","c"]);
|
|
30
|
-
+});
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
diff --git a/src/payments/processor.py b/src/payments/processor.py
|
|
2
|
-
index 1111..2222 100644
|
|
3
|
-
--- a/src/payments/processor.py
|
|
4
|
-
+++ b/src/payments/processor.py
|
|
5
|
-
@@ -10,0 +11,8 @@ class Processor:
|
|
6
|
-
+ def charge(self, amount, currency):
|
|
7
|
-
+ if amount <= 0:
|
|
8
|
-
+ raise ValueError("amount must be positive")
|
|
9
|
-
+ return self._gateway.charge(amount, currency)
|
|
10
|
-
+
|
|
11
|
-
+async def refund(transaction_id):
|
|
12
|
-
+ return await api.refund(transaction_id)
|
|
13
|
-
+
|
|
14
|
-
diff --git a/src/payments/utils.py b/src/payments/utils.py
|
|
15
|
-
index 3333..4444 100644
|
|
16
|
-
--- a/src/payments/utils.py
|
|
17
|
-
+++ b/src/payments/utils.py
|
|
18
|
-
@@ -5,0 +6,3 @@
|
|
19
|
-
+def normalize_currency(code):
|
|
20
|
-
+ return code.upper().strip()
|
|
21
|
-
+
|
|
22
|
-
diff --git a/tests/test_processor.py b/tests/test_processor.py
|
|
23
|
-
new file mode 100644
|
|
24
|
-
index 000..5555
|
|
25
|
-
--- /dev/null
|
|
26
|
-
+++ b/tests/test_processor.py
|
|
27
|
-
@@ -0,0 +1,5 @@
|
|
28
|
-
+import pytest
|
|
29
|
-
+
|
|
30
|
-
+def test_charge_positive_amount():
|
|
31
|
-
+ p = Processor()
|
|
32
|
-
+ assert p.charge(10, "USD") is not None
|