@event4u/agent-config 14.12.0 → 14.14.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/.augment-plugin/marketplace.json +3 -3
- package/.augment-plugin/plugin.json +1 -1
- package/.claude-plugin/marketplace.json +1 -1
- package/CHANGELOG.md +118 -155
- package/CONTRIBUTING.md +38 -0
- package/CREDITS.md +135 -0
- package/NOTICE +13 -0
- package/README.md +8 -6
- package/dist/agent-src/commands/analyze/inbox.md +145 -2
- package/dist/agent-src/commands/feature/dev.md +7 -2
- package/dist/agent-src/commands/ghostwriter/write.md +5 -1
- package/dist/agent-src/commands/implement-ticket.md +1 -1
- package/dist/agent-src/commands/mission/upgrade.md +1 -1
- package/dist/agent-src/commands/orchestrate.md +2 -2
- package/dist/agent-src/commands/post-as/me.md +5 -1
- package/dist/agent-src/commands/pr/merge.md +19 -6
- package/dist/agent-src/commands/roadmap/process-full.md +19 -5
- package/dist/agent-src/commands/tdd/red.md +12 -2
- package/dist/agent-src/commands/work.md +1 -1
- package/dist/agent-src/contexts/communication/rules-auto/token-efficiency-mechanics.md +40 -0
- package/dist/agent-src/contexts/execution/auto-dispatch-classification.md +5 -2
- package/dist/agent-src/contexts/execution/autonomy-mechanics.md +21 -0
- package/dist/agent-src/contexts/execution/orchestration-telemetry.md +1 -0
- package/dist/agent-src/contexts/execution/subagent-response-contract.md +40 -0
- package/dist/agent-src/contexts/execution/subagent-steering.md +13 -1
- package/dist/agent-src/contexts/execution/terminal-states.md +96 -0
- package/dist/agent-src/rules/artifact-drafting-protocol.md +1 -1
- package/dist/agent-src/rules/broken-access-control.md +4 -0
- package/dist/agent-src/rules/code-comment-discipline.md +6 -0
- package/dist/agent-src/rules/code-provenance.md +4 -0
- package/dist/agent-src/rules/design-fidelity.md +1 -1
- package/dist/agent-src/rules/domain-safety-pii.md +4 -0
- package/dist/agent-src/rules/domain-safety-retention.md +4 -0
- package/dist/agent-src/rules/engineering-safety-floor.md +2 -0
- package/dist/agent-src/rules/external-code-graph-interop.md +14 -12
- package/dist/agent-src/rules/improve-before-implement.md +6 -8
- package/dist/agent-src/rules/laravel-routing.md +1 -1
- package/dist/agent-src/rules/lethal-trifecta-guard.md +4 -0
- package/dist/agent-src/rules/secret-vcs-guard.md +2 -0
- package/dist/agent-src/rules/security-sensitive-stop.md +2 -0
- package/dist/agent-src/rules/source-confidentiality.md +12 -34
- package/dist/agent-src/rules/symfony-routing.md +1 -1
- package/dist/agent-src/rules/token-efficiency.md +14 -0
- package/dist/agent-src/rules/tool-safety.md +4 -0
- package/dist/agent-src/rules/untrusted-input-defense.md +4 -0
- package/dist/agent-src/scripts/archive_completed_roadmaps.ts +252 -8
- package/dist/agent-src/scripts/guarded_baseline.ts +343 -0
- package/dist/agent-src/scripts/roadmap_set_step.ts +253 -0
- package/dist/agent-src/scripts/stub_queue.ts +82 -0
- package/dist/agent-src/scripts/stubs_due.ts +516 -0
- package/dist/agent-src/scripts/ticket_bundles.ts +84 -0
- package/dist/agent-src/scripts/update_roadmap_progress.ts +16 -12
- package/dist/agent-src/skills/brand-asset-generation/SKILL.md +17 -4
- package/dist/agent-src/skills/brand-audit/SKILL.md +33 -0
- package/dist/agent-src/skills/code-intelligence/SKILL.md +57 -0
- package/dist/agent-src/skills/database/SKILL.md +8 -0
- package/dist/agent-src/skills/dependency-upgrade/SKILL.md +27 -0
- package/dist/agent-src/skills/design-review/SKILL.md +1 -1
- package/dist/agent-src/skills/doc-coauthoring/SKILL.md +1 -1
- package/dist/agent-src/skills/existing-ui-audit/SKILL.md +17 -1
- package/dist/agent-src/skills/feature-planning/SKILL.md +9 -2
- package/dist/agent-src/skills/frontend-render-security/SKILL.md +44 -0
- package/dist/agent-src/skills/git-workflow/SKILL.md +1 -1
- package/dist/agent-src/skills/js-library-packaging/scripts/check_package_surface.ts +38 -4
- package/dist/agent-src/skills/judge-artifact-completeness/rubrics/ticket-quality-score.json +2 -2
- package/dist/agent-src/skills/laravel/SKILL.md +35 -0
- package/dist/agent-src/skills/laravel-migration/SKILL.md +57 -3
- package/dist/agent-src/skills/monorepo-workspace/SKILL.md +26 -0
- package/dist/agent-src/skills/playbook-authoring/SKILL.md +28 -1
- package/dist/agent-src/skills/react-shadcn-ui/SKILL.md +1 -1
- package/dist/agent-src/skills/skill-writing/references/procedure.md +5 -5
- package/dist/agent-src/skills/sql-writing/SKILL.md +7 -1
- package/dist/agent-src/skills/standards-from-config/SKILL.md +27 -4
- package/dist/agent-src/skills/supply-chain-intake/SKILL.md +11 -4
- package/dist/agent-src/skills/symfony-workflow/SKILL.md +35 -0
- package/dist/agent-src/skills/test-case-discovery/SKILL.md +85 -0
- package/dist/agent-src/skills/test-driven-development/SKILL.md +33 -9
- package/dist/agent-src/skills/testing-anti-patterns/SKILL.md +1 -1
- package/dist/agent-src/skills/testing-anti-patterns/process-anti-patterns.md +37 -0
- package/dist/agent-src/skills/ui-component-architect/SKILL.md +45 -2
- package/dist/agent-src/skills/upstream-contribute/SKILL.md +38 -1
- package/dist/agent-src/skills/verify-repair-loop/SKILL.md +9 -4
- package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
- package/dist/agent-src/templates/roadmaps.md +97 -3
- package/dist/agent-src/templates/scripts/check_memory.ts +258 -17
- package/dist/agent-src/templates/scripts/check_memory_proposal.ts +9 -3
- package/dist/agent-src/templates/scripts/memory_lookup.ts +86 -0
- package/dist/agent-src/templates/scripts/memory_signal.ts +44 -0
- package/dist/agent-src/templates/scripts/work_engine/directives/backend/implement.ts +90 -0
- package/dist/agent-src/templates/scripts/work_engine/directives/backend/index.ts +11 -0
- package/dist/agent-src/templates/scripts/work_engine/directives/backend/test.ts +23 -1
- package/dist/agent-src/templates/scripts/work_engine/directives/backend/verify.ts +10 -1
- package/dist/agent-src/templates/scripts/work_engine/directives/mixed/stitch.ts +5 -1
- package/dist/agent-src/templates/scripts/work_engine/stack/runner.ts +102 -14
- package/dist/cli/commands/uiAudit.js +124 -5
- package/dist/cli/registry.js +2 -0
- package/dist/cli-delegate/{chunk-HYWSRNUX.js → chunk-2DK2VJWG.js} +3 -3
- package/dist/cli-delegate/{chunk-JCW7ZLHK.js → chunk-4FS4WRXP.js} +5 -0
- package/dist/cli-delegate/{chunk-OMIVVCG2.js → chunk-A4QJ6ENT.js} +24 -0
- package/dist/cli-delegate/{chunk-JJA2QEWA.js → chunk-AKJGUQNO.js} +466 -285
- package/dist/cli-delegate/{chunk-E46KPN6K.js → chunk-BTWUYDGN.js} +424 -363
- package/dist/cli-delegate/{chunk-K4FFODTS.js → chunk-ILMUPLGW.js} +1227 -316
- package/dist/cli-delegate/{chunk-FF7WAFMR.js → chunk-KPBWFOPZ.js} +5 -1
- package/dist/cli-delegate/cmd_adr_effective.js +102 -0
- package/dist/cli-delegate/cmd_conformance.js +80 -11
- package/dist/cli-delegate/cmd_doctor.js +3 -3
- package/dist/cli-delegate/cmd_export.js +3 -2
- package/dist/cli-delegate/cmd_fleet.js +2 -2
- package/dist/cli-delegate/cmd_handoff.js +1 -1
- package/dist/cli-delegate/cmd_preflight.js +2 -2
- package/dist/cli-delegate/cmd_prune.js +3 -2
- package/dist/cli-delegate/cmd_settings_get.js +1 -1
- package/dist/cli-delegate/cmd_settings_migrate.js +3 -2
- package/dist/cli-delegate/cmd_settings_set.js +2 -2
- package/dist/cli-delegate/cmd_sync.js +3 -2
- package/dist/cli-delegate/cmd_uninstall.js +3 -2
- package/dist/cli-delegate/cmd_validate.js +3 -2
- package/dist/discovery/deprecation-report.md +1 -1
- package/dist/discovery/discovery-manifest.json +58 -58
- package/dist/discovery/discovery-manifest.json.sha256 +1 -1
- package/dist/discovery/discovery-manifest.summary.md +1 -1
- package/dist/discovery/dormancy-report.md +1 -1
- package/dist/discovery/orphan-report.md +1 -1
- package/dist/discovery/packs.json +2 -2
- package/dist/discovery/trust-report.md +1 -1
- package/dist/discovery/workspaces.json +2 -2
- package/dist/hooks/dispatch.js +2900 -1049
- package/dist/install/claudeRuleRewrite.js +254 -0
- package/dist/install/detect.js +29 -16
- package/dist/install/detect_php_shape.js +159 -0
- package/dist/install/install.mjs +634 -428
- package/dist/mcp/awesome-mcp-servers.row.md +1 -1
- package/dist/mcp/mcp-cloudflare-catalogue.json +1 -1
- package/dist/mcp/registry-manifest.json +3 -3
- package/dist/mcp/server.json +3 -3
- package/dist/mcp/server.mjs +4895 -2627
- package/dist/server/schemas/settings.js +3 -0
- package/dist/ui/assets/index-D0jaCRPM.js +29 -0
- package/dist/ui/index.html +1 -1
- package/docs/MIGRATION.md +1 -1
- package/docs/THIRD-PARTY-NOTICES.md +25 -0
- package/docs/contracts/provider-lifecycle.md +1 -1
- package/docs/contracts/settings-classes.md +10 -4
- package/docs/guidelines/agent-infra/5w2h-analysis.md +3 -3
- package/docs/guidelines/agent-infra/agent-interaction-and-decision-quality.md +1 -0
- package/docs/guidelines/agent-infra/artifact-drafting-protocol-mechanics.md +58 -3
- package/docs/guidelines/agent-infra/ask-when-uncertain-demos.md +2 -2
- package/docs/guidelines/agent-infra/asking-and-brevity-examples.md +3 -3
- package/docs/guidelines/agent-infra/break-glass-usage.md +7 -7
- package/docs/guidelines/agent-infra/carve-out-predicates.md +3 -3
- package/docs/guidelines/agent-infra/comparison-matrix.md +1 -1
- package/docs/guidelines/agent-infra/critical-thinking.md +4 -4
- package/docs/guidelines/agent-infra/direct-answers-demos.md +2 -2
- package/docs/guidelines/agent-infra/domain-eval-anti-pattern.md +1 -1
- package/docs/guidelines/agent-infra/engineering-memory-data-format.md +3 -3
- package/docs/guidelines/agent-infra/existence-question-verdicts.md +80 -0
- package/docs/guidelines/agent-infra/first-principles.md +2 -2
- package/docs/guidelines/agent-infra/framework-neutrality-patterns.md +2 -2
- package/docs/guidelines/agent-infra/guarded-baseline.md +39 -0
- package/docs/guidelines/agent-infra/inversion-thinking.md +5 -5
- package/docs/guidelines/agent-infra/language-and-tone-examples.md +1 -1
- package/docs/guidelines/agent-infra/layered-settings.md +10 -10
- package/docs/guidelines/agent-infra/memory-access.md +1 -1
- package/docs/guidelines/agent-infra/mental-models.md +3 -3
- package/docs/guidelines/agent-infra/reuse-verdict-mechanics.md +71 -0
- package/docs/guidelines/agent-infra/review-routing-data-format.md +7 -7
- package/docs/guidelines/agent-infra/roadmap-progress-mechanics.md +13 -7
- package/docs/guidelines/agent-infra/role-contracts.md +2 -2
- package/docs/guidelines/agent-infra/role-mode-router.md +3 -3
- package/docs/guidelines/agent-infra/rule-type-governance.md +2 -2
- package/docs/guidelines/agent-infra/scqa-framework.md +5 -5
- package/docs/guidelines/agent-infra/self-improvement-pipeline.md +6 -6
- package/docs/guidelines/agent-infra/simplicity-and-goal-demos.md +2 -2
- package/docs/guidelines/agent-infra/six-hats.md +3 -3
- package/docs/guidelines/agent-infra/skill-quality-checklist.md +6 -6
- package/docs/guidelines/agent-infra/source-confidentiality-mechanics.md +109 -0
- package/docs/guidelines/agent-infra/systems-thinking.md +1 -1
- package/docs/guidelines/agent-infra/verify-before-complete-demos.md +2 -2
- package/docs/guidelines/augment-portability-patterns.md +6 -4
- package/docs/guidelines/code-clarity.md +18 -0
- package/docs/guidelines/component-oriented-and-oop-development.md +3 -2
- package/docs/guidelines/cross-role-handoff.md +2 -2
- package/docs/guidelines/design-fidelity-mechanics.md +1 -1
- package/docs/guidelines/docs/readme-size-and-splitting.md +3 -3
- package/docs/guidelines/php/database.md +90 -7
- package/docs/guidelines/prompt-templates.md +7 -7
- package/llms.txt +2 -2
- package/package.json +5 -2
- package/src/agent-src/scripts/archive_completed_roadmaps.ts +252 -8
- package/src/agent-src/scripts/guarded_baseline.ts +343 -0
- package/src/agent-src/scripts/roadmap_set_step.ts +253 -0
- package/src/agent-src/scripts/stub_queue.ts +82 -0
- package/src/agent-src/scripts/stubs_due.ts +516 -0
- package/src/agent-src/scripts/ticket_bundles.ts +84 -0
- package/src/agent-src/scripts/update_roadmap_progress.ts +16 -12
- package/src/agent-src/templates/scripts/check_memory.ts +258 -17
- package/src/agent-src/templates/scripts/check_memory_proposal.ts +9 -3
- package/src/agent-src/templates/scripts/memory_lookup.ts +86 -0
- package/src/agent-src/templates/scripts/memory_signal.ts +44 -0
- package/src/agent-src/templates/scripts/work_engine/directives/backend/implement.ts +90 -0
- package/src/agent-src/templates/scripts/work_engine/directives/backend/index.ts +11 -0
- package/src/agent-src/templates/scripts/work_engine/directives/backend/test.ts +23 -1
- package/src/agent-src/templates/scripts/work_engine/directives/backend/verify.ts +10 -1
- package/src/agent-src/templates/scripts/work_engine/directives/mixed/stitch.ts +5 -1
- package/src/agent-src/templates/scripts/work_engine/stack/runner.ts +102 -14
- package/src/config/agent-settings.template.yml +32 -3
- package/src/config/assurance-capability-registry.json +21 -9
- package/src/config/canonical-terms.yml +119 -0
- package/src/config/capability-native-outcome-bars.json +81 -0
- package/src/config/ci-local-parity.yml +80 -0
- package/src/config/drafting-channels.yml +43 -0
- package/src/config/estate-count-budget.json +57 -36
- package/src/config/evaluator-budgets.json +14 -14
- package/src/config/gate-coverage.yml +729 -63
- package/src/config/gate-reachability-exemptions.json +21 -0
- package/src/config/gate-violation-baselines.json +42 -19
- package/src/config/harness-evolution-budget.json +25 -0
- package/src/config/lapsed-beta-baseline.json +96 -0
- package/src/config/memory-twin-verdicts.yml +129 -0
- package/src/config/metric-registry.yml +124 -0
- package/src/config/pack-size-budget.json +16 -15
- package/src/config/preamble-payload-budget.json +12 -4
- package/src/config/profiles/balanced.ini +27 -0
- package/src/config/profiles/full.ini +27 -0
- package/src/config/profiles/minimal.ini +27 -0
- package/src/config/publish-surface.json +9 -2
- package/src/config/routing-coverage-seed.json +16 -2
- package/src/config/rule-stub-ceilings.json +133 -112
- package/src/config/turnaround-budget.json +66 -0
- package/src/install/claudeRuleRewrite.ts +288 -0
- package/src/install/detect.ts +60 -17
- package/src/install/detect_php_shape.ts +191 -0
- package/src/scripts/_cli/cmd_doctor.ts +27 -112
- package/src/scripts/_dispatch.bash +23 -0
- package/src/scripts/_lib/activation_ladder.ts +255 -0
- package/src/scripts/_lib/activation_receipt_producer.ts +341 -0
- package/src/scripts/_lib/activation_states.ts +88 -0
- package/src/scripts/_lib/adherence_detectors.ts +143 -0
- package/src/scripts/_lib/adr_frontmatter.ts +26 -0
- package/src/scripts/_lib/arith_claims.ts +181 -0
- package/src/scripts/_lib/arm_ranking.ts +62 -0
- package/src/scripts/_lib/asset_delivery_ledger.ts +303 -0
- package/src/scripts/_lib/audit_field_provenance.ts +54 -0
- package/src/scripts/_lib/auto_dispatch.ts +32 -3
- package/src/scripts/_lib/bench_ab_size_claim.ts +98 -4
- package/src/scripts/_lib/benchmark_cache_fields.ts +158 -0
- package/src/scripts/_lib/candidate_pair_delta.ts +349 -0
- package/src/scripts/_lib/candidate_proposer.ts +402 -0
- package/src/scripts/_lib/candidate_record.ts +601 -0
- package/src/scripts/_lib/capture_rate.ts +319 -0
- package/src/scripts/_lib/carrier_status.ts +118 -0
- package/src/scripts/_lib/cascade_stage_enumeration.ts +92 -0
- package/src/scripts/_lib/collector_denominator.ts +771 -0
- package/src/scripts/_lib/collector_record.ts +340 -0
- package/src/scripts/_lib/collector_store.ts +664 -0
- package/src/scripts/_lib/collector_supervision.ts +827 -0
- package/src/scripts/_lib/concern_estate.ts +77 -0
- package/src/scripts/_lib/config_chain.ts +477 -0
- package/src/scripts/_lib/config_cost.ts +209 -0
- package/src/scripts/_lib/corpus_manifest.ts +726 -0
- package/src/scripts/_lib/council_settings_block.ts +6 -0
- package/src/scripts/_lib/curator_ops.ts +172 -0
- package/src/scripts/_lib/delivery_arm_experiment.ts +282 -0
- package/src/scripts/_lib/delivery_criticality.ts +126 -0
- package/src/scripts/_lib/doctor_runtime_checks.ts +70 -0
- package/src/scripts/_lib/empty_cycles.ts +88 -0
- package/src/scripts/_lib/estate_carrier_cases.ts +72 -0
- package/src/scripts/_lib/eval_publication.ts +354 -0
- package/src/scripts/_lib/evaluation_cascade.ts +452 -0
- package/src/scripts/_lib/evaluation_vector.ts +283 -0
- package/src/scripts/_lib/evaluator_promotion.ts +225 -0
- package/src/scripts/_lib/evidence_basis.ts +118 -0
- package/src/scripts/_lib/evolution_roi.ts +562 -0
- package/src/scripts/_lib/exec_evidence.ts +8 -0
- package/src/scripts/_lib/experience_card.ts +272 -0
- package/src/scripts/_lib/experience_report.ts +151 -0
- package/src/scripts/_lib/experiment_freeze.ts +112 -0
- package/src/scripts/_lib/generated_by.ts +66 -0
- package/src/scripts/_lib/harness_evolution_guards.ts +239 -0
- package/src/scripts/_lib/hook_effect_probe.ts +141 -0
- package/src/scripts/_lib/host_denominator.ts +687 -0
- package/src/scripts/_lib/host_projection_reach.ts +157 -0
- package/src/scripts/_lib/ignored_blocker.ts +148 -0
- package/src/scripts/_lib/install_reach_checks.ts +495 -0
- package/src/scripts/_lib/json_duplicate_keys.ts +150 -0
- package/src/scripts/_lib/judge_hygiene.ts +228 -0
- package/src/scripts/_lib/legacy_boundary_map.ts +228 -0
- package/src/scripts/_lib/lexical_shortlist.ts +215 -0
- package/src/scripts/_lib/llm_candidate_proposer.ts +461 -0
- package/src/scripts/_lib/llm_proposer_transport.ts +153 -0
- package/src/scripts/_lib/loop_guards.ts +299 -0
- package/src/scripts/_lib/loss_class.ts +129 -0
- package/src/scripts/_lib/machine_wake.ts +51 -0
- package/src/scripts/_lib/md_prose_lines.ts +177 -0
- package/src/scripts/_lib/minimality_tiebreak.ts +168 -0
- package/src/scripts/_lib/one_resolver_invariant.ts +449 -0
- package/src/scripts/_lib/orchestration_record.ts +253 -5
- package/src/scripts/_lib/outcome_envelope.ts +227 -0
- package/src/scripts/_lib/outcome_vocabularies.ts +125 -0
- package/src/scripts/_lib/paired_stats.ts +245 -0
- package/src/scripts/_lib/paired_verdict.ts +226 -0
- package/src/scripts/_lib/pathology_archive.ts +393 -0
- package/src/scripts/_lib/prefix_stable_surfaces.ts +123 -0
- package/src/scripts/_lib/privacy_class.ts +47 -0
- package/src/scripts/_lib/promotion_capability.ts +315 -0
- package/src/scripts/_lib/promotion_evidence.ts +414 -0
- package/src/scripts/_lib/promotion_review.ts +294 -0
- package/src/scripts/_lib/prompt_shape.ts +92 -0
- package/src/scripts/_lib/regression_neighbourhood.ts +259 -0
- package/src/scripts/_lib/release_highlights.ts +70 -1
- package/src/scripts/_lib/release_material.ts +125 -2
- package/src/scripts/_lib/repeated_failure.ts +105 -0
- package/src/scripts/_lib/repo_root.ts +111 -0
- package/src/scripts/_lib/review_skipped_record.ts +37 -0
- package/src/scripts/_lib/role_split.ts +338 -0
- package/src/scripts/_lib/routing_corpus.ts +307 -0
- package/src/scripts/_lib/routing_index_input.ts +104 -0
- package/src/scripts/_lib/rule_injection.ts +22 -1
- package/src/scripts/_lib/runtime_journal.ts +1499 -0
- package/src/scripts/_lib/runtime_wiring_checks.ts +430 -0
- package/src/scripts/_lib/semantic_noop.ts +167 -0
- package/src/scripts/_lib/skill_catalogue.ts +11 -1
- package/src/scripts/_lib/source_digest.ts +200 -0
- package/src/scripts/_lib/source_redact.ts +260 -0
- package/src/scripts/_lib/source_shape.ts +502 -0
- package/src/scripts/_lib/source_snapshot_dedup.ts +199 -0
- package/src/scripts/_lib/strip_comments.d.mts +8 -0
- package/src/scripts/_lib/strip_comments.mjs +65 -0
- package/src/scripts/_lib/test_red_state.ts +154 -0
- package/src/scripts/_lib/trigger_shift.ts +144 -0
- package/src/scripts/_lib/untrusted_content.ts +82 -7
- package/src/scripts/activation_receipt.ts +258 -0
- package/src/scripts/adoption_report.ts +1 -1
- package/src/scripts/adr/evidence_census.ts +1 -1
- package/src/scripts/adr/regenerate_index.ts +26 -2
- package/src/scripts/adr_cite_check.ts +164 -1
- package/src/scripts/ai_council/blind_review.ts +11 -1
- package/src/scripts/ai_council/clients.ts +4 -0
- package/src/scripts/ai_council/config.ts +3 -0
- package/src/scripts/ai_council/consensus.ts +28 -8
- package/src/scripts/ai_council/consensus_round.ts +226 -0
- package/src/scripts/ai_council/cross_exam.ts +128 -0
- package/src/scripts/ai_council/disagreement_signal.ts +338 -0
- package/src/scripts/ai_council/explain_route.ts +157 -0
- package/src/scripts/ai_council/information_gain.ts +293 -0
- package/src/scripts/ai_council/inline_findings.ts +340 -0
- package/src/scripts/ai_council/judge_position_bias.ts +197 -0
- package/src/scripts/ai_council/leakage_corpus.ts +385 -0
- package/src/scripts/ai_council/leakage_patterns.ts +453 -0
- package/src/scripts/ai_council/minority_retention.ts +171 -0
- package/src/scripts/ai_council/orchestrator.ts +16 -203
- package/src/scripts/ai_council/orchestrator_results.ts +23 -3
- package/src/scripts/ai_council/pricing.ts +106 -0
- package/src/scripts/ai_council/prompts.ts +74 -7
- package/src/scripts/ai_council/provider_leakage_bench.ts +327 -0
- package/src/scripts/ai_council/py_format.ts +14 -3
- package/src/scripts/ai_council/recouncil_guard.ts +293 -0
- package/src/scripts/ai_council/recouncil_savings.ts +299 -0
- package/src/scripts/ai_council/replay_route.ts +223 -0
- package/src/scripts/ai_council/response_record.ts +50 -0
- package/src/scripts/ai_council/reviewer_assignment.ts +279 -0
- package/src/scripts/ai_council/routing_training_row.ts +197 -0
- package/src/scripts/ai_council/synthesis_strategy.ts +159 -0
- package/src/scripts/ai_council/topology_bench_manifest.ts +860 -0
- package/src/scripts/ai_council/topology_vocabulary.ts +175 -0
- package/src/scripts/asset_delivery_ledger.ts +157 -0
- package/src/scripts/attest_artifact.ts +193 -0
- package/src/scripts/audit_auto_rules.ts +2 -1
- package/src/scripts/audit_command_surface.ts +1 -1
- package/src/scripts/audit_mcp_tools.ts +2 -2
- package/src/scripts/audit_skill_overlap_allowlist.json +6 -1
- package/src/scripts/audit_user_type_axis.ts +2 -1
- package/src/scripts/bench_ab_clone.ts +215 -10
- package/src/scripts/bench_ab_integrity.ts +119 -5
- package/src/scripts/bench_ab_v2_stats.ts +40 -213
- package/src/scripts/build_archive_index.ts +1 -1
- package/src/scripts/build_linear_digest.ts +1 -1
- package/src/scripts/build_proof.ts +11 -4
- package/src/scripts/build_rule_trigger_matrix.ts +1 -1
- package/src/scripts/build_source_digests.ts +157 -0
- package/src/scripts/cache_realization_report.ts +103 -0
- package/src/scripts/check_adr_frontmatter.ts +131 -10
- package/src/scripts/check_benchmark_report_fields.ts +207 -0
- package/src/scripts/check_beta_review_markers.ts +235 -6
- package/src/scripts/check_bundle_path_leakage.ts +34 -1
- package/src/scripts/check_claims.ts +576 -6
- package/src/scripts/check_composite_arming.ts +35 -0
- package/src/scripts/check_concern_admissions.ts +365 -0
- package/src/scripts/check_enforcement_coverage.ts +28 -0
- package/src/scripts/check_estate_count.ts +347 -25
- package/src/scripts/check_gate_reachability.ts +383 -0
- package/src/scripts/check_host_loadability.ts +104 -0
- package/src/scripts/check_loss_class_declared.ts +308 -0
- package/src/scripts/check_md_language.ts +15 -98
- package/src/scripts/check_memory_proposal.ts +18 -11
- package/src/scripts/check_no_currency_in_cost_surfaces.ts +225 -0
- package/src/scripts/check_no_external_sources.ts +281 -6
- package/src/scripts/check_pack_conformance_fixture.ts +11 -1
- package/src/scripts/check_pack_size.ts +59 -2
- package/src/scripts/check_preamble_payload_budget.ts +114 -3
- package/src/scripts/check_prefix_stable_mutation.ts +372 -0
- package/src/scripts/check_publish_surface.ts +10 -1
- package/src/scripts/check_references.ts +24 -1
- package/src/scripts/check_release_highlights.ts +96 -13
- package/src/scripts/check_roadmap_trackable.ts +17 -2
- package/src/scripts/check_rule_evidence_declaration.ts +316 -0
- package/src/scripts/check_rule_projection_integrity.ts +14 -4
- package/src/scripts/check_score_contract.ts +26 -3
- package/src/scripts/check_static_parity.ts +285 -0
- package/src/scripts/check_supervision_claim_atomicity.ts +498 -0
- package/src/scripts/check_suppression_hygiene.ts +15 -0
- package/src/scripts/ci_settle.ts +39 -2
- package/src/scripts/code_graph/build.ts +60 -3
- package/src/scripts/code_graph/extract.ts +44 -0
- package/src/scripts/code_graph/types.ts +24 -0
- package/src/scripts/collector_daemon.ts +714 -0
- package/src/scripts/config_cost_report.ts +302 -0
- package/src/scripts/consumer_matrix.ts +74 -1
- package/src/scripts/corpus_manifest.ts +235 -0
- package/src/scripts/cost/budget.mjs +10 -3
- package/src/scripts/cost/track.mjs +10 -3
- package/src/scripts/council_cli.ts +37 -54
- package/src/scripts/dispatch_r2_reviewer.ts +46 -5
- package/src/scripts/evolution_lab.ts +1153 -0
- package/src/scripts/external_sources_denylist.json +5 -18
- package/src/scripts/extract_audit_patterns.ts +21 -0
- package/src/scripts/fold_intake.ts +12 -1
- package/src/scripts/gen_discovery_baseline.ts +1 -1
- package/src/scripts/generate_capabilities_index.ts +12 -5
- package/src/scripts/generate_capability_matrix.ts +3 -3
- package/src/scripts/generate_command_flows.ts +2 -2
- package/src/scripts/generate_cookbook.ts +2 -2
- package/src/scripts/generate_index.ts +8 -1
- package/src/scripts/generate_knowledge_index.ts +2 -1
- package/src/scripts/generate_ownership_matrix.ts +2 -1
- package/src/scripts/generate_pack_manifests.ts +10 -2
- package/src/scripts/generate_role_experiences_catalog.ts +2 -2
- package/src/scripts/generate_settings_reference.ts +1 -1
- package/src/scripts/git_authorization_hook.ts +22 -1
- package/src/scripts/grade_target_readiness.ts +48 -5
- package/src/scripts/hook_effect_doctor.ts +261 -0
- package/src/scripts/hook_manifest.json +1 -1
- package/src/scripts/hook_manifest.yaml +125 -16
- package/src/scripts/hooks/block_speaking_inbox_dir.ts +271 -0
- package/src/scripts/hooks/block_unauthorized_git.ts +18 -0
- package/src/scripts/hooks/comment_discipline_hook.ts +146 -0
- package/src/scripts/hooks/concern_registry.ts +6 -0
- package/src/scripts/hooks/dispatch_hook.ts +32 -0
- package/src/scripts/hooks/journal_record_hook.ts +345 -0
- package/src/scripts/hooks/prepush_metadata_sources.sh +110 -0
- package/src/scripts/hooks/run_continuation_hook.ts +44 -9
- package/src/scripts/hooks/shims/install.sh +61 -0
- package/src/scripts/hooks/shims/php +101 -0
- package/src/scripts/hooks/suggestion_capture_hook.ts +10 -0
- package/src/scripts/hooks/turn_end_gate_hook.ts +9 -1
- package/src/scripts/hot_context_hook.ts +16 -0
- package/src/scripts/install-hooks.sh +20 -5
- package/src/scripts/install.ts +17 -35
- package/src/scripts/inventory_frontmatter.ts +2 -1
- package/src/scripts/language_mirror_hook.ts +14 -2
- package/src/scripts/lint_adr_sweep_routing.ts +12 -0
- package/src/scripts/lint_canonical_terms.ts +604 -0
- package/src/scripts/lint_carrier_integrity.ts +820 -0
- package/src/scripts/lint_code_comments.ts +643 -0
- package/src/scripts/lint_composition_review.ts +619 -0
- package/src/scripts/lint_consolidation_lineage.ts +666 -0
- package/src/scripts/lint_eval_specs.ts +593 -0
- package/src/scripts/lint_hook_manifest.ts +19 -0
- package/src/scripts/lint_host_portability.ts +373 -0
- package/src/scripts/lint_judge_prompt_expectation.ts +27 -1
- package/src/scripts/lint_memory_twin_parity.ts +335 -0
- package/src/scripts/lint_metric_consumers.ts +355 -0
- package/src/scripts/lint_plan_risk_register.ts +39 -19
- package/src/scripts/lint_promotion_paths.ts +779 -0
- package/src/scripts/lint_skill_descriptions.ts +18 -1
- package/src/scripts/lint_skill_frontmatter_safety.ts +58 -0
- package/src/scripts/lint_skill_link_reach.ts +252 -0
- package/src/scripts/lint_skill_trigger_corpus.ts +426 -23
- package/src/scripts/lint_ticket_buildable.ts +17 -1
- package/src/scripts/llm_propose.ts +256 -0
- package/src/scripts/mcp_server/consumer_tool_catalog.json +1 -1
- package/src/scripts/mcp_server/tool_catalog_source.ts +1 -1
- package/src/scripts/measure_delivery_sets.ts +361 -0
- package/src/scripts/measure_docs_dead_links.ts +348 -0
- package/src/scripts/measure_host_capture.ts +461 -0
- package/src/scripts/measure_routing_signal.ts +307 -0
- package/src/scripts/migration_status.ts +3 -3
- package/src/scripts/model_rule_injection.ts +76 -0
- package/src/scripts/orchestration_record.ts +14 -0
- package/src/scripts/preamble_byte_census.ts +139 -10
- package/src/scripts/prepack-check.mjs +4 -1
- package/src/scripts/probe_council_retention.ts +495 -0
- package/src/scripts/probe_turnaround.ts +384 -0
- package/src/scripts/probe_unblocked_ask.ts +279 -0
- package/src/scripts/release.ts +21 -37
- package/src/scripts/release_drill.ts +143 -0
- package/src/scripts/release_env.ts +35 -0
- package/src/scripts/release_publication.ts +84 -0
- package/src/scripts/render_benchmark_md.ts +1 -1
- package/src/scripts/rule_activation_census.ts +70 -9
- package/src/scripts/rule_backlinks.ts +1 -1
- package/src/scripts/run_lifecycle_suite.ts +208 -0
- package/src/scripts/run_skill_evals.ts +40 -0
- package/src/scripts/schemas/delivery-manifest.schema.json +43 -0
- package/src/scripts/schemas/drafting-channels.schema.json +43 -0
- package/src/scripts/schemas/evals.schema.json +24 -1
- package/src/scripts/schemas/rule.schema.json +52 -1
- package/src/scripts/schemas/skill.schema.json +17 -2
- package/src/scripts/skill_overlap.ts +7 -1
- package/src/scripts/skill_tools/run_block_d_eval.ts +8 -1
- package/src/scripts/skill_tools/score_skill_relevance.ts +84 -3
- package/src/scripts/skill_tools/suggest_skill_for_task.ts +8 -1
- package/src/scripts/skill_usage_report.ts +2 -1
- package/src/scripts/stubs_due.ts +211 -0
- package/src/scripts/sweep_source_surfaces.ts +678 -0
- package/src/scripts/sync_pr_branch.ts +3 -3
- package/src/scripts/trigger_eval_grandfather.json +202 -222
- package/src/scripts/utilization_report.ts +22 -2
- package/src/server/schemas/settings.ts +5 -0
- package/src/templates/marketing-copy.yml +1 -1
- package/dist/ui/assets/index-Cxgy63kz.js +0 -26
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Judge hygiene — what an assertion is worth, and what a pass verdict must show.
|
|
3
|
+
*
|
|
4
|
+
* `road-to-skill-ecosystem-eval-integrity` Phase 3, Steps 3–6. Steps 1 and 2
|
|
5
|
+
* (blinding and the order-swap) were already met by
|
|
6
|
+
* `check_quality_regression.evaluatePair`, which judges every pair in both
|
|
7
|
+
* orders and resolves a flip to `inconsistent` rather than to a winner — that
|
|
8
|
+
* is stronger than the step's own "default to a tie", because a flagged tie is
|
|
9
|
+
* still a tie in a denominator while `inconsistent` is its own bucket with its
|
|
10
|
+
* own reported rate.
|
|
11
|
+
*
|
|
12
|
+
* ── Why an assertion that always passes is worse than no assertion ──────────
|
|
13
|
+
* It inflates the treatment pass rate without reflecting any value: both arms
|
|
14
|
+
* satisfy it, so it contributes a guaranteed point to each and narrows nothing.
|
|
15
|
+
* The reverse — an assertion that always fails — is not noise but a broken
|
|
16
|
+
* check or an impossible expectation, and it is worth finding for a different
|
|
17
|
+
* reason. The interesting set is neither: the assertions that pass in treatment
|
|
18
|
+
* and fail in control ARE the measurement, and everything else is overhead
|
|
19
|
+
* masquerading as coverage.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
/** How a rubric item or assertion is phrased, which is what overfitting looks like. */
|
|
23
|
+
export type OverfitShape = 'outcome' | 'technique' | 'vocabulary';
|
|
24
|
+
|
|
25
|
+
export interface OverfitClassification {
|
|
26
|
+
shape: OverfitShape;
|
|
27
|
+
/** Why this shape, in one clause, printable in an advisory report. */
|
|
28
|
+
reason: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Phrases that name HOW rather than WHAT.
|
|
33
|
+
*
|
|
34
|
+
* `technique` is an assertion that prescribes a mechanism — it passes only for
|
|
35
|
+
* an implementation that took the route the author had in mind, so a better
|
|
36
|
+
* solution scores lower. `vocabulary` is narrower and worse: it checks that a
|
|
37
|
+
* particular WORD appears, which any arm can satisfy by saying the word.
|
|
38
|
+
*/
|
|
39
|
+
const TECHNIQUE_MARKERS = [
|
|
40
|
+
'using ',
|
|
41
|
+
'by calling',
|
|
42
|
+
'via ',
|
|
43
|
+
'with the helper',
|
|
44
|
+
'implemented as',
|
|
45
|
+
'should call',
|
|
46
|
+
'must call',
|
|
47
|
+
'invokes',
|
|
48
|
+
];
|
|
49
|
+
|
|
50
|
+
const VOCABULARY_MARKERS = [
|
|
51
|
+
'mentions',
|
|
52
|
+
'contains the word',
|
|
53
|
+
'uses the term',
|
|
54
|
+
'the phrase',
|
|
55
|
+
'says ',
|
|
56
|
+
'literally',
|
|
57
|
+
];
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Classify one criterion by shape. ADVISORY — this gates nothing.
|
|
61
|
+
*
|
|
62
|
+
* Deliberately a keyword heuristic and deliberately never a verdict input. A
|
|
63
|
+
* classifier that decided anything would need to be right, and no phrase list
|
|
64
|
+
* is right about natural language; what it can do honestly is put a shape
|
|
65
|
+
* beside each item so a human reviewing a rubric sees that nine of twelve items
|
|
66
|
+
* check vocabulary. That is a real finding and it costs nothing to be wrong
|
|
67
|
+
* about one of them.
|
|
68
|
+
*/
|
|
69
|
+
export function classifyOverfit(criterion: string): OverfitClassification {
|
|
70
|
+
const c = criterion.toLowerCase();
|
|
71
|
+
for (const m of VOCABULARY_MARKERS) {
|
|
72
|
+
if (c.includes(m)) {
|
|
73
|
+
return {
|
|
74
|
+
shape: 'vocabulary',
|
|
75
|
+
reason: `phrased around wording ("${m.trim()}") — any arm passes by saying the word`,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
for (const m of TECHNIQUE_MARKERS) {
|
|
80
|
+
if (c.includes(m)) {
|
|
81
|
+
return {
|
|
82
|
+
shape: 'technique',
|
|
83
|
+
reason: `prescribes a mechanism ("${m.trim()}") — a better route scores lower`,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return { shape: 'outcome', reason: 'phrased as a result rather than a route or a wording' };
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** One assertion's behaviour across both arms of a paired run. */
|
|
91
|
+
export interface AssertionObservation {
|
|
92
|
+
id: string;
|
|
93
|
+
/** Trials where this assertion passed in the treatment arm. */
|
|
94
|
+
treatment_passes: number;
|
|
95
|
+
/** Trials where it passed in the control arm. */
|
|
96
|
+
control_passes: number;
|
|
97
|
+
trials: number;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export type AssertionVerdict =
|
|
101
|
+
| 'discriminating'
|
|
102
|
+
| 'non-discriminating-always-pass'
|
|
103
|
+
| 'non-discriminating-always-fail'
|
|
104
|
+
| 'inverted'
|
|
105
|
+
| 'partial';
|
|
106
|
+
|
|
107
|
+
export interface AssertionFinding {
|
|
108
|
+
id: string;
|
|
109
|
+
verdict: AssertionVerdict;
|
|
110
|
+
/** The prescribed action, so the report does not stop at a label. */
|
|
111
|
+
action: string;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Classify an assertion by whether it separates the arms.
|
|
116
|
+
*
|
|
117
|
+
* The pruning rule of Step 5 is expressed HERE rather than as prose in a
|
|
118
|
+
* document, because a rule that lives only in a document is applied by whoever
|
|
119
|
+
* remembers it:
|
|
120
|
+
*
|
|
121
|
+
* · always passes in both → REMOVE or replace. It is pure inflation.
|
|
122
|
+
* · always fails in both → INVESTIGATE. A broken check or an impossible bar.
|
|
123
|
+
* · treatment > control → KEEP. This is where the value is.
|
|
124
|
+
* · control > treatment → KEEP and read it. An inverted assertion is a
|
|
125
|
+
* finding about the treatment, not a defect here.
|
|
126
|
+
*/
|
|
127
|
+
export function classifyAssertion(o: AssertionObservation): AssertionFinding {
|
|
128
|
+
if (o.trials <= 0) {
|
|
129
|
+
return {
|
|
130
|
+
id: o.id,
|
|
131
|
+
verdict: 'partial',
|
|
132
|
+
action: 'no trials — the assertion was never exercised, which is not a result',
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
const allT = o.treatment_passes === o.trials;
|
|
136
|
+
const allC = o.control_passes === o.trials;
|
|
137
|
+
const noneT = o.treatment_passes === 0;
|
|
138
|
+
const noneC = o.control_passes === 0;
|
|
139
|
+
if (allT && allC) {
|
|
140
|
+
return {
|
|
141
|
+
id: o.id,
|
|
142
|
+
verdict: 'non-discriminating-always-pass',
|
|
143
|
+
action:
|
|
144
|
+
'remove or replace — it passes in both arms, so it adds a guaranteed point to each ' +
|
|
145
|
+
'and inflates the treatment pass rate without reflecting value',
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
if (noneT && noneC) {
|
|
149
|
+
return {
|
|
150
|
+
id: o.id,
|
|
151
|
+
verdict: 'non-discriminating-always-fail',
|
|
152
|
+
action:
|
|
153
|
+
'investigate — an assertion nothing satisfies is a broken check or an impossible ' +
|
|
154
|
+
'expectation, not a measurement of the treatment',
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
if (o.treatment_passes > o.control_passes) {
|
|
158
|
+
return { id: o.id, verdict: 'discriminating', action: 'keep — this is where the value is' };
|
|
159
|
+
}
|
|
160
|
+
if (o.control_passes > o.treatment_passes) {
|
|
161
|
+
return {
|
|
162
|
+
id: o.id,
|
|
163
|
+
verdict: 'inverted',
|
|
164
|
+
action:
|
|
165
|
+
'keep and read it — the control satisfies this more often, which is a finding about ' +
|
|
166
|
+
'the treatment rather than a defect in the assertion',
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
return {
|
|
170
|
+
id: o.id,
|
|
171
|
+
verdict: 'partial',
|
|
172
|
+
action: 'keep — equal pass counts but not saturated, so it still carries information',
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/** Summary a report can print, with the inflation number named. */
|
|
177
|
+
export interface AssertionAudit {
|
|
178
|
+
findings: readonly AssertionFinding[];
|
|
179
|
+
discriminating: number;
|
|
180
|
+
non_discriminating: number;
|
|
181
|
+
/** Points every arm is guaranteed by always-pass assertions. */
|
|
182
|
+
guaranteed_points: number;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
export function auditAssertions(obs: readonly AssertionObservation[]): AssertionAudit {
|
|
186
|
+
const findings = obs.map(classifyAssertion);
|
|
187
|
+
const nd = findings.filter((f) => f.verdict.startsWith('non-discriminating')).length;
|
|
188
|
+
return {
|
|
189
|
+
findings,
|
|
190
|
+
discriminating: findings.filter((f) => f.verdict === 'discriminating').length,
|
|
191
|
+
non_discriminating: nd,
|
|
192
|
+
guaranteed_points: findings.filter((f) => f.verdict === 'non-discriminating-always-pass').length,
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Does a section carry EVIDENCE, or only the label a pass was looking for?
|
|
198
|
+
*
|
|
199
|
+
* Step 6. The failure it names is specific and common: a section headed with the
|
|
200
|
+
* expected label followed by one vague sentence scores as a pass, because the
|
|
201
|
+
* label being present was mistaken for the substance being present. The check is
|
|
202
|
+
* deliberately crude — a length floor plus a concreteness signal — because the
|
|
203
|
+
* alternative is a judge, and a judge is the thing whose output this is meant to
|
|
204
|
+
* check.
|
|
205
|
+
*
|
|
206
|
+
* Returns `null` when the section passes, or the reason it does not.
|
|
207
|
+
*/
|
|
208
|
+
export function evidenceDeficit(section: string, opts: { minWords?: number } = {}): string | null {
|
|
209
|
+
const minWords = opts.minWords ?? 25;
|
|
210
|
+
const body = section.replace(/^#{1,6} .*$/gm, '').trim();
|
|
211
|
+
const words = body.split(/\s+/).filter((w) => w !== '').length;
|
|
212
|
+
if (words < minWords) {
|
|
213
|
+
return `carries ${String(words)} word(s) of body, below the ${String(minWords)}-word floor — ` +
|
|
214
|
+
'the label is present and the substance is not';
|
|
215
|
+
}
|
|
216
|
+
// A concrete claim cites something a reader can check: a path, a number, a
|
|
217
|
+
// command, an identifier. Prose with none of those is an assertion about
|
|
218
|
+
// itself.
|
|
219
|
+
const concrete =
|
|
220
|
+
/[\w./-]+\.(?:ts|js|md|json|ya?ml|py|sh)\b/.test(body) ||
|
|
221
|
+
/\b\d+(?:\.\d+)?\s*(?:%|ms|s|MB|KB|lines?|files?|cases?|trials?)\b/.test(body) ||
|
|
222
|
+
/`[^`]+`/.test(body);
|
|
223
|
+
if (!concrete) {
|
|
224
|
+
return 'no checkable reference — no path, no measured figure, no identifier. A section that ' +
|
|
225
|
+
'cites nothing is a claim about itself.';
|
|
226
|
+
}
|
|
227
|
+
return null;
|
|
228
|
+
}
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A per-path legacy/modern verdict for a half-migrated repository.
|
|
3
|
+
*
|
|
4
|
+
* `road-to-consumer-repo-reality` Phase 4, step 4.1. Incremental replacement of
|
|
5
|
+
* a legacy system alongside the system it replaces is a named, published
|
|
6
|
+
* migration pattern; the artifact that pattern needs and nobody produces is a
|
|
7
|
+
* decidable answer to *"which conventions hold HERE"*.
|
|
8
|
+
*
|
|
9
|
+
* Current guidance says to respect existing patterns and to keep diffs minimal.
|
|
10
|
+
* Neither is decidable without this: in a tree that is two-fifths namespaced,
|
|
11
|
+
* strict-typed, PSR-shaped code and three-fifths an older include-and-dispatch
|
|
12
|
+
* module system, "the existing pattern" is not a property of the repository.
|
|
13
|
+
*
|
|
14
|
+
* A MIXED VERDICT IS NOT A REFUSAL TO DECIDE. That is the step's own wording and
|
|
15
|
+
* it is the hardest requirement here: a verdict that says only "mixed" leaves
|
|
16
|
+
* the caller exactly where it started. So a mixed file reports WHICH convention
|
|
17
|
+
* governs WHICH REGION, by line range, and `conventionAt(verdict, line)` answers
|
|
18
|
+
* the only question an editor actually has — *what must my edit at this point
|
|
19
|
+
* follow?*
|
|
20
|
+
*
|
|
21
|
+
* WHAT THIS MAY AND MAY NOT ENCODE. The phase states the trap in its own body:
|
|
22
|
+
* 4.2 and 4.3 were each observed in exactly one tree, so what may be encoded is
|
|
23
|
+
* the QUESTION, never the particular mechanism that tree happened to use. 4.1 is
|
|
24
|
+
* not in that category — namespaces, strict-type declarations and PSR-4
|
|
25
|
+
* autoloading are language-level and specification-level facts, not one
|
|
26
|
+
* repository's arrangement — but the same restraint is applied anyway: the
|
|
27
|
+
* signals below are all language constructs, and none is a project-specific
|
|
28
|
+
* naming convention, directory layout or framework marker.
|
|
29
|
+
*
|
|
30
|
+
* PER PATH, NEVER PER REPOSITORY. A repository-wide verdict is the thing that
|
|
31
|
+
* makes this artifact useless: the whole point is that the answer differs by
|
|
32
|
+
* file, and inside a file by region.
|
|
33
|
+
*/
|
|
34
|
+
import * as fs from 'node:fs';
|
|
35
|
+
|
|
36
|
+
/** Which convention governs. `unknown` is returned rather than guessed. */
|
|
37
|
+
export type Convention = 'modern' | 'legacy' | 'mixed' | 'unknown';
|
|
38
|
+
|
|
39
|
+
/** A contiguous run of lines governed by one convention. */
|
|
40
|
+
export interface Region {
|
|
41
|
+
/** 1-indexed, inclusive. */
|
|
42
|
+
startLine: number;
|
|
43
|
+
/** 1-indexed, inclusive. */
|
|
44
|
+
endLine: number;
|
|
45
|
+
convention: 'modern' | 'legacy';
|
|
46
|
+
/** The construct that decided it. Never empty. */
|
|
47
|
+
signal: string;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export interface PathVerdict {
|
|
51
|
+
/** The path as given. */
|
|
52
|
+
path: string;
|
|
53
|
+
convention: Convention;
|
|
54
|
+
/**
|
|
55
|
+
* Populated for EVERY verdict that is not `unknown`, including a
|
|
56
|
+
* single-convention file — a caller should not need two code paths.
|
|
57
|
+
*/
|
|
58
|
+
regions: Region[];
|
|
59
|
+
/**
|
|
60
|
+
* A single foreign signal inside an otherwise uniform file — one `$GLOBALS`
|
|
61
|
+
* read in a namespaced class, one class-like declaration in an
|
|
62
|
+
* include-and-dispatch file. Reported as a POINT and deliberately not as a
|
|
63
|
+
* region: a lone signal says something true about ITS line and nothing about
|
|
64
|
+
* the lines after it, and letting it open a region made every line to the end
|
|
65
|
+
* of the file inherit a convention it does not have.
|
|
66
|
+
*/
|
|
67
|
+
foreignPoints: { line: number; convention: 'modern' | 'legacy'; signal: string }[];
|
|
68
|
+
/** One sentence naming what decided it. Never empty. */
|
|
69
|
+
reason: string;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Signals, as language constructs only.
|
|
74
|
+
*
|
|
75
|
+
* `modern` markers are things the older include-and-dispatch style cannot
|
|
76
|
+
* express; `legacy` markers are things the namespaced style does not use. A line
|
|
77
|
+
* matching neither is inherited from the enclosing region rather than guessed —
|
|
78
|
+
* most lines in any file carry no signal at all.
|
|
79
|
+
*/
|
|
80
|
+
const MODERN: readonly { re: RegExp; signal: string }[] = [
|
|
81
|
+
{ re: /^\s*declare\s*\(\s*strict_types\s*=\s*1\s*\)/, signal: 'declare(strict_types=1)' },
|
|
82
|
+
{ re: /^\s*namespace\s+[A-Za-z_]/, signal: 'namespace declaration' },
|
|
83
|
+
{ re: /^\s*use\s+[A-Za-z_][A-Za-z0-9_]*\\/, signal: 'namespaced use statement' },
|
|
84
|
+
{ re: /^\s*(?:final\s+|abstract\s+)?(?:readonly\s+)?(?:class|interface|trait|enum)\s+/, signal: 'class-like declaration' },
|
|
85
|
+
];
|
|
86
|
+
|
|
87
|
+
const LEGACY: readonly { re: RegExp; signal: string }[] = [
|
|
88
|
+
{ re: /^\s*(?:require|include)(?:_once)?\s*[\s(]/, signal: 'require/include' },
|
|
89
|
+
{ re: /^\s*global\s+\$/, signal: 'global statement' },
|
|
90
|
+
{ re: /\$GLOBALS\s*\[/, signal: '$GLOBALS access' },
|
|
91
|
+
{ re: /^\s*function\s+[a-z][A-Za-z0-9_]*\s*\(/, signal: 'unnamespaced snake/camel function' },
|
|
92
|
+
];
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Requiring the Composer autoloader is the MODERN ecosystem's own bootstrap, not
|
|
96
|
+
* the include-and-dispatch pattern. Without this exception the canonical PSR-4
|
|
97
|
+
* entry point — `require __DIR__ . '/../vendor/autoload.php';` — makes every
|
|
98
|
+
* modern application's front controller come back `mixed`, which is the single
|
|
99
|
+
* most common file shape in the ecosystem this module is meant to classify.
|
|
100
|
+
*/
|
|
101
|
+
const AUTOLOAD_REQUIRE = /vendor\/autoload\.php/;
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* A line that is only a comment carries no convention. `// never touch
|
|
105
|
+
* $GLOBALS['x']` is advice ABOUT legacy code, and reading it as legacy code
|
|
106
|
+
* flips the region a reader is standing in. Block-comment interiors are not
|
|
107
|
+
* tracked — that needs a lexer — so only whole-line `//`, `#` and `*`
|
|
108
|
+
* continuations are skipped.
|
|
109
|
+
*/
|
|
110
|
+
const COMMENT_ONLY = /^\s*(?:\/\/|#|\*|\/\*)/;
|
|
111
|
+
|
|
112
|
+
function _signalFor(line: string): { convention: 'modern' | 'legacy'; signal: string } | null {
|
|
113
|
+
if (COMMENT_ONLY.test(line)) return null;
|
|
114
|
+
for (const { re, signal } of MODERN) if (re.test(line)) return { convention: 'modern', signal };
|
|
115
|
+
if (AUTOLOAD_REQUIRE.test(line)) return null;
|
|
116
|
+
for (const { re, signal } of LEGACY) if (re.test(line)) return { convention: 'legacy', signal };
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Classify one file's text into regions.
|
|
122
|
+
*
|
|
123
|
+
* A signal starts a region that runs until the next signal of the OTHER
|
|
124
|
+
* convention. Lines before the first signal belong to the first region, so every
|
|
125
|
+
* line of a classified file is covered — a caller asking about line 1 of a file
|
|
126
|
+
* whose first signal is on line 3 gets an answer rather than a hole.
|
|
127
|
+
*/
|
|
128
|
+
export function classifyText(pathName: string, text: string): PathVerdict {
|
|
129
|
+
const lines = text.split('\n');
|
|
130
|
+
const hits: { line: number; convention: 'modern' | 'legacy'; signal: string }[] = [];
|
|
131
|
+
for (let i = 0; i < lines.length; i += 1) {
|
|
132
|
+
const hit = _signalFor(lines[i] ?? '');
|
|
133
|
+
if (hit !== null) hits.push({ line: i + 1, ...hit });
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// A lone signal of one convention, against two or more of the other, is a
|
|
137
|
+
// POINT rather than a region boundary. Two is the threshold because one
|
|
138
|
+
// occurrence is as likely to be an artifact — a legacy global read inside a
|
|
139
|
+
// modern service, a single class declaration in an old dispatch file — as a
|
|
140
|
+
// statement about the surrounding code.
|
|
141
|
+
const counts = { modern: 0, legacy: 0 };
|
|
142
|
+
for (const h of hits) counts[h.convention] += 1;
|
|
143
|
+
const isolated: 'modern' | 'legacy' | null =
|
|
144
|
+
counts.modern === 1 && counts.legacy >= 2 ? 'modern'
|
|
145
|
+
: counts.legacy === 1 && counts.modern >= 2 ? 'legacy'
|
|
146
|
+
: null;
|
|
147
|
+
const foreignPoints = isolated === null ? [] : hits.filter((h) => h.convention === isolated);
|
|
148
|
+
const structural = isolated === null ? hits : hits.filter((h) => h.convention !== isolated);
|
|
149
|
+
|
|
150
|
+
const regions: Region[] = [];
|
|
151
|
+
for (const hit of structural) {
|
|
152
|
+
const i = hit.line - 1;
|
|
153
|
+
const last = regions[regions.length - 1];
|
|
154
|
+
if (last !== undefined && last.convention === hit.convention) {
|
|
155
|
+
last.endLine = i + 1;
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
regions.push({ startLine: i + 1, endLine: i + 1, convention: hit.convention, signal: hit.signal });
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if (regions.length === 0) {
|
|
162
|
+
return {
|
|
163
|
+
path: pathName,
|
|
164
|
+
convention: 'unknown',
|
|
165
|
+
regions: [],
|
|
166
|
+
foreignPoints,
|
|
167
|
+
reason: 'no convention signal found — neither a namespaced construct nor an include-and-dispatch one',
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Extend each region to meet the next, and the first back to line 1, so the
|
|
172
|
+
// whole file is covered and `conventionAt` never returns a hole.
|
|
173
|
+
const first = regions[0] as Region;
|
|
174
|
+
first.startLine = 1;
|
|
175
|
+
for (let i = 0; i < regions.length - 1; i += 1) {
|
|
176
|
+
(regions[i] as Region).endLine = (regions[i + 1] as Region).startLine - 1;
|
|
177
|
+
}
|
|
178
|
+
(regions[regions.length - 1] as Region).endLine = Math.max(lines.length, 1);
|
|
179
|
+
|
|
180
|
+
const kinds = new Set(regions.map((r) => r.convention));
|
|
181
|
+
if (kinds.size === 1) {
|
|
182
|
+
const only = [...kinds][0] as 'modern' | 'legacy';
|
|
183
|
+
return {
|
|
184
|
+
path: pathName,
|
|
185
|
+
convention: only,
|
|
186
|
+
regions,
|
|
187
|
+
foreignPoints,
|
|
188
|
+
reason:
|
|
189
|
+
`every structural signal in this file is ${only} (${regions.map((r) => r.signal).join(', ')})` +
|
|
190
|
+
(foreignPoints.length === 0
|
|
191
|
+
? ''
|
|
192
|
+
: `. One isolated ${isolated} signal at L${foreignPoints.map((f) => f.line).join(', L')} ` +
|
|
193
|
+
`(${foreignPoints.map((f) => f.signal).join(', ')}) — true of that line, not of the lines after it`),
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
return {
|
|
197
|
+
path: pathName,
|
|
198
|
+
convention: 'mixed',
|
|
199
|
+
regions,
|
|
200
|
+
foreignPoints,
|
|
201
|
+
reason:
|
|
202
|
+
`both conventions appear in this file, in ${regions.length} regions. ` +
|
|
203
|
+
regions.map((r) => `L${r.startLine}-${r.endLine} ${r.convention} (${r.signal})`).join('; ') +
|
|
204
|
+
'. An edit follows the convention of the region it lands in, not the file as a whole.',
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/** Read and classify one file. An unreadable file is `unknown`, never guessed. */
|
|
209
|
+
export function classifyPath(absPath: string, displayPath = absPath): PathVerdict {
|
|
210
|
+
try {
|
|
211
|
+
return classifyText(displayPath, fs.readFileSync(absPath, 'utf8'));
|
|
212
|
+
} catch {
|
|
213
|
+
return { path: displayPath, convention: 'unknown', regions: [], foreignPoints: [], reason: 'unreadable' };
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* The only question an editor actually has: what must an edit at THIS line
|
|
219
|
+
* follow? Returns `null` when the file carries no signal at all — which is a
|
|
220
|
+
* real answer, and different from "modern by default".
|
|
221
|
+
*/
|
|
222
|
+
export function conventionAt(verdict: PathVerdict, line: number): 'modern' | 'legacy' | null {
|
|
223
|
+
for (const f of verdict.foreignPoints) if (f.line === line) return f.convention;
|
|
224
|
+
for (const r of verdict.regions) {
|
|
225
|
+
if (line >= r.startLine && line <= r.endLine) return r.convention;
|
|
226
|
+
}
|
|
227
|
+
return null;
|
|
228
|
+
}
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A lexical shortlist over the existing BM25 core — a SHORTLIST, never a decider.
|
|
3
|
+
*
|
|
4
|
+
* `road-to-governed-harness-evolution` Phase 6, step 6.3.
|
|
5
|
+
*
|
|
6
|
+
* > *6.3 Only then consider a lexical shortlist, and only as a shortlist. Over
|
|
7
|
+
* > the existing BM25 core. No embeddings […]. The skipped parent proposed the
|
|
8
|
+
* > shortlist and explicitly refused it as final truth.*
|
|
9
|
+
* > verify: **the shortlist feeds a later stage and never decides alone.**
|
|
10
|
+
*
|
|
11
|
+
* ## No embeddings, and the citation this step carried had moved
|
|
12
|
+
*
|
|
13
|
+
* The step's own text cites `docs/contracts/no-runtime-boundary.md:40` for the
|
|
14
|
+
* classification. That file is now a **superseded pointer** (ADR-249,
|
|
15
|
+
* 2026-08-27) and line 40 no longer says it. The substance survived the move
|
|
16
|
+
* intact and is quoted from its live home instead:
|
|
17
|
+
* `docs/contracts/resident-process-governance.md:82` — *"A code-graph cache
|
|
18
|
+
* passes; a vector index fails"*, under the P3 state-store test, with P4
|
|
19
|
+
* separately prohibiting any index build requiring network or model calls.
|
|
20
|
+
* So this module builds over `_lib/lexical_index.ts` — hand-rolled BM25, pure
|
|
21
|
+
* Node stdlib, the core ADR-061 already sanctions. A static scan in
|
|
22
|
+
* `tests/scripts/_lib/lexical_shortlist.test.ts` asserts this module and that
|
|
23
|
+
* core reach no embedding construct, no vector store and no model call. The
|
|
24
|
+
* construct list lives in the test rather than here for a mechanical reason:
|
|
25
|
+
* a scanner whose banned literals sit in the file it scans matches its own
|
|
26
|
+
* declaration and can never pass — the same reason
|
|
27
|
+
* `tests/scripts/governed_harness_no_live_harness.test.ts` keeps its banned set
|
|
28
|
+
* on the test side.
|
|
29
|
+
*
|
|
30
|
+
* ## "Never decides alone", made mechanical rather than promised
|
|
31
|
+
*
|
|
32
|
+
* A shortlist decides alone in exactly two ways, and both are closed here by
|
|
33
|
+
* construction rather than by a caller's discipline:
|
|
34
|
+
*
|
|
35
|
+
* 1. **By addition** — delivering something the authoritative matcher did not
|
|
36
|
+
* fire on. Impossible: {@link orderByShortlist} takes the matcher's output
|
|
37
|
+
* as its input domain and returns a PERMUTATION of it. Ids the shortlist
|
|
38
|
+
* ranks that the matcher did not return are dropped on the floor, and
|
|
39
|
+
* {@link selectForInjection}'s tie-break argument is consulted only to
|
|
40
|
+
* order ids that are already in `matches`.
|
|
41
|
+
* 2. **By subtraction** — silently removing a matcher hit, which is a
|
|
42
|
+
* decision dressed as a filter. Impossible: the same permutation
|
|
43
|
+
* invariant. {@link ShortlistDecidedAloneError} is thrown when the output
|
|
44
|
+
* id multiset differs from the input's in either direction, so a future
|
|
45
|
+
* edit that turns the reordering into a filter fails loudly instead of
|
|
46
|
+
* quietly losing recall.
|
|
47
|
+
*
|
|
48
|
+
* The shortlist's actual job is the third thing, and it is a real one: when the
|
|
49
|
+
* per-prompt byte cap BINDS, something has to choose which matched bodies
|
|
50
|
+
* survive it. Today that is the matcher's trigger-hit count and then router
|
|
51
|
+
* declaration order — and the hit count is a coarse integer on which most rules
|
|
52
|
+
* tie, so the surviving set is decided by declaration order, which is not a
|
|
53
|
+
* relevance signal at all. The shortlist supplies one, subordinate to the
|
|
54
|
+
* matcher score and never above it.
|
|
55
|
+
*
|
|
56
|
+
* ## Subordination is an ORDER of comparison keys, and the order is the contract
|
|
57
|
+
*
|
|
58
|
+
* `score desc → shortlist rank → router order`. The matcher's verdict is read
|
|
59
|
+
* first and the shortlist only breaks its ties. Promoting the shortlist above
|
|
60
|
+
* `score` would let a lexically-similar rule outrank one the matcher fired on
|
|
61
|
+
* twice, which is the shortlist deciding — a test pins the key order and is
|
|
62
|
+
* proved to go red when it is inverted.
|
|
63
|
+
*
|
|
64
|
+
* ## What this module does NOT claim
|
|
65
|
+
*
|
|
66
|
+
* That the shortlist improves anything. Whether a lexical tie-break beats
|
|
67
|
+
* declaration order under a binding cap is a MEASUREMENT, and step 6.4 is where
|
|
68
|
+
* the loss ceiling for it gets pre-registered. `model_rule_injection
|
|
69
|
+
* --three-arm --shortlist` runs the arm with it, default OFF, so 6.1's recorded
|
|
70
|
+
* figures are reproducible unchanged.
|
|
71
|
+
*/
|
|
72
|
+
import { LexicalIndex, type IndexDoc } from './lexical_index.js';
|
|
73
|
+
import { allTierRules, loadRuleBody, type Router, type TierRuleMatch } from './rule_injection.js';
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* How many ids a shortlist may carry.
|
|
77
|
+
*
|
|
78
|
+
* A STATED default, not a measured optimum, and deliberately generous: this
|
|
79
|
+
* shortlist narrows nothing (it reorders), so `k` bounds only how far down the
|
|
80
|
+
* BM25 ranking a tie-break signal is available before router order takes over.
|
|
81
|
+
* Revisit-if 6.4 measures a cap-drop the shortlist could have prevented at a
|
|
82
|
+
* rank beyond it.
|
|
83
|
+
*/
|
|
84
|
+
export const SHORTLIST_SIZE = 40;
|
|
85
|
+
|
|
86
|
+
export class ShortlistDecidedAloneError extends Error {
|
|
87
|
+
constructor(message: string) {
|
|
88
|
+
super(message);
|
|
89
|
+
this.name = 'ShortlistDecidedAloneError';
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Index every tier rule that has a projected body.
|
|
95
|
+
*
|
|
96
|
+
* Kernel rules are excluded for the same reason `_lib/rule_injection.ts`
|
|
97
|
+
* excludes them from injection: they are standing context by definition, so
|
|
98
|
+
* they are never candidates for a cap that only ever binds on injected bodies.
|
|
99
|
+
* `allTierRules` already returns the non-kernel set.
|
|
100
|
+
*/
|
|
101
|
+
export function buildRuleIndex(repoRoot: string, router: Router): LexicalIndex {
|
|
102
|
+
const docs: IndexDoc[] = [];
|
|
103
|
+
for (const r of allTierRules(router)) {
|
|
104
|
+
const body = loadRuleBody(repoRoot, r.id);
|
|
105
|
+
if (body === null) continue;
|
|
106
|
+
// The id is joined into the text on purpose: a rule's own name is the
|
|
107
|
+
// single most reliable lexical signal it carries, and hyphenated ids
|
|
108
|
+
// tokenise into exactly the words a prompt about that rule would use.
|
|
109
|
+
docs.push({ id: r.id, text: `${r.id.split('-').join(' ')}\n${body}` });
|
|
110
|
+
}
|
|
111
|
+
return new LexicalIndex(docs);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* The ranked shortlist for one prompt. BM25 only; no model call, no embedding.
|
|
116
|
+
*
|
|
117
|
+
* Returns ids in descending relevance, capped at `k`. An empty result is a real
|
|
118
|
+
* answer — it means no indexed body shares a scoring term with the prompt, and
|
|
119
|
+
* the caller then falls back to router order exactly as it does today.
|
|
120
|
+
*/
|
|
121
|
+
export function shortlistIds(
|
|
122
|
+
index: LexicalIndex,
|
|
123
|
+
prompt: string,
|
|
124
|
+
k: number = SHORTLIST_SIZE,
|
|
125
|
+
): string[] {
|
|
126
|
+
if (k <= 0) return [];
|
|
127
|
+
return index.rank([prompt]).slice(0, k).map((r) => r.id);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Rank lookup: id → position in the shortlist, or `Infinity` for absent.
|
|
132
|
+
*
|
|
133
|
+
* `Infinity` rather than a large integer so an unshortlisted id can never sort
|
|
134
|
+
* above a shortlisted one by arithmetic accident.
|
|
135
|
+
*/
|
|
136
|
+
export function shortlistRanks(ranked: readonly string[]): Map<string, number> {
|
|
137
|
+
const m = new Map<string, number>();
|
|
138
|
+
for (const [i, id] of ranked.entries()) {
|
|
139
|
+
if (!m.has(id)) m.set(id, i);
|
|
140
|
+
}
|
|
141
|
+
return m;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Reorder the matcher's output by the shortlist — a PERMUTATION, always.
|
|
146
|
+
*
|
|
147
|
+
* This is the function that makes "never decides alone" checkable. It takes the
|
|
148
|
+
* matcher's verdict as its input domain, applies the same key order
|
|
149
|
+
* {@link selectForInjection} applies (`score desc → shortlist rank → router
|
|
150
|
+
* order`), and REFUSES to return anything whose id multiset differs from what
|
|
151
|
+
* it was given.
|
|
152
|
+
*
|
|
153
|
+
* @throws {ShortlistDecidedAloneError} when the result would add or drop an id.
|
|
154
|
+
*/
|
|
155
|
+
export function orderByShortlist(
|
|
156
|
+
matches: readonly TierRuleMatch[],
|
|
157
|
+
ranked: readonly string[],
|
|
158
|
+
): TierRuleMatch[] {
|
|
159
|
+
const rank = shortlistRanks(ranked);
|
|
160
|
+
const out = [...matches].sort(
|
|
161
|
+
(a, b) =>
|
|
162
|
+
b.score - a.score ||
|
|
163
|
+
(rank.get(a.id) ?? Infinity) - (rank.get(b.id) ?? Infinity) ||
|
|
164
|
+
a.order - b.order,
|
|
165
|
+
);
|
|
166
|
+
assertPermutation(matches, out);
|
|
167
|
+
return out;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/** Same-multiset assertion, in both directions. Exported so a test can drive it. */
|
|
171
|
+
export function assertPermutation(
|
|
172
|
+
before: readonly TierRuleMatch[],
|
|
173
|
+
after: readonly TierRuleMatch[],
|
|
174
|
+
): void {
|
|
175
|
+
const count = (xs: readonly TierRuleMatch[]): Map<string, number> => {
|
|
176
|
+
const m = new Map<string, number>();
|
|
177
|
+
for (const x of xs) m.set(x.id, (m.get(x.id) ?? 0) + 1);
|
|
178
|
+
return m;
|
|
179
|
+
};
|
|
180
|
+
const a = count(before);
|
|
181
|
+
const b = count(after);
|
|
182
|
+
const added: string[] = [];
|
|
183
|
+
const dropped: string[] = [];
|
|
184
|
+
for (const [id, n] of b) {
|
|
185
|
+
if ((a.get(id) ?? 0) < n) added.push(id);
|
|
186
|
+
}
|
|
187
|
+
for (const [id, n] of a) {
|
|
188
|
+
if ((b.get(id) ?? 0) < n) dropped.push(id);
|
|
189
|
+
}
|
|
190
|
+
if (added.length > 0 || dropped.length > 0) {
|
|
191
|
+
throw new ShortlistDecidedAloneError(
|
|
192
|
+
'the shortlist changed the matcher`s set instead of ordering it' +
|
|
193
|
+
(added.length > 0 ? ` — added ${added.sort().join(', ')}` : '') +
|
|
194
|
+
(dropped.length > 0 ? ` — dropped ${dropped.sort().join(', ')}` : '') +
|
|
195
|
+
'. A shortlist that adds delivers what the matcher never fired on; one that ' +
|
|
196
|
+
'drops decides by subtraction. Both are the shortlist deciding alone.',
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* A per-prompt shortlist function, bound to one repo root and router.
|
|
203
|
+
*
|
|
204
|
+
* The index is built ONCE and reused for every prompt — building it per prompt
|
|
205
|
+
* would make the cheap stage the expensive one, which is the opposite of what a
|
|
206
|
+
* shortlist is for.
|
|
207
|
+
*/
|
|
208
|
+
export function makePromptShortlist(
|
|
209
|
+
repoRoot: string,
|
|
210
|
+
router: Router,
|
|
211
|
+
k: number = SHORTLIST_SIZE,
|
|
212
|
+
): (prompt: string) => readonly string[] {
|
|
213
|
+
const index = buildRuleIndex(repoRoot, router);
|
|
214
|
+
return (prompt: string) => shortlistIds(index, prompt, k);
|
|
215
|
+
}
|