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,101 @@
|
|
|
1
|
+
"""hooks/lib/review_dispatch_ledger.py — shared review-dispatch-ledger reading (#1998).
|
|
2
|
+
|
|
3
|
+
`review_value_coverage.py` and `contract_failure_report.py` both need the
|
|
4
|
+
same denominator: how many times each review agent was actually dispatched.
|
|
5
|
+
Both hand-rolled an identical `read_jsonl()` + `dispatch_counts()` pair over
|
|
6
|
+
`boundary-events.jsonl`'s `hook == "agent_dispatch_ledger"` / `decision ==
|
|
7
|
+
"record"` rows (see `agent_dispatch_ledger.py`) — the predicate "this row IS
|
|
8
|
+
a review dispatch" had three homes (the emitter plus two independent
|
|
9
|
+
readers) before this module. Per this repo's CLAUDE.md ratchet rule ("a
|
|
10
|
+
mechanical finding reported twice becomes a check"), two independent review
|
|
11
|
+
agents (arch-review, structure-review; #1998 wave 1) plus a third
|
|
12
|
+
(domain-review; #1998 wave 2) flagged the same duplication — this module is
|
|
13
|
+
the single extraction point, so a hook never reaches into `scripts/` (the
|
|
14
|
+
correct dependency direction is `scripts/` -> `hooks/lib/`, never the
|
|
15
|
+
reverse, per `review_agent_registry.py`'s own docstring) and neither reader
|
|
16
|
+
re-derives the ledger's wire shape by hand again.
|
|
17
|
+
|
|
18
|
+
Stdlib only. See ADR 0014 / ADR 0015.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
from __future__ import annotations
|
|
22
|
+
|
|
23
|
+
import json
|
|
24
|
+
import sys
|
|
25
|
+
from collections import Counter
|
|
26
|
+
from pathlib import Path
|
|
27
|
+
|
|
28
|
+
_LIB_DIR = Path(__file__).resolve().parent
|
|
29
|
+
if str(_LIB_DIR) not in sys.path:
|
|
30
|
+
sys.path.insert(0, str(_LIB_DIR))
|
|
31
|
+
|
|
32
|
+
import artifact_paths
|
|
33
|
+
import boundary_events
|
|
34
|
+
|
|
35
|
+
#: The stream every review dispatch is deterministically recorded to. An
|
|
36
|
+
#: alias of `boundary_events.LOG_NAME` (the module that actually writes the
|
|
37
|
+
#: ledger and owns its filename), not a fresh literal — kept as this
|
|
38
|
+
#: module's own public name for its existing callers (backstop review
|
|
39
|
+
#: finding, #2166 + #2171: this filename previously had three independent
|
|
40
|
+
#: homes; `repo_invariants.check_ledger_filename_single_sourced` asserts
|
|
41
|
+
#: this stays an identity, not just an equal value).
|
|
42
|
+
LEDGER_STREAM = boundary_events.LOG_NAME
|
|
43
|
+
|
|
44
|
+
#: The ledger rows that denote a review dispatch.
|
|
45
|
+
_LEDGER_HOOK = "agent_dispatch_ledger"
|
|
46
|
+
_LEDGER_DECISION = "record"
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def resolve_stream(category: str, stream: str, cwd: Path, *, migrate: bool = False) -> Path:
|
|
50
|
+
"""Resolve a `.claude/<category>/<stream>` metrics path.
|
|
51
|
+
|
|
52
|
+
`migrate=False` by default: every current caller of this helper is a
|
|
53
|
+
read-only report/query, so a mere read must never migrate a legacy file
|
|
54
|
+
or create `.claude/<category>/` as a side effect (mirrors
|
|
55
|
+
`hooks/lib/metrics_query.py::_stream_path`'s same reasoning). Pass
|
|
56
|
+
`migrate=True` explicitly for a writer.
|
|
57
|
+
"""
|
|
58
|
+
return artifact_paths.resolve_file(category, stream, cwd, migrate=migrate)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def read_jsonl(path: Path) -> tuple[list[dict], int]:
|
|
62
|
+
"""Return (rows, malformed_count). Malformed lines are skipped but
|
|
63
|
+
counted — silently dropping unreadable telemetry is the same class of
|
|
64
|
+
defect #1998 exists to surface, so the count is reported, not swallowed.
|
|
65
|
+
"""
|
|
66
|
+
rows: list[dict] = []
|
|
67
|
+
malformed = 0
|
|
68
|
+
try:
|
|
69
|
+
text = path.read_text(encoding="utf-8")
|
|
70
|
+
except (OSError, UnicodeDecodeError):
|
|
71
|
+
return rows, malformed
|
|
72
|
+
for line in text.splitlines():
|
|
73
|
+
if not line.strip():
|
|
74
|
+
continue
|
|
75
|
+
try:
|
|
76
|
+
parsed = json.loads(line)
|
|
77
|
+
except ValueError:
|
|
78
|
+
malformed += 1
|
|
79
|
+
continue
|
|
80
|
+
if isinstance(parsed, dict):
|
|
81
|
+
rows.append(parsed)
|
|
82
|
+
else:
|
|
83
|
+
malformed += 1
|
|
84
|
+
return rows, malformed
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def dispatch_counts(ledger_rows) -> Counter:
|
|
88
|
+
"""Per-agent dispatch counts from the deterministic ledger."""
|
|
89
|
+
counts: Counter = Counter()
|
|
90
|
+
for row in ledger_rows:
|
|
91
|
+
if row.get("hook") != _LEDGER_HOOK:
|
|
92
|
+
continue
|
|
93
|
+
if row.get("decision") != _LEDGER_DECISION:
|
|
94
|
+
continue
|
|
95
|
+
agent = row.get("matched_rule")
|
|
96
|
+
if isinstance(agent, str) and agent.strip():
|
|
97
|
+
counts[agent.strip()] += 1
|
|
98
|
+
return counts
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
__all__ = ("LEDGER_STREAM", "dispatch_counts", "read_jsonl", "resolve_stream")
|
|
@@ -0,0 +1,521 @@
|
|
|
1
|
+
"""hooks/lib/review_gate_corroboration.py — fail-CLOSED dispatch-ledger
|
|
2
|
+
corroboration reader for the review-corroboration gate (#1461).
|
|
3
|
+
|
|
4
|
+
Read by `hooks/pre_pr_review.py`'s `.pr-review-passed` gate (#1886) — the
|
|
5
|
+
gate's hash-match check alone proves the branch-diff content hasn't changed
|
|
6
|
+
since that file was written; it does NOT prove an independent review
|
|
7
|
+
actually produced that write (see `hooks/lib/review_gate_hash.py`'s own
|
|
8
|
+
docstring for the full account of that residual gap). This module reads
|
|
9
|
+
`hooks/agent_dispatch_ledger.py`'s `"record"` events from
|
|
10
|
+
`.claude/metrics/boundary-events.jsonl` to corroborate that a hash-matching
|
|
11
|
+
write was backed by genuine, recent, distinct review-agent dispatches.
|
|
12
|
+
(`hooks/pre_commit_review.py`'s own `.review-passed` gate was this module's
|
|
13
|
+
original consumer at #1461; that hook is now a documented no-op — see its
|
|
14
|
+
own module docstring — following #1886's PR-time gate migration. This
|
|
15
|
+
module's own `evaluate()`/`has_doc_only_exemption()`/
|
|
16
|
+
`has_single_agent_exemption()` transferred to the new gate as-is; the
|
|
17
|
+
cosmetic-delta carry-forward machinery specific to the old commit-time gate
|
|
18
|
+
— `evaluate_cosmetic_carry_forward()`, `distinct_normalized_dispatches()`,
|
|
19
|
+
`distinct_review_agent_dispatches()`, `CosmeticCarryForwardEvidence` — was
|
|
20
|
+
deleted in #1904 once confirmed to have zero remaining production callers.)
|
|
21
|
+
|
|
22
|
+
Kept as a separate sibling module rather than folded into
|
|
23
|
+
`review_gate_hash.py` (design feedback, #1461): `review_gate_hash.py` is
|
|
24
|
+
deliberately minimal — a single pure hash function with no registry or
|
|
25
|
+
metrics-stream knowledge. This module's registry-cross-referencing,
|
|
26
|
+
metrics-reading responsibility is a different concern and belongs on its own;
|
|
27
|
+
each module's docstring names the other so the split stays intentional, not
|
|
28
|
+
accidental.
|
|
29
|
+
|
|
30
|
+
Built on `hooks/lib/metrics_query.py`'s existing generic JSONL
|
|
31
|
+
reader/filter (`load_stream` + `filter_entries`) rather than a bespoke
|
|
32
|
+
second parser — this repo already has one malformed-line-tolerant reader for
|
|
33
|
+
exactly this metrics-directory shape.
|
|
34
|
+
|
|
35
|
+
FAIL-CLOSED (the deliberate opposite of `hooks/lib/boundary_events.py`'s own
|
|
36
|
+
fail-open write side): any inability to prove genuine dispatch happened —
|
|
37
|
+
a missing ledger file, an unreadable one, or genuinely no qualifying
|
|
38
|
+
entries — is treated the same way a security gate must treat "can't prove
|
|
39
|
+
it" — as "didn't happen". `boundary_events.py` fails open on the *write*
|
|
40
|
+
side because a broken telemetry write must never block a real tool call;
|
|
41
|
+
this module fails closed on the *read* side because a broken/absent
|
|
42
|
+
corroboration read must never let an uncorroborated `.review-passed` write
|
|
43
|
+
pass the gate. Do not "fix" this asymmetry to match the write side — it is
|
|
44
|
+
intentional; see `boundary_events.py`'s own module docstring for its side of
|
|
45
|
+
the contrast.
|
|
46
|
+
|
|
47
|
+
A missing `boundary-events.jsonl` is bucketed as a **read failure**, not as
|
|
48
|
+
"genuinely no entries": that stream is written by many always-on guard
|
|
49
|
+
hooks (destructive_guard, verify_guard, pre_pr_review, telemetry,
|
|
50
|
+
agent_dispatch_ledger, ...), so in any real session that reaches a `gh pr
|
|
51
|
+
create` attempt the file will almost always already exist — its total absence is
|
|
52
|
+
itself a signal that hook registration is broken, which is an infra
|
|
53
|
+
problem the caller should surface distinctly from "the ledger is fine, it
|
|
54
|
+
just has no qualifying dispatches". Malformed *individual* JSON lines are
|
|
55
|
+
NOT a read failure here — matching `metrics_query.load_stream`'s own
|
|
56
|
+
documented precedent, a single corrupt line is skipped, never fatal, and
|
|
57
|
+
every other consumer of that stream relies on the same tolerance. "Unreadable"
|
|
58
|
+
covers a ledger file that exists but can't be read as text at all (permission
|
|
59
|
+
error, undecodable bytes, or the path is not a regular file).
|
|
60
|
+
|
|
61
|
+
Stdlib only. See ADR 0014 / ADR 0015.
|
|
62
|
+
"""
|
|
63
|
+
|
|
64
|
+
from __future__ import annotations
|
|
65
|
+
|
|
66
|
+
import sys
|
|
67
|
+
from datetime import datetime, timedelta, timezone
|
|
68
|
+
from pathlib import Path
|
|
69
|
+
from typing import NamedTuple
|
|
70
|
+
|
|
71
|
+
_LIB_DIR = Path(__file__).resolve().parent
|
|
72
|
+
if str(_LIB_DIR) not in sys.path:
|
|
73
|
+
sys.path.insert(0, str(_LIB_DIR))
|
|
74
|
+
|
|
75
|
+
import artifact_paths
|
|
76
|
+
import metrics_query
|
|
77
|
+
import review_agent_registry
|
|
78
|
+
from boundary_events import LOG_NAME as _LEDGER_STREAM_NAME
|
|
79
|
+
from boundary_events import TS_FORMAT as _TS_FORMAT
|
|
80
|
+
|
|
81
|
+
_EVENT_TYPE = "agent_dispatch_ledger"
|
|
82
|
+
_DECISION = "record"
|
|
83
|
+
|
|
84
|
+
# Dispatch-failure negative evidence (#1763): emitted by
|
|
85
|
+
# `hooks/lib/boundary_events.py`'s `--event dispatch-failure` CLI (a
|
|
86
|
+
# different hook/tool/decision tuple than the "record" events above — see
|
|
87
|
+
# that module's `_CLI_AGENT_EVENTS`) when a dispatched review agent still
|
|
88
|
+
# fails to return a contract-valid result after its single retry.
|
|
89
|
+
_DISPATCH_FAILURE_HOOK = "code-review"
|
|
90
|
+
_DISPATCH_FAILURE_DECISION = "dispatch-failure"
|
|
91
|
+
|
|
92
|
+
# `dispatch_failure_agents` (#1904 item 2) is modeled as `frozenset | None` —
|
|
93
|
+
# `None` means "cannot prove no dispatch failure exists" (a ledger or
|
|
94
|
+
# registry read failure); a `frozenset()` means "provably no dispatch
|
|
95
|
+
# failures"; a non-empty frozenset names the failing agents. Prior to this,
|
|
96
|
+
# the unprovable case was smuggled into the frozenset value space as a fake
|
|
97
|
+
# sentinel MEMBER (`_UNPROVABLE_DISPATCH_FAILURE`, a string no real agent
|
|
98
|
+
# name could equal) — a control state encoded inside the value type, guarded
|
|
99
|
+
# only by an `==`/`any(...)` check and caller convention rather than the type
|
|
100
|
+
# system. Modeling it as `None` instead mirrors `_registered_agents()`'s own
|
|
101
|
+
# `frozenset | None` pattern (#1461/#1866) and makes "cannot prove this" and
|
|
102
|
+
# "no registered review agents" the same *shape* of unprovable-ness, checked
|
|
103
|
+
# with `is None` rather than an equality comparison against a magic value.
|
|
104
|
+
|
|
105
|
+
# The doc-only / single-agent short-circuit exemption events (#1461): emitted
|
|
106
|
+
# directly by `skills/code-review/SKILL.md`'s write sites via
|
|
107
|
+
# `boundary_events.py`'s purpose-locked CLI (`--event doc-only` /
|
|
108
|
+
# `--event single-agent`), not by a PreToolUse hook — see that module's
|
|
109
|
+
# `_main()` docstring and its `_CLI_EVENTS` mapping.
|
|
110
|
+
_DOC_ONLY_HOOK = "code-review"
|
|
111
|
+
_DOC_ONLY_DECISION = "bypass"
|
|
112
|
+
_DOC_ONLY_RULE = "doc-only-review-exempt"
|
|
113
|
+
_SINGLE_AGENT_RULE = "single-agent-review-exempt"
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
class LedgerEvidence(NamedTuple):
|
|
117
|
+
"""Full corroboration-read result — everything a caller needs to pick
|
|
118
|
+
the correct one of the pinned rejection messages (Step 1.3, #1461).
|
|
119
|
+
|
|
120
|
+
Attributes:
|
|
121
|
+
agents_in_window: distinct registered review-agent names
|
|
122
|
+
(`matched_rule` values) dispatched inside the recency window,
|
|
123
|
+
for THIS subject_hash. Empty on any read failure or when
|
|
124
|
+
nothing qualifies.
|
|
125
|
+
any_dispatch_ever: True if at least one genuine `"record"` event
|
|
126
|
+
exists anywhere in the ledger, for ANY subject_hash, regardless
|
|
127
|
+
of the recency window. False on any read failure.
|
|
128
|
+
same_subject_dispatch_ever: True if at least one genuine `"record"`
|
|
129
|
+
event exists anywhere in the ledger for THIS SAME subject_hash,
|
|
130
|
+
regardless of the recency window (#1461 second security
|
|
131
|
+
re-review) — distinguishes genuinely STALE evidence (a review
|
|
132
|
+
of this exact content happened, just too long ago) from
|
|
133
|
+
evidence that only exists for DIFFERENT staged content
|
|
134
|
+
(`any_dispatch_ever` true but this one false): the caller picks
|
|
135
|
+
the "outside the window" message only when this is true, and a
|
|
136
|
+
"reviewed different content" message when it's false but
|
|
137
|
+
`any_dispatch_ever` is true — otherwise "outside the window"
|
|
138
|
+
would misreport a same-window dispatch for unrelated content as
|
|
139
|
+
if it were this content, just late. False on any read failure.
|
|
140
|
+
read_failure_reason: `None` when the ledger was read successfully
|
|
141
|
+
(whether or not it has qualifying entries); `"missing"` or
|
|
142
|
+
`"unreadable"` when it could not be read at all — see module
|
|
143
|
+
docstring for what each means.
|
|
144
|
+
dispatch_failure_agents: registered review-agent names (live-
|
|
145
|
+
registry re-validated, same as `agents_in_window`) whose MOST
|
|
146
|
+
RECENT qualifying event for THIS `subject_hash` — comparing
|
|
147
|
+
"record" events against "dispatch-failure" events (#1763) by
|
|
148
|
+
each event's own `ts` — is a dispatch-failure rather than a
|
|
149
|
+
later "record"; a later "record" for that same agent+hash
|
|
150
|
+
supersedes and removes it from this set, regardless of either
|
|
151
|
+
event's age. Deliberately UNBOUNDED by `window_seconds`, unlike
|
|
152
|
+
`agents_in_window`: a genuine, never-fixed dispatch-failure
|
|
153
|
+
coverage gap for this exact staged content must not silently
|
|
154
|
+
expire just because time passed — only a genuine superseding
|
|
155
|
+
dispatch clears it, never the clock. Empty `frozenset()` on a
|
|
156
|
+
successful read with no qualifying dispatch-failure events.
|
|
157
|
+
`None` on a ledger OR registry READ FAILURE (#1904 item 2) —
|
|
158
|
+
"cannot prove no dispatch failure exists" — never collapsed to
|
|
159
|
+
an empty/all-clear set.
|
|
160
|
+
|
|
161
|
+
KNOWN RESIDUAL GAP (#1763 security review, same class as
|
|
162
|
+
`agent_dispatch_ledger.py`'s own disclosed gap): the superseding
|
|
163
|
+
"record" is a PreToolUse dispatch-START signal, not proof the
|
|
164
|
+
new dispatch itself returned a valid result — `record` events
|
|
165
|
+
fire before any result exists, the same property that made the
|
|
166
|
+
original dispatch-failure mechanism necessary in the first
|
|
167
|
+
place. A gate check that lands in the narrow window between a
|
|
168
|
+
re-dispatch's own "record" and its eventual outcome (success:
|
|
169
|
+
nothing new emitted; failure: a fresh dispatch-failure event)
|
|
170
|
+
could therefore see a stale failure as already-superseded. Not
|
|
171
|
+
fixed here: doing so would need a completion signal this
|
|
172
|
+
harness has no way to emit today, and the plan's own adversarial
|
|
173
|
+
review (three rounds) deliberately chose "superseded by ANY
|
|
174
|
+
later record, regardless of age" as this field's semantics —
|
|
175
|
+
narrowing it now would reopen a settled design decision, not
|
|
176
|
+
fix an implementation bug. Disclosed rather than silently
|
|
177
|
+
assumed away, matching this codebase's own convention for a
|
|
178
|
+
residual gap that raises the bar without claiming to close it.
|
|
179
|
+
"""
|
|
180
|
+
|
|
181
|
+
agents_in_window: frozenset
|
|
182
|
+
any_dispatch_ever: bool
|
|
183
|
+
same_subject_dispatch_ever: bool
|
|
184
|
+
read_failure_reason: str | None
|
|
185
|
+
dispatch_failure_agents: frozenset | None
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
def mtime_to_iso(mtime: float) -> str:
|
|
189
|
+
"""Convert a `Path.stat().st_mtime` epoch float to this stream's
|
|
190
|
+
`%Y-%m-%dT%H:%M:%SZ` UTC timestamp format.
|
|
191
|
+
|
|
192
|
+
Shared here (rather than duplicated in `pre_commit_review.py`) so the
|
|
193
|
+
gate hook and this module never drift on timestamp formatting — the
|
|
194
|
+
hook anchors `before_ts` on `.claude/memory/.review-passed`'s own mtime
|
|
195
|
+
and must format it identically to how every emitter in this repo
|
|
196
|
+
stamps `ts` (confirmed against `boundary_events.py`).
|
|
197
|
+
"""
|
|
198
|
+
return datetime.fromtimestamp(mtime, tz=timezone.utc).strftime(_TS_FORMAT)
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
def _parse_ts(ts: str) -> datetime:
|
|
202
|
+
return datetime.strptime(ts, _TS_FORMAT).replace(tzinfo=timezone.utc)
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
def _since_bound(before_ts: str, window_seconds: int) -> str:
|
|
206
|
+
return (_parse_ts(before_ts) - timedelta(seconds=window_seconds)).strftime(_TS_FORMAT)
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
def _ledger_path(cwd) -> Path:
|
|
210
|
+
base = Path(cwd) if cwd else Path.cwd()
|
|
211
|
+
# Read-only: migrate=False so a corroboration read never migrates a
|
|
212
|
+
# legacy file or creates .claude/metrics/ as a side effect.
|
|
213
|
+
return artifact_paths.resolve_file("metrics", _LEDGER_STREAM_NAME, base, migrate=False)
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
def _registered_agents() -> frozenset | None:
|
|
217
|
+
"""Re-validate against the live registry at READ time too (#1461 security
|
|
218
|
+
review), not just at write time in `agent_dispatch_ledger.py` — defense
|
|
219
|
+
in depth against a stale ledger (written by an older plugin version, or
|
|
220
|
+
copied from another checkout) supplying names no longer registered.
|
|
221
|
+
|
|
222
|
+
Delegates to `review_agent_registry.read_registered_review_agent_names()`
|
|
223
|
+
(#1904 item 1), which owns the read-failure-vs-empty distinction this
|
|
224
|
+
function used to implement locally — see that function's own docstring
|
|
225
|
+
for the full "why `None` vs `frozenset()`" account. `None` on any read
|
|
226
|
+
error, never an empty frozenset (#1763 correctness/security review):
|
|
227
|
+
"registry read failed" and "registry read fine, genuinely zero agents
|
|
228
|
+
registered" require OPPOSITE treatment depending on which side of the
|
|
229
|
+
evidence they narrow (see `_agents_with_unsuperseded_failure`, which
|
|
230
|
+
checks for `None` explicitly rather than treating it as "no agents
|
|
231
|
+
registered").
|
|
232
|
+
"""
|
|
233
|
+
return review_agent_registry.read_registered_review_agent_names(
|
|
234
|
+
review_agent_registry.default_agents_dir()
|
|
235
|
+
)
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
def _read_ledger(cwd) -> tuple:
|
|
239
|
+
"""Return `(entries, failure_reason)`.
|
|
240
|
+
|
|
241
|
+
`failure_reason` is `None` on a successful read (file exists and is
|
|
242
|
+
decodable — even if it yields zero entries); `"missing"` when the
|
|
243
|
+
ledger file does not exist; `"unreadable"` when it exists but raised on
|
|
244
|
+
read. See module docstring for the missing-vs-unreadable rationale and
|
|
245
|
+
why malformed individual lines are not a failure here.
|
|
246
|
+
"""
|
|
247
|
+
path = _ledger_path(cwd)
|
|
248
|
+
if not path.is_file():
|
|
249
|
+
return [], "missing"
|
|
250
|
+
try:
|
|
251
|
+
entries = list(metrics_query.load_stream(path))
|
|
252
|
+
except (OSError, UnicodeDecodeError):
|
|
253
|
+
return [], "unreadable"
|
|
254
|
+
return entries, None
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
def _extract_timeline_entries(entries: list, decision: str, ts_sentinel: str) -> list:
|
|
258
|
+
"""Build `(ts, agent, decision)` tuples for one entry kind — shared by
|
|
259
|
+
both loops in `_agents_with_unsuperseded_failure` (#1799), which
|
|
260
|
+
duplicated this exact loop body twice, differing only in the ts-less
|
|
261
|
+
sentinel default and the `decision` constant.
|
|
262
|
+
|
|
263
|
+
`ts_sentinel` is the value substituted when an entry has no usable
|
|
264
|
+
`ts` (resolved via `metrics_query`'s own `_TS_FIELDS` fallback, never a
|
|
265
|
+
bare `entry.get("ts")`, and never a raw non-str value). Records and
|
|
266
|
+
failures pass opposite sentinels on purpose — see
|
|
267
|
+
`_agents_with_unsuperseded_failure`'s own docstring for the rationale;
|
|
268
|
+
this helper is deliberately unopinionated about which sentinel is
|
|
269
|
+
"correct" and just applies whatever the caller passes.
|
|
270
|
+
"""
|
|
271
|
+
result = []
|
|
272
|
+
for entry in entries:
|
|
273
|
+
agent = entry.get("matched_rule")
|
|
274
|
+
if not isinstance(agent, str):
|
|
275
|
+
continue
|
|
276
|
+
ts = metrics_query._first_present(entry, metrics_query._TS_FIELDS)
|
|
277
|
+
ts = ts if isinstance(ts, str) else ts_sentinel
|
|
278
|
+
result.append((ts, agent, decision))
|
|
279
|
+
return result
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
def _agents_with_unsuperseded_failure(
|
|
283
|
+
records: list, failures: list, registered: frozenset | None
|
|
284
|
+
) -> frozenset | None:
|
|
285
|
+
"""Per agent, compare the most recent qualifying event — a "record" from
|
|
286
|
+
`records` or a "dispatch-failure" from `failures`, both already narrowed
|
|
287
|
+
to the SAME `subject_hash` by the caller — by each event's own `ts`,
|
|
288
|
+
unbounded by any recency window (#1763; see `LedgerEvidence.
|
|
289
|
+
dispatch_failure_agents` for the rationale). Returns the agents whose
|
|
290
|
+
most-recent event is a dispatch-failure; a later "record" for that same
|
|
291
|
+
agent removes it, regardless of either event's age. On an exact `ts` tie
|
|
292
|
+
the dispatch-failure wins (fail CLOSED), since `records` is folded into
|
|
293
|
+
the timeline before `failures` and Python's sort is stable.
|
|
294
|
+
|
|
295
|
+
`registered` being `None` (a registry READ FAILURE, per
|
|
296
|
+
`_registered_agents()` — never "genuinely zero agents registered", which
|
|
297
|
+
is a real `frozenset()`) fails CLOSED by returning `None` immediately
|
|
298
|
+
(#1904 item 2: modeled as `frozenset | None` rather than a fake sentinel
|
|
299
|
+
MEMBER of the frozenset value space), without inspecting `records`/
|
|
300
|
+
`failures` at all (#1763 security/correctness review). Filtering
|
|
301
|
+
negative evidence through an empty set here — the same collapse that
|
|
302
|
+
safely narrows `agents_in_window` — would instead WIDEN the gate: every
|
|
303
|
+
genuine dispatch-failure would be excluded by "not in an empty set",
|
|
304
|
+
producing an all-clear indistinguishable from "provably no failures".
|
|
305
|
+
|
|
306
|
+
An entry with no usable `ts` (checked via `metrics_query`'s own
|
|
307
|
+
`_TS_FIELDS` fallback, the same resolution `filter_entries` already
|
|
308
|
+
applies elsewhere in this module — not a bare `entry.get("ts")`, which
|
|
309
|
+
would silently miss a `timestamp`-keyed entry, and never a raw non-str
|
|
310
|
+
value, which would otherwise crash the sort below on a mixed-type
|
|
311
|
+
comparison) is likewise never dropped, and the two entry KINDS are
|
|
312
|
+
defaulted in OPPOSITE directions on purpose: a ts-less **failure**
|
|
313
|
+
sorts as `""` (after every real ISO timestamp), so it can never
|
|
314
|
+
be superseded by anything — the safe default for negative evidence we
|
|
315
|
+
cannot chronologically place. A ts-less **record** sorts as `""`
|
|
316
|
+
(before every real timestamp), so it can never supersede a real
|
|
317
|
+
failure — the safe default for positive evidence we cannot place. Do
|
|
318
|
+
NOT default both kinds to the same sentinel (an earlier draft used
|
|
319
|
+
`ts or ""` for both, which let ANY ts-bearing record silently supersede
|
|
320
|
+
a ts-less failure — the opposite of "ordered last, never superseded"
|
|
321
|
+
this docstring promises; #1763 correctness review).
|
|
322
|
+
|
|
323
|
+
Live-registry re-validated (`registered`) exactly like `agents_in_window`
|
|
324
|
+
— an unregistered/fabricated agent name is excluded, so a forged event
|
|
325
|
+
can only ever narrow evidence, never widen it.
|
|
326
|
+
"""
|
|
327
|
+
if registered is None:
|
|
328
|
+
return None
|
|
329
|
+
|
|
330
|
+
# A ts-less (or non-str-ts) RECORD defaults to the minimum sort key
|
|
331
|
+
# ("") — it can never supersede a real failure, the safe default for
|
|
332
|
+
# positive evidence we cannot chronologically place. A ts-less FAILURE
|
|
333
|
+
# defaults to the maximum sort key ("" sorts after every real
|
|
334
|
+
# ISO-8601 timestamp string) — it can never be superseded, the safe
|
|
335
|
+
# default for negative evidence we cannot chronologically place.
|
|
336
|
+
# Deliberately the OPPOSITE default from the record case.
|
|
337
|
+
timeline = _extract_timeline_entries(records, _DECISION, "")
|
|
338
|
+
timeline += _extract_timeline_entries(failures, _DISPATCH_FAILURE_DECISION, "")
|
|
339
|
+
timeline.sort(key=lambda item: item[0])
|
|
340
|
+
|
|
341
|
+
latest_decision: dict = {}
|
|
342
|
+
for _ts, agent, decision in timeline:
|
|
343
|
+
latest_decision[agent] = decision
|
|
344
|
+
|
|
345
|
+
return frozenset(
|
|
346
|
+
agent
|
|
347
|
+
for agent, decision in latest_decision.items()
|
|
348
|
+
if decision == _DISPATCH_FAILURE_DECISION and agent in registered
|
|
349
|
+
)
|
|
350
|
+
|
|
351
|
+
|
|
352
|
+
def _binding_evidence(
|
|
353
|
+
dispatches: list,
|
|
354
|
+
failures: list,
|
|
355
|
+
since: str,
|
|
356
|
+
before_ts: str,
|
|
357
|
+
registered: frozenset | None,
|
|
358
|
+
) -> tuple:
|
|
359
|
+
"""Compute `(agents_in_window, dispatch_failure_agents)` from dispatch/
|
|
360
|
+
failure entries ALREADY narrowed to one hash binding by the caller.
|
|
361
|
+
|
|
362
|
+
Shared by `evaluate()` and `evaluate_cosmetic_carry_forward()` (once per
|
|
363
|
+
hash binding it evaluates) — each independently re-implemented this exact
|
|
364
|
+
window-filter + positive-frozenset + `_agents_with_unsuperseded_failure`
|
|
365
|
+
sequence before this extraction (#1836 perf finding), which risked the
|
|
366
|
+
fail-closed positive/negative asymmetry — `registered_for_positive`'s
|
|
367
|
+
`None`-to-empty collapse is safe ONLY for positive evidence, never for
|
|
368
|
+
negative — silently drifting out of sync across independently
|
|
369
|
+
maintained copies.
|
|
370
|
+
"""
|
|
371
|
+
in_window = metrics_query.filter_entries(dispatches, since=since, until=before_ts)
|
|
372
|
+
registered_for_positive = registered if registered is not None else frozenset()
|
|
373
|
+
agents = frozenset(
|
|
374
|
+
e["matched_rule"]
|
|
375
|
+
for e in in_window
|
|
376
|
+
if isinstance(e.get("matched_rule"), str) and e["matched_rule"] in registered_for_positive
|
|
377
|
+
)
|
|
378
|
+
dispatch_failure_agents = _agents_with_unsuperseded_failure(dispatches, failures, registered)
|
|
379
|
+
return agents, dispatch_failure_agents
|
|
380
|
+
|
|
381
|
+
|
|
382
|
+
def _load_ledger_pipeline(cwd, before_ts: str, window_seconds: int) -> tuple:
|
|
383
|
+
"""Shared read-ledger -> since-bound -> registered-agents -> filtered-
|
|
384
|
+
dispatches/failures pipeline (#1799) — `evaluate()` and
|
|
385
|
+
`evaluate_cosmetic_carry_forward()` each independently re-implemented
|
|
386
|
+
this exact sequence before this extraction.
|
|
387
|
+
|
|
388
|
+
Returns `(failure, since, registered, all_dispatches, all_failures)`.
|
|
389
|
+
`failure` is `None` on a successful ledger read; when it is non-`None`
|
|
390
|
+
(`"missing"`/`"unreadable"` — see `_read_ledger`'s own docstring), every
|
|
391
|
+
other element is `None` and the caller must build its own fail-closed
|
|
392
|
+
result immediately, matching each function's own `LedgerEvidence`/
|
|
393
|
+
`CosmeticCarryForwardEvidence` shape — this helper does not build either
|
|
394
|
+
result type itself, since the two callers' failure shapes differ.
|
|
395
|
+
"""
|
|
396
|
+
entries, failure = _read_ledger(cwd)
|
|
397
|
+
if failure is not None:
|
|
398
|
+
return failure, None, None, None, None
|
|
399
|
+
since = _since_bound(before_ts, window_seconds)
|
|
400
|
+
registered = _registered_agents()
|
|
401
|
+
all_dispatches = list(
|
|
402
|
+
metrics_query.filter_entries(entries, event_type=_EVENT_TYPE, gate_outcome=_DECISION)
|
|
403
|
+
)
|
|
404
|
+
all_failures = list(
|
|
405
|
+
metrics_query.filter_entries(
|
|
406
|
+
entries, event_type=_DISPATCH_FAILURE_HOOK, gate_outcome=_DISPATCH_FAILURE_DECISION
|
|
407
|
+
)
|
|
408
|
+
)
|
|
409
|
+
return None, since, registered, all_dispatches, all_failures
|
|
410
|
+
|
|
411
|
+
|
|
412
|
+
def evaluate(cwd, before_ts: str, window_seconds: int, subject_hash: str) -> LedgerEvidence:
|
|
413
|
+
"""Single-read-pass corroboration evaluation — the primary entry point.
|
|
414
|
+
|
|
415
|
+
`before_ts` anchors the recency window (typically the gate file's own
|
|
416
|
+
mtime, converted via `mtime_to_iso`); qualifying dispatches must fall in
|
|
417
|
+
`(before_ts - window_seconds, before_ts]`, inclusive of both bounds via
|
|
418
|
+
`metrics_query.filter_entries`'s `since`/`until` semantics.
|
|
419
|
+
|
|
420
|
+
`subject_hash` (#1461 security review) is `.review-passed`'s own
|
|
421
|
+
`review_gate_hash()` value — required, not optional. Dispatch events are
|
|
422
|
+
stamped with the `review_gate_hash()` value in effect at dispatch time
|
|
423
|
+
(`agent_dispatch_ledger.py`); only events whose `subject_hash` matches
|
|
424
|
+
THIS gate's current hash count as evidence. Without this, a genuine
|
|
425
|
+
review of one changeset (file A) would satisfy the gate for an unrelated
|
|
426
|
+
later changeset (file B) staged and self-hashed within the same recency
|
|
427
|
+
window — this binds "a review happened recently" to "a review of THIS
|
|
428
|
+
staged content happened recently". An event missing `subject_hash`
|
|
429
|
+
entirely (e.g. written before this field existed) never matches.
|
|
430
|
+
|
|
431
|
+
Also re-validates each dispatch's `matched_rule` against the LIVE
|
|
432
|
+
registered-agent set (`_registered_agents()`), not just trusting
|
|
433
|
+
whatever the ledger says — defense in depth against a stale or
|
|
434
|
+
hand-edited ledger.
|
|
435
|
+
"""
|
|
436
|
+
failure, since, registered, all_dispatches, all_failures = _load_ledger_pipeline(
|
|
437
|
+
cwd, before_ts, window_seconds
|
|
438
|
+
)
|
|
439
|
+
if failure is not None:
|
|
440
|
+
return LedgerEvidence(frozenset(), False, False, failure, None)
|
|
441
|
+
|
|
442
|
+
# `any_ever` intentionally reads the UNFILTERED dispatch set (#1461
|
|
443
|
+
# security review) — it means "a genuine dispatch exists somewhere in
|
|
444
|
+
# the ledger, for ANY subject", which is what `_STALE_MESSAGE` vs
|
|
445
|
+
# `_NO_DISPATCH_MESSAGE` needs to distinguish. Computing it after the
|
|
446
|
+
# subject_hash narrowing below would silently redefine it to "a
|
|
447
|
+
# dispatch for THIS exact hash exists somewhere" — collapsing the
|
|
448
|
+
# common, legitimate case (a real review of slightly different staged
|
|
449
|
+
# content) into the same "no dispatch ever" message a genuinely
|
|
450
|
+
# unreviewed changeset gets.
|
|
451
|
+
any_ever = any(isinstance(e.get("matched_rule"), str) for e in all_dispatches)
|
|
452
|
+
dispatches = [e for e in all_dispatches if e.get("subject_hash") == subject_hash]
|
|
453
|
+
# Same-subject existence, independent of the recency window (#1461
|
|
454
|
+
# second security re-review) — see the LedgerEvidence docstring for why
|
|
455
|
+
# this must be tracked separately from `any_ever`.
|
|
456
|
+
same_subject_ever = any(isinstance(e.get("matched_rule"), str) for e in dispatches)
|
|
457
|
+
|
|
458
|
+
# Dispatch-failure negative evidence (#1763) — unbounded by the recency
|
|
459
|
+
# window, per the field's own docstring. `dispatches` above is already
|
|
460
|
+
# narrowed to THIS subject_hash and carries every qualifying "record"
|
|
461
|
+
# regardless of age, so it doubles as the "records" side of the
|
|
462
|
+
# supersession comparison with no extra filtering needed.
|
|
463
|
+
same_subject_failures = [e for e in all_failures if e.get("subject_hash") == subject_hash]
|
|
464
|
+
agents, dispatch_failure_agents = _binding_evidence(
|
|
465
|
+
dispatches, same_subject_failures, since, before_ts, registered
|
|
466
|
+
)
|
|
467
|
+
|
|
468
|
+
return LedgerEvidence(agents, any_ever, same_subject_ever, None, dispatch_failure_agents)
|
|
469
|
+
|
|
470
|
+
|
|
471
|
+
def _has_exemption(cwd, before_ts: str, window_seconds: int, subject_hash: str, rule: str) -> bool:
|
|
472
|
+
entries, failure = _read_ledger(cwd)
|
|
473
|
+
if failure is not None:
|
|
474
|
+
return False
|
|
475
|
+
since = _since_bound(before_ts, window_seconds)
|
|
476
|
+
matched = metrics_query.filter_entries(
|
|
477
|
+
entries,
|
|
478
|
+
event_type=_DOC_ONLY_HOOK,
|
|
479
|
+
gate_outcome=_DOC_ONLY_DECISION,
|
|
480
|
+
since=since,
|
|
481
|
+
until=before_ts,
|
|
482
|
+
)
|
|
483
|
+
return any(
|
|
484
|
+
e.get("matched_rule") == rule and e.get("subject_hash") == subject_hash for e in matched
|
|
485
|
+
)
|
|
486
|
+
|
|
487
|
+
|
|
488
|
+
def has_doc_only_exemption(cwd, before_ts: str, window_seconds: int, subject_hash: str) -> bool:
|
|
489
|
+
"""True if the doc-only short-circuit's `"doc-only-review-exempt"`
|
|
490
|
+
bypass event was recorded inside the recency window before `before_ts`,
|
|
491
|
+
bound to `subject_hash` — the doc-only path's auditable alternative to
|
|
492
|
+
dispatch-ledger evidence (#1461). Fails CLOSED like every other read in
|
|
493
|
+
this module: any read failure returns False, same as "no exemption
|
|
494
|
+
found". The `subject_hash` requirement means an exemption emitted for
|
|
495
|
+
one changeset cannot be replayed to pass the gate for a different one.
|
|
496
|
+
"""
|
|
497
|
+
return _has_exemption(cwd, before_ts, window_seconds, subject_hash, _DOC_ONLY_RULE)
|
|
498
|
+
|
|
499
|
+
|
|
500
|
+
def has_single_agent_exemption(cwd, before_ts: str, window_seconds: int, subject_hash: str) -> bool:
|
|
501
|
+
"""True if `--agent <name>`'s `"single-agent-review-exempt"` bypass
|
|
502
|
+
event was recorded inside the recency window before `before_ts`, bound
|
|
503
|
+
to `subject_hash` (#1461) — a sanctioned single-agent `/code-review`
|
|
504
|
+
run only ever dispatches 1 distinct agent. Historically this could never
|
|
505
|
+
clear the (then-2) distinct-dispatch floor on its own, so this exemption
|
|
506
|
+
existed to keep that documented workflow from regressing to an
|
|
507
|
+
always-blocked gate; #2147 lowered the floor to 1, so a single dispatch
|
|
508
|
+
now clears it unaided and this exemption is no longer load-bearing for
|
|
509
|
+
that case — kept as an explicit, auditable alternate path rather than
|
|
510
|
+
removed. Fails CLOSED like every other read in this module.
|
|
511
|
+
"""
|
|
512
|
+
return _has_exemption(cwd, before_ts, window_seconds, subject_hash, _SINGLE_AGENT_RULE)
|
|
513
|
+
|
|
514
|
+
|
|
515
|
+
__all__ = (
|
|
516
|
+
"LedgerEvidence",
|
|
517
|
+
"evaluate",
|
|
518
|
+
"has_doc_only_exemption",
|
|
519
|
+
"has_single_agent_exemption",
|
|
520
|
+
"mtime_to_iso",
|
|
521
|
+
)
|