pi-dev-team 0.2.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/LICENSE +21 -0
- package/PORTING.md +134 -0
- package/README.md +207 -0
- package/UPSTREAM.json +64 -0
- package/agents/Explore.md +15 -0
- package/agents/a11y-review.md +118 -0
- package/agents/adr-author.md +70 -0
- package/agents/ai-provenance-review.md +120 -0
- package/agents/angular-reactivity-review.md +95 -0
- package/agents/arch-review.md +135 -0
- package/agents/architect.md +78 -0
- package/agents/autoship-batch-proposer.md +69 -0
- package/agents/claude-setup-review.md +136 -0
- package/agents/codebase-recon.md +184 -0
- package/agents/component-architecture-review.md +119 -0
- package/agents/concurrency-review.md +109 -0
- package/agents/correctness-review.md +290 -0
- package/agents/data-flow-tracer.md +120 -0
- package/agents/doc-review.md +165 -0
- package/agents/domain-review.md +136 -0
- package/agents/general-purpose.md +10 -0
- package/agents/gherkin-quality-critic.md +113 -0
- package/agents/js-fp-review.md +114 -0
- package/agents/mutation-kill.md +684 -0
- package/agents/naming-review.md +142 -0
- package/agents/orchestrator.md +339 -0
- package/agents/performance-review.md +105 -0
- package/agents/plan-review-acceptance.md +115 -0
- package/agents/plan-review-design.md +90 -0
- package/agents/plan-review-parallelization.md +84 -0
- package/agents/plan-review-strategic.md +96 -0
- package/agents/plan-review-ux.md +110 -0
- package/agents/platform-engineer.md +64 -0
- package/agents/product-manager.md +68 -0
- package/agents/progress-guardian.md +79 -0
- package/agents/qa-engineer.md +289 -0
- package/agents/quality-reviewer.md +132 -0
- package/agents/react-reactivity-review.md +102 -0
- package/agents/refactor-opportunity-review.md +128 -0
- package/agents/security-engineer.md +60 -0
- package/agents/security-review.md +218 -0
- package/agents/session-analysis.md +95 -0
- package/agents/software-engineer.md +105 -0
- package/agents/spec-compliance-review.md +100 -0
- package/agents/spec-reviewer.md +114 -0
- package/agents/structure-review.md +146 -0
- package/agents/tech-writer.md +84 -0
- package/agents/test-review.md +246 -0
- package/agents/test-smell-review.md +188 -0
- package/agents/token-efficiency-review.md +139 -0
- package/agents/ui-ux-designer.md +54 -0
- package/agents/vue-reactivity-review.md +95 -0
- package/bin/__pycache__/claudecpython-314.pyc +0 -0
- package/bin/claude +258 -0
- package/docs/upstream/.pages +1 -0
- package/docs/upstream/CHANGELOG.md +2586 -0
- package/docs/upstream/README.md +155 -0
- package/docs/upstream/agent-architecture.md +214 -0
- package/docs/upstream/agent_info.md +187 -0
- package/docs/upstream/artifact-migration.md +124 -0
- package/docs/upstream/code-intelligence-nudge.md +149 -0
- package/docs/upstream/code-review-process.md +294 -0
- package/docs/upstream/concurrent-use.md +73 -0
- package/docs/upstream/context-management.md +111 -0
- package/docs/upstream/developer-notes.md +280 -0
- package/docs/upstream/diagrams/architecture-overview.svg +101 -0
- package/docs/upstream/diagrams/review-dispatch.svg +139 -0
- package/docs/upstream/diagrams/team-agents.svg +128 -0
- package/docs/upstream/diagrams/test-improve-flow.svg +166 -0
- package/docs/upstream/diagrams/workflow-linear.svg +66 -0
- package/docs/upstream/diagrams/workflow-three-phase.svg +200 -0
- package/docs/upstream/eval-maintenance.md +95 -0
- package/docs/upstream/eval-running-guide.md +147 -0
- package/docs/upstream/eval-system.md +291 -0
- package/docs/upstream/session-review-oss-complements.md +75 -0
- package/docs/upstream/session-review.md +212 -0
- package/docs/upstream/skills.md +188 -0
- package/docs/upstream/team-structure.md +21 -0
- package/docs/upstream/telemetry-ci-access.md +129 -0
- package/docs/upstream/telemetry-repo-security.md +120 -0
- package/docs/upstream/test-evaluation.md +277 -0
- package/docs/upstream/test-improve.md +154 -0
- package/docs/upstream/triage-workflow.md +282 -0
- package/docs/upstream/workflows.md +289 -0
- package/extensions/dev-team/index.ts +539 -0
- package/extensions/dev-team/lib/agents.ts +272 -0
- package/extensions/dev-team/lib/ai-credits.ts +92 -0
- package/extensions/dev-team/lib/autocompact.ts +81 -0
- package/extensions/dev-team/lib/child-run.ts +102 -0
- package/extensions/dev-team/lib/config.ts +236 -0
- package/extensions/dev-team/lib/gh-command.ts +103 -0
- package/extensions/dev-team/lib/github-style.ts +307 -0
- package/extensions/dev-team/lib/hooks.ts +350 -0
- package/extensions/dev-team/lib/metrics.ts +115 -0
- package/extensions/dev-team/lib/safe-read.ts +49 -0
- package/extensions/dev-team/lib/session-files.ts +57 -0
- package/extensions/dev-team/lib/session-spend.ts +123 -0
- package/extensions/dev-team/lib/shell-scan.ts +205 -0
- package/extensions/dev-team/lib/skills.ts +213 -0
- package/extensions/dev-team/lib/subagent-render.ts +245 -0
- package/extensions/dev-team/lib/subagent-types.ts +164 -0
- package/extensions/dev-team/lib/subagent.ts +596 -0
- package/extensions/dev-team/lib/terminal-text.ts +54 -0
- package/extensions/dev-team/lib/tools-misc.ts +152 -0
- package/extensions/dev-team/lib/transcript.ts +110 -0
- package/extensions/dev-team/lib/trust.ts +52 -0
- package/extensions/dev-team/lib/usage-breakdown.ts +176 -0
- package/extensions/dev-team/lib/usage-chart.ts +153 -0
- package/extensions/dev-team/lib/usage-command.ts +107 -0
- package/extensions/dev-team/lib/usage-history.ts +203 -0
- package/extensions/dev-team/lib/usage-render.ts +225 -0
- package/extensions/dev-team/lib/usage-split-bar.ts +127 -0
- package/extensions/dev-team/lib/usage-state.ts +116 -0
- package/extensions/dev-team/lib/usage-text.ts +159 -0
- package/extensions/dev-team/lib/usage-view.ts +109 -0
- package/hooks/__pycache__/refactor_test_freeze_guard.cpython-314.pyc +0 -0
- package/hooks/agent_dispatch_ledger.py +190 -0
- package/hooks/autocompact_setup_nudge.py +99 -0
- package/hooks/bash_retry_guard.py +228 -0
- package/hooks/boundary_events_write_guard.py +352 -0
- package/hooks/code_intelligence_nudge.py +293 -0
- package/hooks/code_intelligence_turn_mark.py +317 -0
- package/hooks/codegraph_bootstrap.py +139 -0
- package/hooks/contract_version_guard.py +362 -0
- package/hooks/cost_meter.py +106 -0
- package/hooks/destructive-commands.json +62 -0
- package/hooks/destructive_guard.py +477 -0
- package/hooks/eval_compliance_check.py +440 -0
- package/hooks/guards.json +17 -0
- package/hooks/hooks.json +323 -0
- package/hooks/internal_double_gate.py +296 -0
- package/hooks/js_fp_review.py +212 -0
- package/hooks/knowledge_index.py +119 -0
- package/hooks/lib/__pycache__/artifact_paths.cpython-314.pyc +0 -0
- package/hooks/lib/__pycache__/atomic_state.cpython-314.pyc +0 -0
- package/hooks/lib/__pycache__/autocompact_config.cpython-314.pyc +0 -0
- package/hooks/lib/__pycache__/boundary_events.cpython-314.pyc +0 -0
- package/hooks/lib/__pycache__/doc_classification.cpython-314.pyc +0 -0
- package/hooks/lib/__pycache__/gh_pr_create_detect.cpython-314.pyc +0 -0
- package/hooks/lib/__pycache__/git_safe_diff.cpython-314.pyc +0 -0
- package/hooks/lib/__pycache__/instrument_log.cpython-314.pyc +0 -0
- package/hooks/lib/__pycache__/metrics_query.cpython-314.pyc +0 -0
- package/hooks/lib/__pycache__/plugin_version.cpython-314.pyc +0 -0
- package/hooks/lib/__pycache__/pre_commit_doc_classifier.cpython-314.pyc +0 -0
- package/hooks/lib/__pycache__/review_agent_registry.cpython-314.pyc +0 -0
- package/hooks/lib/__pycache__/review_gate_corroboration.cpython-314.pyc +0 -0
- package/hooks/lib/__pycache__/review_gate_hash.cpython-314.pyc +0 -0
- package/hooks/lib/__pycache__/review_verdicts.cpython-314.pyc +0 -0
- package/hooks/lib/__pycache__/stdin_json.cpython-314.pyc +0 -0
- package/hooks/lib/__pycache__/stryker_invocation.cpython-314.pyc +0 -0
- package/hooks/lib/__pycache__/telemetry_consent.cpython-314.pyc +0 -0
- package/hooks/lib/__pycache__/test_file_classify.cpython-314.pyc +0 -0
- package/hooks/lib/__pycache__/token_efficiency_limits.cpython-314.pyc +0 -0
- package/hooks/lib/__pycache__/verify_guard_state.cpython-314.pyc +0 -0
- package/hooks/lib/__pycache__/xunit_v3_operator_gate.cpython-314.pyc +0 -0
- package/hooks/lib/agent_skill_hints.py +74 -0
- package/hooks/lib/artifact_paths.py +263 -0
- package/hooks/lib/atomic_state.py +557 -0
- package/hooks/lib/autocompact_config.py +103 -0
- package/hooks/lib/autoship_log.py +106 -0
- package/hooks/lib/banned_scripts_policy.py +51 -0
- package/hooks/lib/boundary_events.py +436 -0
- package/hooks/lib/build_knowledge_index.py +504 -0
- package/hooks/lib/build_skills_index.py +361 -0
- package/hooks/lib/build_state.py +116 -0
- package/hooks/lib/classify_ship_outcome.py +126 -0
- package/hooks/lib/config_changelog_schema.py +115 -0
- package/hooks/lib/cost_meter.py +955 -0
- package/hooks/lib/doc_classification.py +116 -0
- package/hooks/lib/gh_pr_create_detect.py +136 -0
- package/hooks/lib/git_safe_diff.py +123 -0
- package/hooks/lib/instrument_log.py +66 -0
- package/hooks/lib/iteration_journal_gate.py +197 -0
- package/hooks/lib/knowledge_index_paths.py +88 -0
- package/hooks/lib/mcp_json_repowise.py +177 -0
- package/hooks/lib/metrics_query.py +202 -0
- package/hooks/lib/minimal_yaml.py +434 -0
- package/hooks/lib/plugin_version.py +142 -0
- package/hooks/lib/pre_commit_detect.py +537 -0
- package/hooks/lib/pre_commit_doc_classifier.py +126 -0
- package/hooks/lib/pricing.py +118 -0
- package/hooks/lib/report_pdf.py +371 -0
- package/hooks/lib/review_agent_registry.py +142 -0
- package/hooks/lib/review_dispatch_ledger.py +101 -0
- package/hooks/lib/review_gate_corroboration.py +521 -0
- package/hooks/lib/review_gate_hash.py +252 -0
- package/hooks/lib/review_gate_normalized_hash.py +1115 -0
- package/hooks/lib/review_verdicts.py +301 -0
- package/hooks/lib/run_report.py +160 -0
- package/hooks/lib/skill_categories.yaml +125 -0
- package/hooks/lib/stdin_json.py +57 -0
- package/hooks/lib/stryker_invocation.py +102 -0
- package/hooks/lib/telemetry_consent.py +41 -0
- package/hooks/lib/telemetry_report.py +108 -0
- package/hooks/lib/test_file_classify.py +160 -0
- package/hooks/lib/token_efficiency_limits.py +51 -0
- package/hooks/lib/turn_identity.py +77 -0
- package/hooks/lib/verify_guard_state.py +110 -0
- package/hooks/lib/workflow_state.py +206 -0
- package/hooks/lib/xunit_v3_operator_gate.py +596 -0
- package/hooks/mcp_json_repowise_nudge.py +74 -0
- package/hooks/mutation_adapters/__init__.py +7 -0
- package/hooks/mutation_adapters/__pycache__/__init__.cpython-314.pyc +0 -0
- package/hooks/mutation_adapters/__pycache__/lib.cpython-314.pyc +0 -0
- package/hooks/mutation_adapters/__pycache__/mutmut.cpython-314.pyc +0 -0
- package/hooks/mutation_adapters/__pycache__/pitest.cpython-314.pyc +0 -0
- package/hooks/mutation_adapters/__pycache__/stryker.cpython-314.pyc +0 -0
- package/hooks/mutation_adapters/__pycache__/stryker_net.cpython-314.pyc +0 -0
- package/hooks/mutation_adapters/lib.py +478 -0
- package/hooks/mutation_adapters/mutmut.py +188 -0
- package/hooks/mutation_adapters/pitest.py +266 -0
- package/hooks/mutation_adapters/stryker.py +157 -0
- package/hooks/mutation_adapters/stryker_net.py +264 -0
- package/hooks/mutation_gate.py +193 -0
- package/hooks/mutation_testing_smoke_gate.py +371 -0
- package/hooks/pending_review_notify.py +121 -0
- package/hooks/phase_marker.py +138 -0
- package/hooks/post_compact_state_reinject.py +180 -0
- package/hooks/post_format.py +115 -0
- package/hooks/pre_commit_knowledge_index.py +128 -0
- package/hooks/pre_commit_review.py +66 -0
- package/hooks/pre_pr_review.py +694 -0
- package/hooks/pre_tool_guard.py +405 -0
- package/hooks/py.sh +73 -0
- package/hooks/refactor-bash-write-patterns.json +29 -0
- package/hooks/refactor_test_bash_guard.py +253 -0
- package/hooks/refactor_test_freeze_guard.py +139 -0
- package/hooks/refactor_test_revert_guard.py +186 -0
- package/hooks/repo_review_nudge.py +287 -0
- package/hooks/review_verdict_recorder.py +464 -0
- package/hooks/scan_bash_command_for_banned_scripts.py +428 -0
- package/hooks/scan_worktree_for_banned_scripts.py +238 -0
- package/hooks/session_learning_trigger.py +248 -0
- package/hooks/skills_index.py +126 -0
- package/hooks/stryker_xunit_shim_guard.py +571 -0
- package/hooks/subagent_completion_guard.py +309 -0
- package/hooks/subagent_skill_context.py +139 -0
- package/hooks/task_completion_metrics.py +216 -0
- package/hooks/tdd_guard.py +229 -0
- package/hooks/telemetry.py +341 -0
- package/hooks/token_efficiency_review.py +194 -0
- package/hooks/verify_guard.py +183 -0
- package/hooks/verify_guard_edit_marker.py +73 -0
- package/hooks/version_check.py +173 -0
- package/knowledge/accepted-risks-schema.md +98 -0
- package/knowledge/adr-decision-criteria.md +64 -0
- package/knowledge/adversarial-review-protocol.md +139 -0
- package/knowledge/agent-registry.md +228 -0
- package/knowledge/agent-review-methodology.md +80 -0
- package/knowledge/ai-friendly-repo-guidelines.md +67 -0
- package/knowledge/architecture-assessment.md +96 -0
- package/knowledge/artifact-lifecycle.md +57 -0
- package/knowledge/cd-maturity-model.md +82 -0
- package/knowledge/cd-test-architecture.md +190 -0
- package/knowledge/ci-cd-file-scope.md +24 -0
- package/knowledge/codegraph-vs-graphify.md +192 -0
- package/knowledge/component-test-patterns.md +139 -0
- package/knowledge/database-change-management.md +80 -0
- package/knowledge/database-test-patterns.md +79 -0
- package/knowledge/decision-defaults.md +88 -0
- package/knowledge/dependency-breaking-techniques.md +116 -0
- package/knowledge/deployment-pipeline.md +86 -0
- package/knowledge/design-smells.md +122 -0
- package/knowledge/directory-enumeration.md +38 -0
- package/knowledge/domain-modeling.md +123 -0
- package/knowledge/evidence-bundle.md +90 -0
- package/knowledge/exploratory-testing-field-guide.md +122 -0
- package/knowledge/failure-routing.md +28 -0
- package/knowledge/fixture-construction.md +56 -0
- package/knowledge/frontend-component-architecture.md +139 -0
- package/knowledge/gherkin-quality-review-dispatch.md +135 -0
- package/knowledge/index.json +6766 -0
- package/knowledge/internal-collaborator-doubling.md +101 -0
- package/knowledge/legacy-test-strategy.md +71 -0
- package/knowledge/long-run-waiting.md +66 -0
- package/knowledge/microservice-testing.md +71 -0
- package/knowledge/model-pricing.json +23 -0
- package/knowledge/mutation-score-formulas.md +60 -0
- package/knowledge/object-calisthenics.md +147 -0
- package/knowledge/oracle-provenance.md +94 -0
- package/knowledge/orchestrator-script-implementation.md +185 -0
- package/knowledge/owasp-detection.md +148 -0
- package/knowledge/plan-review-rubric.md +56 -0
- package/knowledge/proxy-connectivity.md +62 -0
- package/knowledge/reactive-effect-patterns.md +73 -0
- package/knowledge/recon-inventory-excludes.txt +32 -0
- package/knowledge/references/bdd-value-guide.md +61 -0
- package/knowledge/references/csharp-http-client-testing.md +264 -0
- package/knowledge/release-strategies.md +74 -0
- package/knowledge/report-output-location.md +117 -0
- package/knowledge/report-pdf-integration.md +63 -0
- package/knowledge/report-print.css +129 -0
- package/knowledge/report-template.md +114 -0
- package/knowledge/report-to-pdf.md +69 -0
- package/knowledge/request-processing-flow.md +63 -0
- package/knowledge/result-verification.md +52 -0
- package/knowledge/review-agent-output-contract.md +121 -0
- package/knowledge/review-lens-classification.md +113 -0
- package/knowledge/review-rubric.md +62 -0
- package/knowledge/review-template.md +104 -0
- package/knowledge/rule-fixtures/A02.insecure-random-js/negative.js +1 -0
- package/knowledge/rule-fixtures/A02.insecure-random-js/positive.js +1 -0
- package/knowledge/rule-fixtures/A02.weak-hashing-md5/negative.py +1 -0
- package/knowledge/rule-fixtures/A02.weak-hashing-md5/positive.py +1 -0
- package/knowledge/rule-fixtures/A03.command-injection/negative.js +1 -0
- package/knowledge/rule-fixtures/A03.command-injection/positive.js +1 -0
- package/knowledge/rule-fixtures/A03.sql-injection/negative.js +1 -0
- package/knowledge/rule-fixtures/A03.sql-injection/positive.js +1 -0
- package/knowledge/rule-fixtures/A03.xss-innerhtml/negative.js +1 -0
- package/knowledge/rule-fixtures/A03.xss-innerhtml/positive.js +1 -0
- package/knowledge/rule-fixtures/A05.cors-wildcard/negative.js +1 -0
- package/knowledge/rule-fixtures/A05.cors-wildcard/positive.js +1 -0
- package/knowledge/rule-fixtures/A05.default-credentials/negative.js +1 -0
- package/knowledge/rule-fixtures/A05.default-credentials/positive.js +1 -0
- package/knowledge/rule-fixtures/A07.jwt-alg-none/negative.js +1 -0
- package/knowledge/rule-fixtures/A07.jwt-alg-none/positive.js +1 -0
- package/knowledge/rule-fixtures/A08.binary-formatter/negative.cs +1 -0
- package/knowledge/rule-fixtures/A08.binary-formatter/positive.cs +1 -0
- package/knowledge/rule-fixtures/A08.js-eval/negative.js +1 -0
- package/knowledge/rule-fixtures/A08.js-eval/positive.js +1 -0
- package/knowledge/rule-fixtures/A08.object-input-stream/negative.java +1 -0
- package/knowledge/rule-fixtures/A08.object-input-stream/positive.java +1 -0
- package/knowledge/schemas/disposition-register-v1.json +65 -0
- package/knowledge/schemas/recon-envelope-v1.json +198 -0
- package/knowledge/schemas/unified-finding-v1.json +72 -0
- package/knowledge/security-primitives-contract.md +301 -0
- package/knowledge/security-review-rule-map.yaml +107 -0
- package/knowledge/skills-registry.md +72 -0
- package/knowledge/task-size-classifier.md +103 -0
- package/knowledge/telemetry-schema.md +881 -0
- package/knowledge/test-automation-maturity.md +56 -0
- package/knowledge/test-automation-principles.md +71 -0
- package/knowledge/test-cadence-tradeoffs.md +68 -0
- package/knowledge/test-doubles.md +105 -0
- package/knowledge/test-file-indicators.md +22 -0
- package/knowledge/test-layer-gates.md +35 -0
- package/knowledge/test-matrix-examples/django-batch.md +24 -0
- package/knowledge/test-matrix-examples/dotnet-grpc-fronting-api.md +90 -0
- package/knowledge/test-matrix-examples/dotnet-http-consumer.md +131 -0
- package/knowledge/test-matrix-examples/react-node-spa.md +24 -0
- package/knowledge/test-matrix-examples/spring-boot-service.md +25 -0
- package/knowledge/test-matrix-examples/ssr-htmx.md +24 -0
- package/knowledge/test-organization.md +70 -0
- package/knowledge/test-pyramid.md +84 -0
- package/knowledge/test-refactoring.md +67 -0
- package/knowledge/test-review-division-of-labor.md +85 -0
- package/knowledge/test-smells.md +80 -0
- package/knowledge/test-stack-profiles/bdd-frameworks.md +235 -0
- package/knowledge/test-stack-profiles/django.md +13 -0
- package/knowledge/test-stack-profiles/dotnet.md +18 -0
- package/knowledge/test-stack-profiles/go.md +16 -0
- package/knowledge/test-stack-profiles/node.md +16 -0
- package/knowledge/test-stack-profiles/react.md +12 -0
- package/knowledge/test-stack-profiles/spring-boot.md +16 -0
- package/knowledge/test-stack-profiles/ssr-htmx.md +14 -0
- package/knowledge/test-stack-profiles/vue.md +12 -0
- package/knowledge/test-strategy.md +70 -0
- package/knowledge/testability-patterns.md +240 -0
- package/knowledge/testing-quadrants.md +44 -0
- package/knowledge/testing-techniques/approval.md +15 -0
- package/knowledge/testing-techniques/chaos.md +17 -0
- package/knowledge/testing-techniques/fuzz.md +15 -0
- package/knowledge/testing-techniques/property-based.md +15 -0
- package/knowledge/testing-techniques/schema-validation.md +15 -0
- package/knowledge/testing-techniques/screenshot.md +15 -0
- package/knowledge/three-phase-workflow.md +198 -0
- package/knowledge/value-patterns.md +55 -0
- package/knowledge/verification-mode.md +116 -0
- package/knowledge/virtual-service-libraries.md +75 -0
- package/knowledge/wave-consolidation-guidance.md +21 -0
- package/overrides/agents/Explore.md +15 -0
- package/overrides/agents/general-purpose.md +10 -0
- package/overrides/notes/autoship.md +6 -0
- package/overrides/notes/issues-from-assessment.md +3 -0
- package/overrides/notes/issues-from-plan.md +3 -0
- package/overrides/notes/mutation-night-watch.md +3 -0
- package/overrides/notes/mutation-testing.md +3 -0
- package/overrides/notes/pr.md +7 -0
- package/overrides/notes/project-init.md +6 -0
- package/overrides/notes/setup.md +13 -0
- package/overrides/notes/specs.md +3 -0
- package/overrides/skills/headless-run/SKILL.md +45 -0
- package/overrides/skills/upgrade/SKILL.md +30 -0
- package/overrides/skills/version/SKILL.md +25 -0
- package/package.json +36 -0
- package/scripts/authoring_digest.py +93 -0
- package/scripts/autoship_discover.py +121 -0
- package/scripts/autoship_group.py +409 -0
- package/scripts/autoship_proposals.py +494 -0
- package/scripts/autoship_queue.py +291 -0
- package/scripts/autoship_reclaim.py +495 -0
- package/scripts/build_jobs.py +108 -0
- package/scripts/build_rollback_point.py +240 -0
- package/scripts/build_slice_scope.py +157 -0
- package/scripts/build_wave.py +109 -0
- package/scripts/build_wave_reconcile.py +252 -0
- package/scripts/build_worktree_baseref.py +113 -0
- package/scripts/check_agent_scope.py +117 -0
- package/scripts/check_agent_tool_mapping.py +213 -0
- package/scripts/check_review_agent_mcp_tools.py +317 -0
- package/scripts/check_security_assessment_mcp_tools.py +165 -0
- package/scripts/checkpoint_abort.py +502 -0
- package/scripts/claude_setup_review.py +438 -0
- package/scripts/codebase_recon.py +556 -0
- package/scripts/coverage_config.py +623 -0
- package/scripts/coverage_delta_steering.py +330 -0
- package/scripts/coverage_discovery_dotnet.py +315 -0
- package/scripts/coverage_discovery_java.py +742 -0
- package/scripts/coverage_discovery_js.py +546 -0
- package/scripts/coverage_gap_ranking.py +556 -0
- package/scripts/coverage_readiness.py +455 -0
- package/scripts/coverage_report_parse.py +521 -0
- package/scripts/detect_bdd_convention.py +252 -0
- package/scripts/eval_ablation.py +376 -0
- package/scripts/gherkin_analysis_coverage_gate.py +306 -0
- package/scripts/gherkin_cross_feature_duplicate_titles_gate.py +173 -0
- package/scripts/gherkin_effectiveness_rollup.py +238 -0
- package/scripts/gherkin_failure_path_gate.py +206 -0
- package/scripts/gherkin_feature_merge.py +720 -0
- package/scripts/gherkin_stub_gate.py +163 -0
- package/scripts/gherkin_stub_merge.py +479 -0
- package/scripts/git_origin_host.py +88 -0
- package/scripts/install-java-static-analysis.py +110 -0
- package/scripts/issue_deps.py +74 -0
- package/scripts/lib/_bdd_markers.py +28 -0
- package/scripts/lib/_gherkin_text.py +93 -0
- package/scripts/lib/_vendored_tree.py +70 -0
- package/scripts/lib/autoship_state.py +397 -0
- package/scripts/lib/claude_md_guard.py +226 -0
- package/scripts/lib/deterministic_recon.py +446 -0
- package/scripts/lib/mcp_tool_grants.py +211 -0
- package/scripts/lib/plan_parse.py +386 -0
- package/scripts/lib/review_result.py +84 -0
- package/scripts/lib/review_roster.py +86 -0
- package/scripts/lib/session_log/__init__.py +34 -0
- package/scripts/lib/session_log/__pycache__/__init__.cpython-314.pyc +0 -0
- package/scripts/lib/session_log/__pycache__/records.cpython-314.pyc +0 -0
- package/scripts/lib/session_log/classify.py +231 -0
- package/scripts/lib/session_log/corrections.py +194 -0
- package/scripts/lib/session_log/discovery.py +108 -0
- package/scripts/lib/session_log/records.py +218 -0
- package/scripts/lib/session_log/redact.py +76 -0
- package/scripts/lib/session_log/signals.py +373 -0
- package/scripts/lib/session_report_downstream.py +614 -0
- package/scripts/lib/session_report_maintainer.py +1273 -0
- package/scripts/lib/session_report_shared.py +262 -0
- package/scripts/lib/settings_hook_guard.py +157 -0
- package/scripts/lib/slug.py +33 -0
- package/scripts/lib/stub_extractors/__init__.py +82 -0
- package/scripts/lib/stub_extractors/_common.py +328 -0
- package/scripts/lib/stub_extractors/csharp.py +19 -0
- package/scripts/lib/stub_extractors/go.py +173 -0
- package/scripts/lib/stub_extractors/java.py +18 -0
- package/scripts/lib/stub_extractors/jsts.py +126 -0
- package/scripts/mutation_stack_sections.py +149 -0
- package/scripts/mutation_yield_steering.py +345 -0
- package/scripts/orchestrator.py +895 -0
- package/scripts/plan_gherkin_export.py +227 -0
- package/scripts/plan_waves.py +208 -0
- package/scripts/pr_close_keyword_lint.py +108 -0
- package/scripts/progress_guardian.py +888 -0
- package/scripts/recon_inventory.py +273 -0
- package/scripts/review_findings_log.py +93 -0
- package/scripts/run_invariants.py +124 -0
- package/scripts/select_lenses.py +640 -0
- package/scripts/session_report.py +486 -0
- package/scripts/set_autocompact_env.py +221 -0
- package/scripts/ship_resume_guard.py +135 -0
- package/scripts/ship_review_gate.py +63 -0
- package/scripts/specs_convention_marker.py +103 -0
- package/scripts/test_improve_resume.py +277 -0
- package/scripts/test_review_mechanics.py +958 -0
- package/scripts/token_efficiency_review.py +322 -0
- package/scripts/verdict_scope.py +285 -0
- package/scripts/verify_gherkin_quality_critic_isolation.py +296 -0
- package/scripts/verify_tier.py +157 -0
- package/skills/adr-tools/SKILL.md +118 -0
- package/skills/agent-readiness/SKILL.md +105 -0
- package/skills/agent-readiness/ai_friendly_analyzers.py +326 -0
- package/skills/agent-readiness/scanner.py +441 -0
- package/skills/agent-readiness/scorecard.yaml +88 -0
- package/skills/api-design/SKILL.md +115 -0
- package/skills/apply-fixes/SKILL.md +171 -0
- package/skills/apply-test-doubles/SKILL.md +321 -0
- package/skills/artifact-lifecycle/SKILL.md +127 -0
- package/skills/autoship/SKILL.md +1124 -0
- package/skills/benchmark/SKILL.md +105 -0
- package/skills/branch-workflow/SKILL.md +89 -0
- package/skills/browse/SKILL.md +184 -0
- package/skills/browser-testing/SKILL.md +62 -0
- package/skills/browser-testing/references/playwright-patterns.md +216 -0
- package/skills/build/SKILL.md +422 -0
- package/skills/build/references/static-self-heal.md +245 -0
- package/skills/careful/SKILL.md +72 -0
- package/skills/cd-test-architecture/SKILL.md +371 -0
- package/skills/ci-debugging/SKILL.md +105 -0
- package/skills/co-evolution-audit/SKILL.md +269 -0
- package/skills/code-review/SKILL.md +1015 -0
- package/skills/code-review/examples/aggregated-sample.json +56 -0
- package/skills/code-review/examples/sample-report.md +41 -0
- package/skills/code-review/output-format.md +478 -0
- package/skills/code-review/scripts/activation.py +86 -0
- package/skills/code-review/scripts/change_impact.py +357 -0
- package/skills/code-review/scripts/change_shape.py +372 -0
- package/skills/code-review/scripts/change_size.py +212 -0
- package/skills/code-review/scripts/changed_file_list.py +141 -0
- package/skills/code-review/scripts/closing_pass.py +187 -0
- package/skills/code-review/scripts/consolidate.py +277 -0
- package/skills/code-review/scripts/contract_failure_report.py +185 -0
- package/skills/code-review/scripts/dispatch_reconcile.py +66 -0
- package/skills/code-review/scripts/dispatch_waves.py +164 -0
- package/skills/code-review/scripts/finding_signature.py +446 -0
- package/skills/code-review/scripts/ledger.py +283 -0
- package/skills/code-review/scripts/partition.py +169 -0
- package/skills/code-review/scripts/render_tiered_findings.py +274 -0
- package/skills/code-review/scripts/repo_invariants.py +1066 -0
- package/skills/code-review/scripts/review_context_pack.py +306 -0
- package/skills/code-review/scripts/review_round_log.py +345 -0
- package/skills/code-review/scripts/review_value_coverage.py +297 -0
- package/skills/code-review/scripts/validate_review_output.py +467 -0
- package/skills/code-review/sliced-mode.md +205 -0
- package/skills/competitive-analysis/SKILL.md +191 -0
- package/skills/context-loading-protocol/SKILL.md +157 -0
- package/skills/continue/SKILL.md +90 -0
- package/skills/cost-report/SKILL.md +178 -0
- package/skills/coverage-baseline/SKILL.md +335 -0
- package/skills/coverage-baseline/references/multi-project-discovery.md +202 -0
- package/skills/coverage-delta/SKILL.md +181 -0
- package/skills/coverage-delta/references/mutation-gate.md +70 -0
- package/skills/design-doc/SKILL.md +95 -0
- package/skills/design-interrogation/SKILL.md +89 -0
- package/skills/design-it-twice/SKILL.md +91 -0
- package/skills/docker-image-audit/SKILL.md +108 -0
- package/skills/docker-image-audit/references/install-guide.md +64 -0
- package/skills/docker-image-audit/references/report-template.md +73 -0
- package/skills/docker-image-create/SKILL.md +185 -0
- package/skills/domain-analysis/SKILL.md +183 -0
- package/skills/domain-driven-design/SKILL.md +194 -0
- package/skills/exploratory-testing/SKILL.md +108 -0
- package/skills/explore/SKILL.md +51 -0
- package/skills/farley-score/SKILL.md +165 -0
- package/skills/feature-file-validation/SKILL.md +78 -0
- package/skills/feature-file-validation/references/validation-rules.md +115 -0
- package/skills/feedback-learning/SKILL.md +414 -0
- package/skills/fix/SKILL.md +450 -0
- package/skills/freeze/SKILL.md +68 -0
- package/skills/frontend-architecture/SKILL.md +113 -0
- package/skills/gherkin-derive/SKILL.md +630 -0
- package/skills/gherkin-public/SKILL.md +266 -0
- package/skills/governance-compliance/SKILL.md +150 -0
- package/skills/guard/SKILL.md +75 -0
- package/skills/handoff/SKILL.md +139 -0
- package/skills/handoff/references/summary-templates.md +242 -0
- package/skills/harness-audit/SKILL.md +751 -0
- package/skills/harness-audit/scripts/lesson_validate.py +386 -0
- package/skills/harness-audit/scripts/redundancy_criterion.py +188 -0
- package/skills/headless-run/SKILL.md +45 -0
- package/skills/headless-run/scripts/isolated_dispatch.py +381 -0
- package/skills/help/SKILL.md +72 -0
- package/skills/hexagonal-architecture/SKILL.md +85 -0
- package/skills/human-oversight-protocol/SKILL.md +224 -0
- package/skills/issues-from-assessment/SKILL.md +223 -0
- package/skills/issues-from-plan/SKILL.md +133 -0
- package/skills/legacy-code/SKILL.md +132 -0
- package/skills/mermaid-diagramming/SKILL.md +120 -0
- package/skills/mutation-night-watch/SKILL.md +154 -0
- package/skills/mutation-night-watch/references/scheduling.md +135 -0
- package/skills/mutation-testing/SKILL.md +396 -0
- package/skills/mutation-testing/references/languages/csharp-stryker-net.md +676 -0
- package/skills/mutation-testing/references/languages/go-go-mutesting.md +95 -0
- package/skills/mutation-testing/references/languages/java-pitest.md +77 -0
- package/skills/mutation-testing/references/languages/javascript-stryker.md +188 -0
- package/skills/mutation-testing/references/languages/python-mutmut.md +97 -0
- package/skills/mutation-testing/references/time-estimation.md +34 -0
- package/skills/mutation-testing/references/tool-detection.md +15 -0
- package/skills/mutation-testing/references/workflow-callers.md +23 -0
- package/skills/mutation-testing/scripts/__pycache__/xunit_v3_feature_detector.cpython-314.pyc +0 -0
- package/skills/mutation-testing/scripts/csharp_stryker_net_slice_runner.py +635 -0
- package/skills/mutation-testing/scripts/csharp_stryker_net_status_loop.py +525 -0
- package/skills/mutation-testing/scripts/csharp_stryker_net_wrapper.py +681 -0
- package/skills/mutation-testing/scripts/mutation_baseline_reuse.py +292 -0
- package/skills/mutation-testing/scripts/mutation_exclude_policy.py +268 -0
- package/skills/mutation-testing/scripts/mutation_feasibility_gate.py +463 -0
- package/skills/mutation-testing/scripts/mutation_kill_headless.py +331 -0
- package/skills/mutation-testing/scripts/mutation_kill_insert.py +199 -0
- package/skills/mutation-testing/scripts/mutation_kill_insert_python.py +150 -0
- package/skills/mutation-testing/scripts/mutation_kill_loop.py +869 -0
- package/skills/mutation-testing/scripts/mutation_kill_loop_python.py +949 -0
- package/skills/mutation-testing/scripts/mutation_kill_retry.py +592 -0
- package/skills/mutation-testing/scripts/mutation_kill_shared.py +620 -0
- package/skills/mutation-testing/scripts/mutation_nightwatch.py +462 -0
- package/skills/mutation-testing/scripts/mutation_nightwatch_stacks.py +425 -0
- package/skills/mutation-testing/scripts/mutation_report.py +743 -0
- package/skills/mutation-testing/scripts/mutation_report_cli.py +175 -0
- package/skills/mutation-testing/scripts/mutation_safety_gate.py +69 -0
- package/skills/mutation-testing/scripts/stryker_shard_pipeline.py +847 -0
- package/skills/mutation-testing/scripts/stryker_shard_setup.py +440 -0
- package/skills/mutation-testing/scripts/stryker_timeout_retry.py +142 -0
- package/skills/mutation-testing/scripts/xunit_v3_feature_detector.py +341 -0
- package/skills/performance-benchmark/SKILL.md +174 -0
- package/skills/performance-benchmark/examples/report-format.md +43 -0
- package/skills/performance-benchmark/references/benchmark-script.md +169 -0
- package/skills/performance-metrics/SKILL.md +265 -0
- package/skills/plan/SKILL.md +199 -0
- package/skills/plan/references/gherkin-persistence.md +43 -0
- package/skills/plan/references/plan-template.md +182 -0
- package/skills/pr/SKILL.md +289 -0
- package/skills/pr/scripts/gate_retry_state.py +368 -0
- package/skills/project-init/README.md +141 -0
- package/skills/project-init/SKILL.md +1197 -0
- package/skills/project-init/evals/evals.json +200 -0
- package/skills/project-init/references/capability-tools.md +55 -0
- package/skills/project-init/references/configs.md +221 -0
- package/skills/property-based-testing/SKILL.md +121 -0
- package/skills/property-based-testing/fixtures/invariant_fixture.py +15 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/README.md +42 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/fast-check/LICENSE +21 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/fast-check/README.md +263 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/fast-check/lib/cjs/fast-check.d.ts +5165 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/fast-check/lib/cjs/fast-check.js +12147 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/fast-check/lib/cjs/package.json +3 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/fast-check/lib/cjs/types57/fast-check.d.ts +5165 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/fast-check/lib/fast-check.d.ts +5165 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/fast-check/lib/fast-check.js +12011 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/fast-check/lib/rolldown-runtime-D7D4PA-g.js +13 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/fast-check/lib/types57/fast-check.d.ts +5165 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/fast-check/package.json +94 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/LICENSE +21 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/README.md +168 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/RandomGenerator-DcXj09Ch.d.ts +14 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/distribution/uniformBigInt.d.ts +15 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/distribution/uniformBigInt.js +38 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/distribution/uniformFloat32.d.ts +15 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/distribution/uniformFloat32.js +18 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/distribution/uniformFloat64.d.ts +15 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/distribution/uniformFloat64.js +22 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/distribution/uniformInt.d.ts +15 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/distribution/uniformInt.js +134 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/RandomGenerator-DcXj09Ch.d.ts +14 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/distribution/uniformBigInt.d.ts +15 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/distribution/uniformBigInt.js +37 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/distribution/uniformFloat32.d.ts +15 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/distribution/uniformFloat32.js +17 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/distribution/uniformFloat64.d.ts +15 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/distribution/uniformFloat64.js +21 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/distribution/uniformInt.d.ts +15 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/distribution/uniformInt.js +133 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/generator/congruential32.d.ts +7 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/generator/congruential32.js +44 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/generator/mersenne.d.ts +7 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/generator/mersenne.js +90 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/generator/xoroshiro128plus.d.ts +7 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/generator/xoroshiro128plus.js +80 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/generator/xorshift128plus.d.ts +7 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/generator/xorshift128plus.js +78 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/package.json +3 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/types/JumpableRandomGenerator.d.ts +16 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/types/JumpableRandomGenerator.js +0 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/types/RandomGenerator.d.ts +2 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/types/RandomGenerator.js +0 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/utils/generateN.d.ts +6 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/utils/generateN.js +8 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/utils/purify.d.ts +12 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/utils/purify.js +9 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/utils/skipN.d.ts +6 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/utils/skipN.js +6 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/generator/congruential32.d.ts +7 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/generator/congruential32.js +46 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/generator/mersenne.d.ts +7 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/generator/mersenne.js +92 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/generator/xoroshiro128plus.d.ts +7 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/generator/xoroshiro128plus.js +82 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/generator/xorshift128plus.d.ts +7 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/generator/xorshift128plus.js +80 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/types/JumpableRandomGenerator.d.ts +16 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/types/JumpableRandomGenerator.js +0 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/types/RandomGenerator.d.ts +2 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/types/RandomGenerator.js +0 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/utils/generateN.d.ts +6 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/utils/generateN.js +9 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/utils/purify.d.ts +12 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/utils/purify.js +10 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/utils/skipN.d.ts +6 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/utils/skipN.js +7 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/package.json +133 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/package-lock.json +1179 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/package.json +14 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/roundtrip.js +29 -0
- package/skills/property-based-testing/fixtures/js-roundtrip/roundtrip.properties.test.js +16 -0
- package/skills/property-based-testing/fixtures/no_property_fixture.py +10 -0
- package/skills/property-based-testing/fixtures/roundtrip_fixture.py +16 -0
- package/skills/property-based-testing/references/languages/javascript.md +54 -0
- package/skills/property-based-testing/scripts/detect_and_dispatch.py +80 -0
- package/skills/property-based-testing/scripts/hypothesis_scaffold.py +276 -0
- package/skills/proxy-resilience/SKILL.md +84 -0
- package/skills/quality-gate-pipeline/SKILL.md +184 -0
- package/skills/quality-targets-converge/SKILL.md +254 -0
- package/skills/repo-review/SKILL.md +159 -0
- package/skills/report-pdf/SKILL.md +66 -0
- package/skills/review/SKILL.md +47 -0
- package/skills/review-agent/SKILL.md +152 -0
- package/skills/review-summary/SKILL.md +73 -0
- package/skills/run-report/SKILL.md +70 -0
- package/skills/semantic-duplication-scan/SKILL.md +337 -0
- package/skills/semantic-scan/SKILL.md +53 -0
- package/skills/semgrep-analyze/SKILL.md +139 -0
- package/skills/setup/SKILL.md +1122 -0
- package/skills/ship/SKILL.md +240 -0
- package/skills/source-verification/SKILL.md +210 -0
- package/skills/source-verification/scripts/claim_extractor.py +155 -0
- package/skills/specs/.size-baseline.json +4 -0
- package/skills/specs/SKILL.md +243 -0
- package/skills/specs/references/completeness-checklist.md +83 -0
- package/skills/specs/references/extraction.md +58 -0
- package/skills/specs/references/glossary.md +59 -0
- package/skills/specs/references/persistence.md +115 -0
- package/skills/specs/references/predictability-check.md +77 -0
- package/skills/static-analysis-integration/SKILL.md +235 -0
- package/skills/static-analysis-integration/adapters/_envelope.py +26 -0
- package/skills/static-analysis-integration/adapters/jscpd-adapter.py +66 -0
- package/skills/static-analysis-integration/adapters/lizard-adapter.py +81 -0
- package/skills/static-analysis-integration/adapters/mypy-adapter.py +50 -0
- package/skills/static-analysis-integration/adapters/mypy-src-layout.py +93 -0
- package/skills/static-analysis-integration/adapters/security-review-adapter.py +212 -0
- package/skills/static-analysis-integration/maintenance.md +23 -0
- package/skills/static-analysis-integration/references/language-setup.md +228 -0
- package/skills/static-analysis-integration/references/sarif-parser.md +124 -0
- package/skills/static-analysis-integration/references/security-review-adapter.md +118 -0
- package/skills/static-analysis-integration/references/tool-configs.md +617 -0
- package/skills/static-analysis-integration/rulesets/pmd-quickstart.xml +24 -0
- package/skills/stryker-xunit-v2-shim/SKILL.md +274 -0
- package/skills/stryker-xunit-v2-shim/references/shim-howto.md +256 -0
- package/skills/stryker-xunit-v2-shim/scripts/generate_shim.py +143 -0
- package/skills/systematic-debugging/SKILL.md +130 -0
- package/skills/telemetry/SKILL.md +75 -0
- package/skills/test-audit-disable/SKILL.md +129 -0
- package/skills/test-design/SKILL.md +177 -0
- package/skills/test-design/scripts/__pycache__/internal_double_detector.cpython-314.pyc +0 -0
- package/skills/test-design/scripts/internal_double_detector.py +631 -0
- package/skills/test-design-advisor/SKILL.md +166 -0
- package/skills/test-driven-development/SKILL.md +169 -0
- package/skills/test-health/SKILL.md +262 -0
- package/skills/test-improve/SKILL.md +239 -0
- package/skills/test-improve/references/phase-0-approach-contract.md +228 -0
- package/skills/test-improve/references/phase-1-analyze.md +131 -0
- package/skills/test-improve/references/phase-2-baseline.md +121 -0
- package/skills/test-improve/references/phase-3-derive-gherkin.md +53 -0
- package/skills/test-improve/references/phase-4-plan-fixes.md +34 -0
- package/skills/test-improve/references/phase-5-improve.md +215 -0
- package/skills/test-improve/references/phase-6-refactor-decision.md +45 -0
- package/skills/test-improve/references/phase-7-refactor.md +44 -0
- package/skills/test-improve/references/phase-8-validate.md +66 -0
- package/skills/test-improve/references/phase-9-close-out-prompt.md +11 -0
- package/skills/test-improve/references/phase-9-report.md +62 -0
- package/skills/test-improve/references/review-loop.md +92 -0
- package/skills/test-improve/templates/executive-summary.md +123 -0
- package/skills/threat-modeling/SKILL.md +108 -0
- package/skills/triage/SKILL.md +211 -0
- package/skills/ubiquitous-language/SKILL.md +192 -0
- package/skills/ubiquitous-language/scripts/collect_domain_signals.py +300 -0
- package/skills/unfreeze/SKILL.md +37 -0
- package/skills/upgrade/SKILL.md +31 -0
- package/skills/upgrade/scripts/check_version_drift.py +113 -0
- package/skills/upgrade/scripts/enable_autoupdate.py +149 -0
- package/skills/version/SKILL.md +25 -0
- package/sync/__pycache__/sync_upstream.cpython-314.pyc +0 -0
- package/sync/sync_upstream.py +293 -0
- package/templates/ACCEPTED-RISKS.md.tmpl +46 -0
- package/templates/agents/agent-template.md +151 -0
- package/templates/agents/angular-testing.md +66 -0
- package/templates/agents/csharp-quality.md +63 -0
- package/templates/agents/esm-enforcer.md +52 -0
- package/templates/agents/front-end-testing.md +65 -0
- package/templates/agents/go-quality.md +65 -0
- package/templates/agents/python-quality.md +62 -0
- package/templates/agents/react-testing.md +61 -0
- package/templates/agents/ts-enforcer.md +60 -0
- package/templates/agents/twelve-factor-audit.md +49 -0
- package/tools/entropy-check.py +250 -0
- package/tools/model-hash-verify.py +213 -0
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""mutation_kill_headless.py — unattended generation + CLI entry point.
|
|
3
|
+
|
|
4
|
+
Split out of ``mutation_kill_loop.py`` (#1562): the ``--headless`` CLI
|
|
5
|
+
argument parsing and entry point for the C#/Stryker.NET loop. The
|
|
6
|
+
agent-driven (default, interactive) path never touches this module — the
|
|
7
|
+
mutation-kill agent calls :func:`mutation_kill_loop.run_for_file` directly
|
|
8
|
+
with its own ``generate`` hook.
|
|
9
|
+
|
|
10
|
+
The ``claude --print`` invocation glue itself (``run_claude_headless``,
|
|
11
|
+
``resolve_model``, ``strip_code_fences``, ``claude_cli_available``,
|
|
12
|
+
``CLAUDE_CLI``) lives in ``mutation_kill_shared.py``, not here (#1601) — it's
|
|
13
|
+
reused verbatim by ``mutation_kill_loop_python.py``, and none of it is
|
|
14
|
+
C#-specific. This module only builds the C#-flavored prompt
|
|
15
|
+
(``build_generation_prompt``) and imports the handful of shared names it
|
|
16
|
+
actually calls (``CLAUDE_CLI``, ``resolve_model``, ``claude_cli_available``,
|
|
17
|
+
``run_claude_headless``); ``mutation_kill_loop_python.py`` now imports the
|
|
18
|
+
same names directly from ``mutation_kill_shared`` too, rather than through
|
|
19
|
+
this module, which previously dragged the whole C#/Stryker.NET stack
|
|
20
|
+
(``mutation_kill_loop`` and everything it imports) into the Python loop just
|
|
21
|
+
to reuse five language-neutral names.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
from __future__ import annotations
|
|
25
|
+
|
|
26
|
+
import argparse
|
|
27
|
+
import os
|
|
28
|
+
import sys
|
|
29
|
+
import time
|
|
30
|
+
from collections.abc import Callable, Sequence
|
|
31
|
+
from pathlib import Path
|
|
32
|
+
|
|
33
|
+
from mutation_kill_loop import Generator, RunContext, load_loop_config, run_for_file
|
|
34
|
+
from mutation_kill_retry import (
|
|
35
|
+
EXIT_GENERATION_EXHAUSTED,
|
|
36
|
+
EXIT_REVERT_FAILED,
|
|
37
|
+
DowngradeEvent,
|
|
38
|
+
GenerationExhausted,
|
|
39
|
+
make_downgrade_audit_hook,
|
|
40
|
+
make_retrying_headless_call,
|
|
41
|
+
)
|
|
42
|
+
from mutation_kill_shared import (
|
|
43
|
+
CLAUDE_CLI,
|
|
44
|
+
RevertFailed,
|
|
45
|
+
claude_cli_available,
|
|
46
|
+
resolve_model,
|
|
47
|
+
run_claude_headless, # noqa: F401 — re-exported for tests (headless.run_claude_headless identity check); make_headless_generator now calls it indirectly via make_retrying_headless_call (#1908)
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
# The exact message the bare-CLI startup preflight prints. Pinned so the
|
|
51
|
+
# contract test can assert it verbatim.
|
|
52
|
+
NO_GENERATOR_MESSAGE = (
|
|
53
|
+
"no test generator available — invoke via the mutation-kill agent "
|
|
54
|
+
"or pass --headless"
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
# Printed when --headless is requested but the Claude CLI can't be reached.
|
|
58
|
+
# Names exactly how to install and authenticate it, and mutates no files.
|
|
59
|
+
MISSING_CLAUDE_MESSAGE = (
|
|
60
|
+
f"--headless requires the Claude CLI but '{CLAUDE_CLI}' is not available. "
|
|
61
|
+
"Install Claude Code (`npm install -g @anthropic-ai/claude-code`) and "
|
|
62
|
+
"authenticate it (run `claude` once to log in, or set ANTHROPIC_API_KEY) — "
|
|
63
|
+
"or set CLAUDE_BIN to the CLI's path."
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def build_survivor_summary(survivors: list[dict], *, limit: int = 40) -> str:
|
|
68
|
+
"""Render surviving mutants as a compact, framework-agnostic list."""
|
|
69
|
+
lines = []
|
|
70
|
+
for mutant in survivors[:limit]:
|
|
71
|
+
line = mutant.get("location", {}).get("start", {}).get("line", "?")
|
|
72
|
+
mutator = mutant.get("mutatorName", "?")
|
|
73
|
+
replacement = mutant.get("replacement", "")
|
|
74
|
+
lines.append(f"- L{line} {mutator}: {replacement}".rstrip())
|
|
75
|
+
if len(survivors) > limit:
|
|
76
|
+
lines.append(f"- … and {len(survivors) - limit} more")
|
|
77
|
+
return "\n".join(lines)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def build_generation_prompt(
|
|
81
|
+
source_file: str,
|
|
82
|
+
survivors: list[dict],
|
|
83
|
+
source_text: str,
|
|
84
|
+
test_text: str,
|
|
85
|
+
*,
|
|
86
|
+
source_limit: int = 8000,
|
|
87
|
+
) -> str:
|
|
88
|
+
"""Build the generation prompt.
|
|
89
|
+
|
|
90
|
+
The existing test file is the *only* pattern — assertion library, mocking
|
|
91
|
+
approach, fixtures, and naming conventions are all inferred from it. No
|
|
92
|
+
library name is hardcoded here, so the prompt is repo-agnostic (AC1).
|
|
93
|
+
"""
|
|
94
|
+
return (
|
|
95
|
+
f"You are adding new test methods that KILL surviving mutations in "
|
|
96
|
+
f"{source_file}.\n\n"
|
|
97
|
+
"Match the existing test file exactly: its imports, assertion style, "
|
|
98
|
+
"mocking approach, fixtures, and naming conventions are the pattern to "
|
|
99
|
+
"follow. Do not introduce any library, helper, or convention that does "
|
|
100
|
+
"not already appear in it.\n\n"
|
|
101
|
+
f"## Surviving mutations ({len(survivors)})\n"
|
|
102
|
+
f"{build_survivor_summary(survivors)}\n\n"
|
|
103
|
+
f"## Source under test\n{source_text[:source_limit]}\n\n"
|
|
104
|
+
f"## Existing test file (the pattern to match)\n{test_text}\n\n"
|
|
105
|
+
"## Rules\n"
|
|
106
|
+
"1. Return ONLY the new test methods — no class wrapper, namespace, or "
|
|
107
|
+
"imports.\n"
|
|
108
|
+
"2. Each must compile against the helpers already in the existing test "
|
|
109
|
+
"file.\n"
|
|
110
|
+
"3. Reuse the existing file's assertion, mocking, and fixture patterns "
|
|
111
|
+
"exactly.\n"
|
|
112
|
+
"4. Match the existing naming convention.\n"
|
|
113
|
+
"5. Do not redeclare fields or helpers already present.\n"
|
|
114
|
+
"6. Do not emit closing braces for the class or namespace.\n"
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def make_headless_generator(
|
|
119
|
+
model: str | None = None,
|
|
120
|
+
*,
|
|
121
|
+
cwd: Path | None = None,
|
|
122
|
+
log: Callable[[str], None] = print,
|
|
123
|
+
on_downgrade: Callable[[DowngradeEvent], None] | None = None,
|
|
124
|
+
sleep: Callable[[float], None] = time.sleep,
|
|
125
|
+
) -> Generator:
|
|
126
|
+
"""Return a :data:`mutation_kill_loop.Generator` that shells to
|
|
127
|
+
``claude --print``.
|
|
128
|
+
|
|
129
|
+
The returned callable builds the prompt from the existing test file (the
|
|
130
|
+
pattern) plus the survivor summary, then delegates everything else to
|
|
131
|
+
:func:`mutation_kill_retry.make_retrying_headless_call` (#1908) — the
|
|
132
|
+
3-consecutive-gateway-class-failures/1-same-model-retry/at-most-once-
|
|
133
|
+
per-file-downgrade wrapper around :func:`run_claude_headless`.
|
|
134
|
+
|
|
135
|
+
The retry/downgrade state (consecutive-failure counter, model in use,
|
|
136
|
+
whether this file already spent its one downgrade) lives in the
|
|
137
|
+
``retrying_call`` closure below — constructed once per file, here, never
|
|
138
|
+
at module scope — so a new file's generator always starts fresh at the
|
|
139
|
+
top of the ladder regardless of a prior file's downgrade, and concurrent
|
|
140
|
+
files under ``--all --concurrency`` (each with their own closure) never
|
|
141
|
+
leak state to one another. ``round_num`` is derived from how many times
|
|
142
|
+
THIS closure has been invoked (the loop calls its ``generate`` once per
|
|
143
|
+
round), since the shared :data:`Generator` signature carries no round
|
|
144
|
+
number of its own.
|
|
145
|
+
|
|
146
|
+
``on_downgrade``, when given, is passed straight through to
|
|
147
|
+
:func:`mutation_kill_retry.make_retrying_headless_call`. Building the
|
|
148
|
+
``on_downgrade``/``get_label_override`` audit-trail pair
|
|
149
|
+
(:func:`mutation_kill_retry.make_downgrade_audit_hook`) is this
|
|
150
|
+
module's own ``main()``'s job now (#1908 review) — this function no
|
|
151
|
+
longer constructs one internally or attaches a
|
|
152
|
+
``label_override_provider`` attribute to the returned ``generate``;
|
|
153
|
+
``main()`` has ``get_label_override`` directly in scope and wires it
|
|
154
|
+
into :class:`RunContext` itself, so no attribute-smuggling is needed.
|
|
155
|
+
"""
|
|
156
|
+
retrying_call = make_retrying_headless_call(
|
|
157
|
+
initial_model=model, cwd=cwd, log=log, on_downgrade=on_downgrade, sleep=sleep
|
|
158
|
+
)
|
|
159
|
+
round_counter = {"n": 0}
|
|
160
|
+
|
|
161
|
+
def generate(
|
|
162
|
+
source_file: str,
|
|
163
|
+
survivors: list[dict],
|
|
164
|
+
source_text: str,
|
|
165
|
+
test_text: str,
|
|
166
|
+
) -> str:
|
|
167
|
+
round_counter["n"] += 1
|
|
168
|
+
prompt = build_generation_prompt(source_file, survivors, source_text, test_text)
|
|
169
|
+
return retrying_call(prompt, source_file, round_counter["n"])
|
|
170
|
+
|
|
171
|
+
return generate
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def parse_args(argv: Sequence[str]) -> argparse.Namespace:
|
|
175
|
+
p = argparse.ArgumentParser(
|
|
176
|
+
prog="mutation_kill_loop.py",
|
|
177
|
+
description=(
|
|
178
|
+
"Config-driven survivor-kill loop. Agent-driven by default; "
|
|
179
|
+
"--headless enables unattended generation via the Claude CLI."
|
|
180
|
+
),
|
|
181
|
+
)
|
|
182
|
+
p.add_argument("--config", default="stryker-config.json", help="stryker-config.json path")
|
|
183
|
+
p.add_argument("--file", help="Source file to target (basename or path)")
|
|
184
|
+
p.add_argument("--output", default="StrykerOutput/agent", help="Scoped-run output dir")
|
|
185
|
+
p.add_argument("--max-rounds", type=int, default=5, help="Max rounds per file")
|
|
186
|
+
p.add_argument(
|
|
187
|
+
"--stryker-bin",
|
|
188
|
+
default=os.environ.get("STRYKER_BIN", "dotnet"),
|
|
189
|
+
help="Stryker executable name, or 'dotnet' to invoke a local-tool-"
|
|
190
|
+
"manifest install via 'dotnet stryker' (default: %(default)s)",
|
|
191
|
+
)
|
|
192
|
+
p.add_argument(
|
|
193
|
+
"--headless",
|
|
194
|
+
action="store_true",
|
|
195
|
+
help="Unattended generation via `claude --print` (CI / shard pipeline).",
|
|
196
|
+
)
|
|
197
|
+
p.add_argument(
|
|
198
|
+
"--model",
|
|
199
|
+
help=(
|
|
200
|
+
"Generation model for --headless. Default: DEV_TEAM_MUTATION_MODEL "
|
|
201
|
+
"env var, else omitted so `claude --print` uses its own default."
|
|
202
|
+
),
|
|
203
|
+
)
|
|
204
|
+
p.add_argument("--test-file", help="Test file to extend (required with --headless)")
|
|
205
|
+
p.add_argument("--source-path", help="Source file under test (required with --headless)")
|
|
206
|
+
p.add_argument(
|
|
207
|
+
"--target-honest-score",
|
|
208
|
+
type=float,
|
|
209
|
+
default=None,
|
|
210
|
+
help=(
|
|
211
|
+
"Phase-0 mutation target (percent). Stop a file once its honest "
|
|
212
|
+
"score reaches this, since work past the threshold cannot change "
|
|
213
|
+
"the Phase-8 verdict. Default off — unset reproduces pre-#2030 "
|
|
214
|
+
"behavior exactly."
|
|
215
|
+
),
|
|
216
|
+
)
|
|
217
|
+
p.add_argument(
|
|
218
|
+
"--min-kills-per-round",
|
|
219
|
+
type=float,
|
|
220
|
+
default=None,
|
|
221
|
+
help=(
|
|
222
|
+
"Marginal-yield floor. >=1 is an absolute kill count; 0<v<1 is a "
|
|
223
|
+
"fraction of the round's starting survivors. A round below the "
|
|
224
|
+
"floor while still under target is surfaced to the operator "
|
|
225
|
+
"([c]ontinue / [r]etry / [w]aive / [q]uit), never stopped "
|
|
226
|
+
"silently. Default off."
|
|
227
|
+
),
|
|
228
|
+
)
|
|
229
|
+
p.add_argument("--report", help="Initial mutation report to seed round 1 (--headless)")
|
|
230
|
+
return p.parse_args(list(argv))
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
def main(argv: Sequence[str] | None = None) -> int:
|
|
234
|
+
"""CLI entry point.
|
|
235
|
+
|
|
236
|
+
The CLI has no way to inject an agent generator (that path calls
|
|
237
|
+
:func:`mutation_kill_loop.run_for_file` directly). So without
|
|
238
|
+
``--headless`` there is no generator, and we fail fast **at startup** —
|
|
239
|
+
before resolving config, probing DOTNET_ROOT, running Stryker, or
|
|
240
|
+
touching any file.
|
|
241
|
+
|
|
242
|
+
With ``--headless`` the generator shells to ``claude --print``. The Claude
|
|
243
|
+
CLI is preflight-checked *before* any file argument validation, so a
|
|
244
|
+
missing CLI fails cleanly at startup and mutates nothing.
|
|
245
|
+
"""
|
|
246
|
+
argv = list(sys.argv[1:] if argv is None else argv)
|
|
247
|
+
args = parse_args(argv)
|
|
248
|
+
|
|
249
|
+
if not args.headless:
|
|
250
|
+
sys.stderr.write(f"error: {NO_GENERATOR_MESSAGE}\n")
|
|
251
|
+
return 1
|
|
252
|
+
|
|
253
|
+
model = resolve_model(args.model)
|
|
254
|
+
|
|
255
|
+
# Preflight the CLI first — a missing CLI must fail before we touch any
|
|
256
|
+
# file or validate run arguments.
|
|
257
|
+
if not claude_cli_available():
|
|
258
|
+
sys.stderr.write(f"error: {MISSING_CLAUDE_MESSAGE}\n")
|
|
259
|
+
return 3
|
|
260
|
+
|
|
261
|
+
if not (args.file and args.test_file and args.source_path):
|
|
262
|
+
sys.stderr.write(
|
|
263
|
+
"error: --headless requires --file, --test-file, and --source-path\n"
|
|
264
|
+
)
|
|
265
|
+
return 2
|
|
266
|
+
|
|
267
|
+
on_downgrade, get_label_override = make_downgrade_audit_hook()
|
|
268
|
+
generate = make_headless_generator(model, on_downgrade=on_downgrade)
|
|
269
|
+
try:
|
|
270
|
+
run_for_file(
|
|
271
|
+
args.file,
|
|
272
|
+
RunContext(
|
|
273
|
+
config=load_loop_config(Path(args.config)),
|
|
274
|
+
test_file=Path(args.test_file),
|
|
275
|
+
source_path=Path(args.source_path),
|
|
276
|
+
output_dir=Path(args.output),
|
|
277
|
+
stryker_bin=args.stryker_bin,
|
|
278
|
+
initial_report_path=Path(args.report) if args.report else None,
|
|
279
|
+
generator_label=f"headless ({model or 'default'})",
|
|
280
|
+
label_override_provider=get_label_override,
|
|
281
|
+
target_honest_score=args.target_honest_score,
|
|
282
|
+
min_kills_per_round=args.min_kills_per_round,
|
|
283
|
+
),
|
|
284
|
+
generate=generate,
|
|
285
|
+
max_rounds=args.max_rounds,
|
|
286
|
+
)
|
|
287
|
+
except GenerationExhausted as exc:
|
|
288
|
+
# This file's retry-then-downgrade budget is fully spent (3
|
|
289
|
+
# consecutive gateway-class failures + 1 same-model retry, at the
|
|
290
|
+
# original model AND at most one fallback tier) — distinct from
|
|
291
|
+
# RevertFailed below (exit 4, working tree possibly mutated) and
|
|
292
|
+
# from the generic RuntimeError case below it (exit 5, clean but
|
|
293
|
+
# not exhausted — e.g. a non-gateway-class generation timeout). A
|
|
294
|
+
# clean exhaustion mutates nothing: generation precedes insertion
|
|
295
|
+
# within a round, and a prior round's own revert failure is itself
|
|
296
|
+
# fatal (raised as RevertFailed, never swallowed). What isn't
|
|
297
|
+
# independently re-verified here is that a revert git reports as
|
|
298
|
+
# successful actually left the tree clean (#1955) — so callers
|
|
299
|
+
# (stryker_shard_pipeline.py's shard driver) can log this file as
|
|
300
|
+
# unfixed and continue to the next file without affecting the run's
|
|
301
|
+
# exit status, instead of aborting the whole shard (#1908 review).
|
|
302
|
+
sys.stderr.write(f"error: {exc}\n")
|
|
303
|
+
return EXIT_GENERATION_EXHAUSTED
|
|
304
|
+
except RevertFailed as exc:
|
|
305
|
+
# A failed revert (or a failed-commit round-abandonment's own
|
|
306
|
+
# revert) leaves the working tree in an unknown, possibly-mutated
|
|
307
|
+
# state (#1930) — narrower and more urgent than the generic
|
|
308
|
+
# RuntimeError case below: this is the only case that can't be
|
|
309
|
+
# trusted as clean.
|
|
310
|
+
sys.stderr.write(f"error: {exc}\n")
|
|
311
|
+
return EXIT_REVERT_FAILED
|
|
312
|
+
except RuntimeError as exc:
|
|
313
|
+
# Every other RuntimeError this loop raises (a generation timeout,
|
|
314
|
+
# a mutmut/Stryker infrastructure failure, etc.) is clean with
|
|
315
|
+
# respect to inserted test content — generation precedes insertion,
|
|
316
|
+
# and a failed insertion-revert is RevertFailed, caught above. The
|
|
317
|
+
# C# solution-file restore (wrapper.restore_sln) now also reports a
|
|
318
|
+
# bool outcome (#1955) and run_scoped_stryker raises RevertFailed on
|
|
319
|
+
# a False return, so a stray hidden .sln is caught by the
|
|
320
|
+
# RevertFailed branch above, not silently reaching here. Not a
|
|
321
|
+
# retry-budget exhaustion — reuses exit 5 (#1956: this is the
|
|
322
|
+
# OUTCOME class, not a specific cause) because the shard driver only
|
|
323
|
+
# distinguishes "fatal, stop" (4) from "clean, continue" (5), not
|
|
324
|
+
# why a file wasn't fixed.
|
|
325
|
+
sys.stderr.write(f"error: {exc} — generation failed cleanly, continuing\n")
|
|
326
|
+
return EXIT_GENERATION_EXHAUSTED
|
|
327
|
+
return 0
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
if __name__ == "__main__":
|
|
331
|
+
sys.exit(main())
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""mutation_kill_insert.py — C# test-method insertion mechanics.
|
|
3
|
+
|
|
4
|
+
Split out of ``mutation_kill_loop.py`` (#1562): detect-or-refuse insertion of
|
|
5
|
+
generated test methods before a test class's closing brace. The heuristic
|
|
6
|
+
supports only conventional block-namespace, 4-space-indented C# test classes
|
|
7
|
+
— a file-scoped namespace or non-4-space indentation is refused rather than
|
|
8
|
+
risk a mis-insertion.
|
|
9
|
+
|
|
10
|
+
``InsertOutcome``/``InsertionRefused`` are imported from
|
|
11
|
+
``mutation_kill_shared`` (#1583, relocated from ``mutation_safety_gate`` in
|
|
12
|
+
#1602), not defined here — they're structurally identical to the Python
|
|
13
|
+
sibling's (``mutation_kill_insert_python.py``), so both modules share one
|
|
14
|
+
definition instead of two hand-maintained copies. See
|
|
15
|
+
``mutation_kill_shared``'s module docstring for the unification rationale.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
|
|
20
|
+
import re
|
|
21
|
+
from collections.abc import Sequence
|
|
22
|
+
from pathlib import Path
|
|
23
|
+
|
|
24
|
+
import mutation_safety_gate
|
|
25
|
+
from mutation_kill_shared import InsertionRefused, InsertOutcome
|
|
26
|
+
|
|
27
|
+
# Matches a public test-method declaration (async Task or void), capturing the
|
|
28
|
+
# method name. Framework-agnostic — no attribute or library name is assumed.
|
|
29
|
+
_METHOD_RE = re.compile(
|
|
30
|
+
r"public\s+(?:async\s+)?(?:void|Task(?:<[^>]*>)?)\s+(\w+)\s*\("
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
# A file-scoped namespace declaration ends with a semicolon and has no
|
|
34
|
+
# wrapping braces: ``namespace Foo.Bar;``
|
|
35
|
+
_FILE_SCOPED_NS_RE = re.compile(r"^\s*namespace\s+[\w.]+\s*;", re.MULTILINE)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def detect_duplicate_methods(test_text: str, new_text: str) -> list[str]:
|
|
39
|
+
"""Return the method names in ``new_text`` that already exist in the file."""
|
|
40
|
+
existing = set(_METHOD_RE.findall(test_text))
|
|
41
|
+
incoming = _METHOD_RE.findall(new_text)
|
|
42
|
+
return [name for name in incoming if name in existing]
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def count_methods(new_text: str) -> int:
|
|
46
|
+
"""Count public test-method declarations in ``new_text``."""
|
|
47
|
+
return len(_METHOD_RE.findall(new_text))
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
# A deny-list of APIs a test method that merely kills mutants never
|
|
51
|
+
# legitimately needs. Scoped to generated *test* text only — never to the
|
|
52
|
+
# source-under-test — so it doesn't false-positive on production code that
|
|
53
|
+
# does real I/O. This is a static-pattern gate, not a sandbox: a
|
|
54
|
+
# sufficiently obfuscated payload (reflection-built names, string
|
|
55
|
+
# concatenation) could still evade it. Guards against a prompt-injection
|
|
56
|
+
# payload in the mutated source producing generated test code with
|
|
57
|
+
# network/filesystem/process side effects that would still pass build+test
|
|
58
|
+
# (which only check compilation and pass/fail, never behavior) and, in
|
|
59
|
+
# --headless mode, get committed with zero human review. Category names are
|
|
60
|
+
# a stable, test-pinned contract shared with mutation_kill_loop_python's
|
|
61
|
+
# deny-list — renaming one is a breaking change for callers matching on
|
|
62
|
+
# InsertOutcome.reason.
|
|
63
|
+
_UNSAFE_PATTERNS: dict[str, re.Pattern[str]] = {
|
|
64
|
+
"network": re.compile(
|
|
65
|
+
r"\b(HttpClient|WebRequest|WebClient|TcpClient|UdpClient|SmtpClient|Socket|Dns|"
|
|
66
|
+
r"System\s*\.\s*Net\s*\.)\b"
|
|
67
|
+
),
|
|
68
|
+
"process": re.compile(r"\b(Process|StartInfo|ProcessStartInfo)\b"),
|
|
69
|
+
"filesystem": re.compile(
|
|
70
|
+
r"\b(File\s*\.\s*(WriteAllText|WriteAllBytes|WriteAllLines|AppendAllText|"
|
|
71
|
+
r"AppendAllLines)(Async)?|File\s*\.\s*(Create|Delete|Move|Copy|Open|OpenWrite)|"
|
|
72
|
+
r"Directory\s*\.\s*(Delete|CreateDirectory|Move)|StreamWriter|FileStream|"
|
|
73
|
+
r"FileInfo|DirectoryInfo)\b"
|
|
74
|
+
),
|
|
75
|
+
"environment": re.compile(
|
|
76
|
+
r"\b(Environment\s*\.\s*GetEnvironmentVariables?|ConfigurationManager\s*\.\s*AppSettings)\b"
|
|
77
|
+
),
|
|
78
|
+
"reflection": re.compile(
|
|
79
|
+
r"\b(Assembly\s*\.\s*Load(From|File|WithPartialName)?|"
|
|
80
|
+
r"Activator\s*\.\s*CreateInstance(From)?|System\s*\.\s*Reflection\s*\.\s*Emit)\b"
|
|
81
|
+
),
|
|
82
|
+
"interop": re.compile(r"\b(Marshal\s*\.|DllImport)\b|(?<![\w.])unsafe\b"),
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def scan_for_unsafe_patterns(new_methods: str) -> list[str]:
|
|
87
|
+
"""Return the category names of any unsafe pattern found in generated text.
|
|
88
|
+
|
|
89
|
+
A non-empty result refuses insertion outright — this deny-list is
|
|
90
|
+
deliberately conservative for *generated test* code specifically (a
|
|
91
|
+
legitimate mutant-killing test never needs network/filesystem/process/env
|
|
92
|
+
access), so a false positive refuses-and-logs rather than silently
|
|
93
|
+
inserting unreviewed code. Delegates to :mod:`mutation_safety_gate`,
|
|
94
|
+
shared with :mod:`mutation_kill_insert_python`, so a future bypass fix or
|
|
95
|
+
new category lands once for both languages.
|
|
96
|
+
"""
|
|
97
|
+
return mutation_safety_gate.scan_for_unsafe_patterns(new_methods, _UNSAFE_PATTERNS)
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def _find_block_namespace_class_close(lines: Sequence[str]) -> int | None:
|
|
101
|
+
"""Return the index of the class-closing brace, or None to refuse.
|
|
102
|
+
|
|
103
|
+
Walks from the end: finds the namespace-closing brace (a line whose
|
|
104
|
+
stripped form is ``}``), then the last line that is exactly four spaces
|
|
105
|
+
followed by ``}`` before it — the conventional block-namespace class
|
|
106
|
+
close. Any other indentation yields None (refuse).
|
|
107
|
+
"""
|
|
108
|
+
ns_idx = None
|
|
109
|
+
for i in range(len(lines) - 1, -1, -1):
|
|
110
|
+
if lines[i].strip() == "}":
|
|
111
|
+
ns_idx = i
|
|
112
|
+
break
|
|
113
|
+
if ns_idx is None:
|
|
114
|
+
return None
|
|
115
|
+
for i in range(ns_idx - 1, -1, -1):
|
|
116
|
+
if lines[i].rstrip("\r\n") == " }":
|
|
117
|
+
return i
|
|
118
|
+
return None
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def insert_before_class_close(test_file: Path, new_methods: str, text: str) -> None:
|
|
122
|
+
"""Insert ``new_methods`` before the test class's closing brace.
|
|
123
|
+
|
|
124
|
+
Raises :class:`InsertionRefused` for a file-scoped namespace or any
|
|
125
|
+
non-4-space-indented class the heuristic can't safely locate. The file is
|
|
126
|
+
left untouched on refusal.
|
|
127
|
+
|
|
128
|
+
``text`` is the current test-file content, already read by the caller
|
|
129
|
+
(:func:`apply_generated_methods`) — this function performs no read of its
|
|
130
|
+
own. A previous, optional ``test_text=`` shape let a caller pass in
|
|
131
|
+
content that bypassed reading the file's CURRENT state (including this
|
|
132
|
+
function's own refusal-guard check running against stale content rather
|
|
133
|
+
than what's actually on disk) — a real hazard even though no caller
|
|
134
|
+
exploited it. Removing the optional/None-triggered-fallback shape closes
|
|
135
|
+
that off: freshness is now entirely ``apply_generated_methods``'s
|
|
136
|
+
responsibility, which reads immediately before calling this (#1598/#1584
|
|
137
|
+
review, item 7).
|
|
138
|
+
"""
|
|
139
|
+
if _FILE_SCOPED_NS_RE.search(text):
|
|
140
|
+
raise InsertionRefused(
|
|
141
|
+
f"refusing to insert into {test_file.name}: file-scoped namespace "
|
|
142
|
+
"detected — the class-close heuristic supports only block-namespace, "
|
|
143
|
+
"4-space-indented classes"
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
lines = text.splitlines(keepends=True)
|
|
147
|
+
cc_idx = _find_block_namespace_class_close(lines)
|
|
148
|
+
if cc_idx is None:
|
|
149
|
+
raise InsertionRefused(
|
|
150
|
+
f"refusing to insert into {test_file.name}: could not locate a "
|
|
151
|
+
"conventional 4-space class-closing brace (non-standard indentation?)"
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
block = (
|
|
155
|
+
["\n"]
|
|
156
|
+
+ [ln if ln.endswith("\n") else ln + "\n" for ln in new_methods.strip().splitlines()]
|
|
157
|
+
+ ["\n"]
|
|
158
|
+
)
|
|
159
|
+
lines = lines[:cc_idx] + block + lines[cc_idx:]
|
|
160
|
+
test_file.write_text("".join(lines), encoding="utf-8")
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def apply_generated_methods(test_file: Path, new_methods: str) -> InsertOutcome:
|
|
164
|
+
"""Insert generated methods, guarding duplicates and unsafe structure.
|
|
165
|
+
|
|
166
|
+
Returns an :class:`InsertOutcome`; the file is only ever written on the
|
|
167
|
+
``inserted=True`` path. Empty generation, an unsafe pattern, duplicate
|
|
168
|
+
method names, and a refused insert all leave the file untouched.
|
|
169
|
+
|
|
170
|
+
Reads ``test_file`` exactly once here — immediately before the
|
|
171
|
+
duplicate-name check — and reuses that single fresh read for both the
|
|
172
|
+
check and the write (passed through to :func:`insert_before_class_close`,
|
|
173
|
+
which performs no read of its own). A prior shape let the caller (e.g.
|
|
174
|
+
``_run_round``) thread in its OWN already-read ``test_text`` for the
|
|
175
|
+
duplicate check — but that text was read *before* the possibly
|
|
176
|
+
multi-minute ``generate()`` call, so the check ran against a stale
|
|
177
|
+
snapshot: a method name added to the file during generation (by another
|
|
178
|
+
process, or a second concurrent round) would go undetected as a
|
|
179
|
+
duplicate. Reading fresh here, after ``generate()`` has already
|
|
180
|
+
returned, closes that gap (#1598/#1584 review, item 7).
|
|
181
|
+
"""
|
|
182
|
+
if not new_methods.strip():
|
|
183
|
+
return InsertOutcome(False, "no methods generated")
|
|
184
|
+
|
|
185
|
+
unsafe_categories = scan_for_unsafe_patterns(new_methods)
|
|
186
|
+
if unsafe_categories:
|
|
187
|
+
return InsertOutcome(False, f"refused — unsafe pattern(s): {unsafe_categories}")
|
|
188
|
+
|
|
189
|
+
text = test_file.read_text(encoding="utf-8")
|
|
190
|
+
|
|
191
|
+
dupes = detect_duplicate_methods(text, new_methods)
|
|
192
|
+
if dupes:
|
|
193
|
+
return InsertOutcome(False, f"duplicate method names: {dupes}")
|
|
194
|
+
|
|
195
|
+
try:
|
|
196
|
+
insert_before_class_close(test_file, new_methods, text)
|
|
197
|
+
except InsertionRefused as exc:
|
|
198
|
+
return InsertOutcome(False, str(exc))
|
|
199
|
+
return InsertOutcome(True, "inserted")
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""mutation_kill_insert_python.py — Python/pytest test-function insertion
|
|
3
|
+
mechanics.
|
|
4
|
+
|
|
5
|
+
Split out of ``mutation_kill_loop_python.py`` (#1583), mirroring the C#
|
|
6
|
+
split ``mutation_kill_insert.py`` (#1562): detect-or-refuse insertion of
|
|
7
|
+
generated pytest test functions at the end of a test file. Unlike C#'s
|
|
8
|
+
"find the class-closing brace" problem, a flat pytest module has no
|
|
9
|
+
enclosing structure to locate — the safe insertion point is simply
|
|
10
|
+
end-of-file, PROVIDED the file already follows that flat, top-level
|
|
11
|
+
``def test_*():`` convention. A file with no top-level ``def test_`` function
|
|
12
|
+
at all (e.g. tests organized as ``class Test...:`` methods) doesn't match
|
|
13
|
+
this convention, and the loop refuses rather than guess.
|
|
14
|
+
|
|
15
|
+
``InsertOutcome``/``InsertionRefused`` are imported from
|
|
16
|
+
``mutation_kill_shared`` (relocated from ``mutation_safety_gate`` in #1602;
|
|
17
|
+
not defined here) — they're structurally identical to the C# sibling's, so
|
|
18
|
+
both modules share one definition instead of two hand-maintained copies. See
|
|
19
|
+
``mutation_kill_shared``'s module docstring for the unification rationale.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
from __future__ import annotations
|
|
23
|
+
|
|
24
|
+
import re
|
|
25
|
+
from pathlib import Path
|
|
26
|
+
|
|
27
|
+
import mutation_safety_gate
|
|
28
|
+
from mutation_kill_shared import InsertionRefused, InsertOutcome
|
|
29
|
+
|
|
30
|
+
# A top-level (unindented) pytest test function declaration, capturing the name.
|
|
31
|
+
_FUNC_RE = re.compile(r"^def\s+(test_\w+)\s*\(", re.MULTILINE)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def detect_duplicate_functions(test_text: str, new_text: str) -> list[str]:
|
|
35
|
+
"""Return the function names in ``new_text`` that already exist in the file."""
|
|
36
|
+
existing = set(_FUNC_RE.findall(test_text))
|
|
37
|
+
incoming = _FUNC_RE.findall(new_text)
|
|
38
|
+
return [name for name in incoming if name in existing]
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def count_tests(new_text: str) -> int:
|
|
42
|
+
"""Count top-level ``def test_*():`` declarations in ``new_text``.
|
|
43
|
+
|
|
44
|
+
Mirrors ``mutation_kill_insert.count_methods`` — the loop uses this to
|
|
45
|
+
report how many new tests a round added in its commit message.
|
|
46
|
+
"""
|
|
47
|
+
return len(_FUNC_RE.findall(new_text))
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def append_at_end_of_file(test_file: Path, new_tests: str, text: str) -> None:
|
|
51
|
+
"""Append ``new_tests`` to the end of the file, with one blank line of
|
|
52
|
+
separation, and a trailing newline.
|
|
53
|
+
|
|
54
|
+
Raises :class:`InsertionRefused` when the file has no existing top-level
|
|
55
|
+
``def test_*():`` — the flat-module convention this heuristic supports.
|
|
56
|
+
The file is left untouched on refusal.
|
|
57
|
+
|
|
58
|
+
``text`` is the current test-file content, already read by the caller
|
|
59
|
+
(:func:`apply_generated_tests`) — this function performs no read of its
|
|
60
|
+
own, so freshness is entirely the caller's responsibility.
|
|
61
|
+
"""
|
|
62
|
+
if not _FUNC_RE.search(text):
|
|
63
|
+
raise InsertionRefused(
|
|
64
|
+
f"refusing to insert into {test_file.name}: no top-level "
|
|
65
|
+
"`def test_*():` found — this heuristic supports only the flat "
|
|
66
|
+
"module convention (a class-based test file needs a different "
|
|
67
|
+
"insertion point, not appended at end-of-file)"
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
body = new_tests.strip()
|
|
71
|
+
separator = "" if text.endswith("\n\n") else ("\n" if text.endswith("\n") else "\n\n")
|
|
72
|
+
test_file.write_text(text + separator + body + "\n", encoding="utf-8")
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
# A deny-list of APIs a test function that merely kills mutants never
|
|
76
|
+
# legitimately needs. Scoped to generated *test* text only — never to the
|
|
77
|
+
# source-under-test. Same rationale and same category names as
|
|
78
|
+
# ``mutation_kill_insert``'s C# counterpart (kept consistent as a stable,
|
|
79
|
+
# operator-facing and test-pinned contract) — this is the Python/pytest
|
|
80
|
+
# equivalent, guarding this loop's own ``--headless`` unattended-commit path
|
|
81
|
+
# against a prompt-injection payload in the mutated source producing
|
|
82
|
+
# generated test code with network/filesystem/process side effects that
|
|
83
|
+
# would still pass compile+test and get committed with zero human review.
|
|
84
|
+
_UNSAFE_PATTERNS: dict[str, re.Pattern[str]] = {
|
|
85
|
+
"network": re.compile(
|
|
86
|
+
r"\b(requests\s*\.|urllib\s*\.\s*request|urllib3\s*\.|httpx\s*\.|socket\s*\.|http\s*\.\s*client)\b"
|
|
87
|
+
),
|
|
88
|
+
"process": re.compile(
|
|
89
|
+
r"\b(subprocess\s*\.|os\s*\.\s*system|os\s*\.\s*popen|os\s*\.\s*exec\w*|"
|
|
90
|
+
r"os\s*\.\s*(spawn\w*|posix_spawn|fork|forkpty)|Popen)\b"
|
|
91
|
+
),
|
|
92
|
+
"filesystem": re.compile(
|
|
93
|
+
r"\b(shutil\s*\.\s*rmtree|os\s*\.\s*(remove|unlink))\b"
|
|
94
|
+
r"|\.\s*(write_text|write_bytes|unlink)\s*\("
|
|
95
|
+
r"|open\s*\([^)]*[\"'][waxA]"
|
|
96
|
+
),
|
|
97
|
+
"environment": re.compile(r"\b(os\s*\.\s*environ|os\s*\.\s*getenv)\b"),
|
|
98
|
+
"reflection": re.compile(r"\b(importlib\s*\.\s*import_module|__import__)\b"),
|
|
99
|
+
"interop": re.compile(r"\b(eval|exec)\s*\(|\bctypes\s*\.|\bpickle\s*\.\s*loads\s*\("),
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def scan_for_unsafe_patterns(new_tests: str) -> list[str]:
|
|
104
|
+
"""Return the category names of any unsafe pattern found in generated text.
|
|
105
|
+
|
|
106
|
+
A non-empty result refuses insertion outright — this deny-list is
|
|
107
|
+
deliberately conservative for *generated test* code specifically (a
|
|
108
|
+
legitimate mutant-killing test never needs network/filesystem/process/env
|
|
109
|
+
access), so a false positive refuses-and-logs rather than silently
|
|
110
|
+
inserting unreviewed code. Delegates to :mod:`mutation_safety_gate`,
|
|
111
|
+
shared with :mod:`mutation_kill_insert`, so a future bypass fix or new
|
|
112
|
+
category lands once for both languages.
|
|
113
|
+
"""
|
|
114
|
+
return mutation_safety_gate.scan_for_unsafe_patterns(new_tests, _UNSAFE_PATTERNS)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def apply_generated_tests(test_file: Path, new_tests: str) -> InsertOutcome:
|
|
118
|
+
"""Insert generated tests, guarding duplicates and unsafe structure.
|
|
119
|
+
|
|
120
|
+
Returns an :class:`InsertOutcome`; the file is only ever written on the
|
|
121
|
+
``inserted=True`` path. Empty generation, an unsafe pattern, duplicate
|
|
122
|
+
function names, and a refused insert all leave the file untouched.
|
|
123
|
+
|
|
124
|
+
Reads ``test_file`` exactly once here — immediately before the
|
|
125
|
+
duplicate-name check — and reuses that single fresh read for both the
|
|
126
|
+
check and the write (passed through to :func:`append_at_end_of_file`,
|
|
127
|
+
which performs no read of its own). Reading fresh here, after the
|
|
128
|
+
caller's (possibly multi-minute) generation call has already returned,
|
|
129
|
+
means a test function added to the file externally in the meantime is
|
|
130
|
+
still caught as a duplicate rather than silently double-inserted
|
|
131
|
+
(#1598/#1584 review, item 7).
|
|
132
|
+
"""
|
|
133
|
+
if not new_tests.strip():
|
|
134
|
+
return InsertOutcome(False, "no tests generated")
|
|
135
|
+
|
|
136
|
+
unsafe_categories = scan_for_unsafe_patterns(new_tests)
|
|
137
|
+
if unsafe_categories:
|
|
138
|
+
return InsertOutcome(False, f"refused — unsafe pattern(s): {unsafe_categories}")
|
|
139
|
+
|
|
140
|
+
text = test_file.read_text(encoding="utf-8")
|
|
141
|
+
|
|
142
|
+
dupes = detect_duplicate_functions(text, new_tests)
|
|
143
|
+
if dupes:
|
|
144
|
+
return InsertOutcome(False, f"duplicate function names: {dupes}")
|
|
145
|
+
|
|
146
|
+
try:
|
|
147
|
+
append_at_end_of_file(test_file, new_tests, text)
|
|
148
|
+
except InsertionRefused as exc:
|
|
149
|
+
return InsertOutcome(False, str(exc))
|
|
150
|
+
return InsertOutcome(True, "inserted")
|