@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
|
@@ -13,6 +13,8 @@
|
|
|
13
13
|
* change to either has to touch the record and the code together.
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
|
+
import { decidePairedVerdict, floorWarning, MIN_DISCORDANT } from './paired_verdict.js';
|
|
17
|
+
|
|
16
18
|
/** T1 — median added lines must fall by at least this much. */
|
|
17
19
|
export const T1_MEDIAN_LINES_PCT = -10;
|
|
18
20
|
/** Two-sided significance for every endpoint in the pair. */
|
|
@@ -25,7 +27,15 @@ export interface PairedContinuous {
|
|
|
25
27
|
/** `null` when the baseline median is 0 — no percent change exists. */
|
|
26
28
|
median_delta_pct: number | null;
|
|
27
29
|
median_delta: number;
|
|
30
|
+
/**
|
|
31
|
+
* Retained for TRIAGE and reporting. Since PREREG amendment v2 it decides
|
|
32
|
+
* nothing — see {@link evaluateSizeClaim}.
|
|
33
|
+
*/
|
|
28
34
|
wilcoxon_p: number;
|
|
35
|
+
/** Non-tied pairs in the direction of interest. Absent on a pre-amendment report. */
|
|
36
|
+
direction_wins?: number | undefined;
|
|
37
|
+
/** Non-tied pairs against the direction of interest. Absent on a pre-amendment report. */
|
|
38
|
+
direction_losses?: number | undefined;
|
|
29
39
|
}
|
|
30
40
|
|
|
31
41
|
/** A paired binary endpoint (the safety tier). */
|
|
@@ -56,6 +66,44 @@ export interface SizeClaimVerdict {
|
|
|
56
66
|
safety_measured: boolean;
|
|
57
67
|
}
|
|
58
68
|
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
/** One clause naming the directional evidence, printable in a verdict reason. */
|
|
72
|
+
function describeDirection(v: ReturnType<typeof decidePairedVerdict> | null): string {
|
|
73
|
+
if (v === null) {
|
|
74
|
+
return `no direction counts in the report — pre-amendment or unmeasured, so the direction bar is not met`;
|
|
75
|
+
}
|
|
76
|
+
if (v.kind === 'underpowered') {
|
|
77
|
+
return `underpowered: ${String(v.discordant)} non-tied pair(s) below the derived floor of ${String(MIN_DISCORDANT)}`;
|
|
78
|
+
}
|
|
79
|
+
const warn = floorWarning(v);
|
|
80
|
+
return `${String(v.wins)}/${String(v.discordant)} non-tied pairs, one-sided exact p=${v.p.toFixed(4)}` +
|
|
81
|
+
(warn === null ? '' : ` — ${warn}`);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* The directional verdict for one endpoint, or `null` when it cannot be formed.
|
|
86
|
+
*
|
|
87
|
+
* `null` means the report carries no direction counts — a pre-amendment report,
|
|
88
|
+
* or an unmeasured endpoint. The caller treats that as "not significant" rather
|
|
89
|
+
* than reaching for `wilcoxon_p`, which is the whole point of the amendment.
|
|
90
|
+
*
|
|
91
|
+
* The counts are reconstructed into a delta vector of ±1 rather than carrying
|
|
92
|
+
* raw deltas through the report: the exact sign test needs direction only, and
|
|
93
|
+
* a council seat noted the raw deltas are preferable for auditability but not
|
|
94
|
+
* mathematically required. The raw values remain in the trial artifacts.
|
|
95
|
+
*/
|
|
96
|
+
function directionVerdict(e: PairedContinuous): ReturnType<typeof decidePairedVerdict> | null {
|
|
97
|
+
if (!e.measured) return null;
|
|
98
|
+
const w = e.direction_wins;
|
|
99
|
+
const l = e.direction_losses;
|
|
100
|
+
if (w === undefined || l === undefined) return null;
|
|
101
|
+
return decidePairedVerdict({
|
|
102
|
+
deltas: [...Array<number>(w).fill(1), ...Array<number>(l).fill(-1)],
|
|
103
|
+
alpha: ALPHA,
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
59
107
|
/**
|
|
60
108
|
* Evaluate the size claim for one comparison — binding, and structurally unable
|
|
61
109
|
* to report a win on size alone.
|
|
@@ -98,9 +146,52 @@ export function evaluateSizeClaim(input: SizeClaimInput): SizeClaimVerdict {
|
|
|
98
146
|
};
|
|
99
147
|
}
|
|
100
148
|
|
|
101
|
-
|
|
149
|
+
// ── PREREG amendment v2, 2026-08-26 — direction decides significance ────
|
|
150
|
+
//
|
|
151
|
+
// The significance half of every endpoint below was a Wilcoxon signed-rank
|
|
152
|
+
// p, which ranks by |difference| and is therefore magnitude-weighted. That
|
|
153
|
+
// was shown to disagree with the exact test on twelve records up to ten
|
|
154
|
+
// trials, PERMISSIVE in every one, with the visible symptom inverted: an
|
|
155
|
+
// artifact winning every trial still failed, because a few large
|
|
156
|
+
// opposite-direction deltas outweighed a clean sweep of small ones.
|
|
157
|
+
//
|
|
158
|
+
// TWO PROPOSITIONS, KEPT SEPARATE — this is the refinement both council
|
|
159
|
+
// seats independently required (2026-08-26, 2/2) and it is the half that
|
|
160
|
+
// makes the amendment safe. A sign test answers "did this help more often
|
|
161
|
+
// than it hurt" and says NOTHING about how much. Replacing Wilcoxon with it
|
|
162
|
+
// outright would let a clean sweep of negligible improvements claim a SIZE
|
|
163
|
+
// win. So:
|
|
164
|
+
// · directional reliability → exact one-sided sign test over non-tied pairs
|
|
165
|
+
// · practical magnitude → the pre-registered T1_MEDIAN_LINES_PCT bar,
|
|
166
|
+
// unchanged and still independently binding
|
|
167
|
+
// Both must hold. Dropping either is a different claim and needs its own
|
|
168
|
+
// amendment.
|
|
169
|
+
//
|
|
170
|
+
// A report predating the amendment carries no direction counts. It is
|
|
171
|
+
// treated as UNMEASURED for the significance half rather than falling back
|
|
172
|
+
// to `wilcoxon_p`, because a silent fallback to the defective test is
|
|
173
|
+
// exactly what this amendment removes.
|
|
174
|
+
const sizeDir = directionVerdict(size);
|
|
175
|
+
const cxDir = directionVerdict(cx);
|
|
176
|
+
// ASYMMETRY, deliberate and the conservative direction of it. A PASS needs
|
|
177
|
+
// the direction bar MET; a REFUSAL fires on EITHER signal. Applying the
|
|
178
|
+
// strict reading to both would make the anti-golfing refusal disappear on
|
|
179
|
+
// any report without direction counts — a missing input silently rescuing
|
|
180
|
+
// an arm is the failure mode a refusal exists to prevent, and it is the
|
|
181
|
+
// opposite of what the amendment is for. So the refusal keeps the legacy
|
|
182
|
+
// Wilcoxon signal as a second trigger; the claim does not.
|
|
183
|
+
const cx_rose =
|
|
184
|
+
cx.measured &&
|
|
185
|
+
cx.median_delta > 0 &&
|
|
186
|
+
(cxDir === null ? cx.wilcoxon_p < ALPHA : cxDir.kind === 'regression');
|
|
187
|
+
// `regression`, NOT `pass`, and the distinction cost a test run to find.
|
|
188
|
+
// `direction_wins` counts the endpoint's IMPROVEMENT direction — a negative
|
|
189
|
+
// delta — for every continuous endpoint, so on complexity a `pass` means
|
|
190
|
+
// complexity FELL. What `cx_rose` needs is the opposite tail, which is
|
|
191
|
+
// exactly what `regression` names. Writing `pass` here reads correctly in
|
|
192
|
+
// English and inverts the anti-golfing guard.
|
|
102
193
|
const pct = size.measured ? size.median_delta_pct : null;
|
|
103
|
-
const lines_fell = pct !== null && pct <= T1_MEDIAN_LINES_PCT &&
|
|
194
|
+
const lines_fell = pct !== null && pct <= T1_MEDIAN_LINES_PCT && sizeDir?.kind === 'pass';
|
|
104
195
|
|
|
105
196
|
// (2b) Golfing is refused as soon as T2 shows it, and does NOT wait for T4.
|
|
106
197
|
//
|
|
@@ -161,8 +252,11 @@ export function evaluateSizeClaim(input: SizeClaimInput): SizeClaimVerdict {
|
|
|
161
252
|
...base,
|
|
162
253
|
verdict: lines_fell ? 'PASS' : 'NO-SIZE-WIN',
|
|
163
254
|
reason: lines_fell
|
|
164
|
-
? `median added lines ${pct}%
|
|
165
|
-
|
|
255
|
+
? `median added lines ${pct}% (magnitude bar) AND ${describeDirection(sizeDir)} (direction bar) ` +
|
|
256
|
+
'with no significant complexity rise and no safety regression'
|
|
257
|
+
: `T1 not met (median added lines ${pct}%, bar <= ${T1_MEDIAN_LINES_PCT}%; ` +
|
|
258
|
+
`direction: ${describeDirection(sizeDir)}). Wilcoxon p=${size.wilcoxon_p} is reported for ` +
|
|
259
|
+
'triage and decides nothing.',
|
|
166
260
|
lines_fell,
|
|
167
261
|
size_measured: true,
|
|
168
262
|
complexity_measured: true,
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `cache` block of a benchmark / cost report — required fields, and the one
|
|
3
|
+
* legitimate way to not have a number (`road-to-runtime-context-floors` step 4.1).
|
|
4
|
+
*
|
|
5
|
+
* Both fields are REQUIRED. A report that cannot compute one states a reason; it
|
|
6
|
+
* does not omit the key and it does not emit a zero. A blank reads as "no cache
|
|
7
|
+
* activity" and a zero reads as "the worst possible ratio" — both are claims, and
|
|
8
|
+
* neither is the claim "this was not measured".
|
|
9
|
+
*
|
|
10
|
+
* Contract: `docs/contracts/benchmark-report-schema.md` § The `cache` block.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
export const REQUIRED_CACHE_FIELDS = ['read_write_ratio', 'stable_prefix_share'] as const;
|
|
14
|
+
export type RequiredCacheField = (typeof REQUIRED_CACHE_FIELDS)[number];
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* What produced the report (`road-to-delivered-cost-truth` step 4.2).
|
|
18
|
+
*
|
|
19
|
+
* A benchmark number without its provenance cannot be reproduced or challenged,
|
|
20
|
+
* and this repository carries a measured instance of that failure: an external
|
|
21
|
+
* round's headline figures are recorded `unverifiable` precisely because the run
|
|
22
|
+
* behind them is unreachable. These four keep a report here from becoming that.
|
|
23
|
+
*/
|
|
24
|
+
export const REQUIRED_PROVENANCE_FIELDS = [
|
|
25
|
+
'host_binary_hash',
|
|
26
|
+
'harness_commit',
|
|
27
|
+
'harness_dirty',
|
|
28
|
+
'reproducibility',
|
|
29
|
+
] as const;
|
|
30
|
+
export type RequiredProvenanceField = (typeof REQUIRED_PROVENANCE_FIELDS)[number];
|
|
31
|
+
|
|
32
|
+
/** A measured number, or a stated reason there is none. Never a bare absence. */
|
|
33
|
+
export type CacheFieldValue = number | { unavailable: string };
|
|
34
|
+
|
|
35
|
+
export interface CacheBlock {
|
|
36
|
+
read_write_ratio: CacheFieldValue;
|
|
37
|
+
stable_prefix_share: CacheFieldValue;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export type AnyRequiredField = RequiredCacheField | RequiredProvenanceField;
|
|
41
|
+
|
|
42
|
+
export type FieldProblem =
|
|
43
|
+
| { field: AnyRequiredField; kind: 'missing' }
|
|
44
|
+
| { field: AnyRequiredField; kind: 'blank-unavailable' }
|
|
45
|
+
| { field: AnyRequiredField; kind: 'not-a-number'; value: unknown }
|
|
46
|
+
| { field: AnyRequiredField; kind: 'out-of-range'; value: number }
|
|
47
|
+
| { field: AnyRequiredField; kind: 'blank-value' };
|
|
48
|
+
|
|
49
|
+
export function unavailable(reason: string): CacheFieldValue {
|
|
50
|
+
return { unavailable: reason };
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function validateField(field: RequiredCacheField, raw: unknown): FieldProblem | null {
|
|
54
|
+
if (raw === undefined || raw === null) return { field, kind: 'missing' };
|
|
55
|
+
if (typeof raw === 'object') {
|
|
56
|
+
const reason = (raw as { unavailable?: unknown }).unavailable;
|
|
57
|
+
if (typeof reason !== 'string' || reason.trim().length === 0) {
|
|
58
|
+
return { field, kind: 'blank-unavailable' };
|
|
59
|
+
}
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
if (typeof raw !== 'number' || !Number.isFinite(raw)) return { field, kind: 'not-a-number', value: raw };
|
|
63
|
+
// A share is a fraction; a ratio is unbounded above but never negative.
|
|
64
|
+
if (raw < 0) return { field, kind: 'out-of-range', value: raw };
|
|
65
|
+
if (field === 'stable_prefix_share' && raw > 1) return { field, kind: 'out-of-range', value: raw };
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** Validate a report's `cache` block. Empty array = valid. */
|
|
70
|
+
export function validateCacheBlock(report: unknown): FieldProblem[] {
|
|
71
|
+
const cache = (report as { cache?: unknown } | null)?.cache;
|
|
72
|
+
if (cache === undefined || cache === null || typeof cache !== 'object') {
|
|
73
|
+
return REQUIRED_CACHE_FIELDS.map((field) => ({ field, kind: 'missing' as const }));
|
|
74
|
+
}
|
|
75
|
+
const rec = cache as Record<string, unknown>;
|
|
76
|
+
return REQUIRED_CACHE_FIELDS.map((f) => validateField(f, rec[f])).filter((p): p is FieldProblem => p !== null);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function blockOf(field: AnyRequiredField): string {
|
|
80
|
+
return (REQUIRED_PROVENANCE_FIELDS as readonly string[]).includes(field) ? 'provenance' : 'cache';
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export function describeProblem(p: FieldProblem): string {
|
|
84
|
+
const q = `${blockOf(p.field)}.${p.field}`;
|
|
85
|
+
switch (p.kind) {
|
|
86
|
+
case 'missing':
|
|
87
|
+
return `${q} is missing — it is required; emit { unavailable: "<reason>" } when it cannot be supplied`;
|
|
88
|
+
case 'blank-unavailable':
|
|
89
|
+
return `${q} declares itself unavailable with no reason — a reason is the whole point of the form`;
|
|
90
|
+
case 'blank-value':
|
|
91
|
+
return `${q} is an empty string, which states nothing; emit { unavailable: "<reason>" } instead`;
|
|
92
|
+
case 'not-a-number':
|
|
93
|
+
return `${q} is ${JSON.stringify(p.value)}, which is neither a number nor { unavailable: "<reason>" }`;
|
|
94
|
+
case 'out-of-range':
|
|
95
|
+
return `${q} is ${String(p.value)}, outside its valid range`;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Validate a report's `provenance` block.
|
|
101
|
+
*
|
|
102
|
+
* `harness_dirty` is a BOOLEAN and the others are strings, so the shape check is
|
|
103
|
+
* per-field rather than uniform — and `false` must pass, which a truthiness
|
|
104
|
+
* check would silently reject. That is the bug this comment exists to prevent on
|
|
105
|
+
* the next edit: a dirty flag of `false` is the good case.
|
|
106
|
+
*/
|
|
107
|
+
export function validateProvenanceBlock(report: unknown): FieldProblem[] {
|
|
108
|
+
const block = (report as { provenance?: unknown } | null)?.provenance;
|
|
109
|
+
if (block === undefined || block === null || typeof block !== 'object') {
|
|
110
|
+
return REQUIRED_PROVENANCE_FIELDS.map((field) => ({ field, kind: 'missing' as const }));
|
|
111
|
+
}
|
|
112
|
+
const rec = block as Record<string, unknown>;
|
|
113
|
+
const out: FieldProblem[] = [];
|
|
114
|
+
for (const field of REQUIRED_PROVENANCE_FIELDS) {
|
|
115
|
+
const raw = rec[field];
|
|
116
|
+
if (raw === undefined || raw === null) {
|
|
117
|
+
out.push({ field, kind: 'missing' });
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
if (typeof raw === 'object') {
|
|
121
|
+
const reason = (raw as { unavailable?: unknown }).unavailable;
|
|
122
|
+
if (typeof reason !== 'string' || reason.trim().length === 0) out.push({ field, kind: 'blank-unavailable' });
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
if (field === 'harness_dirty') {
|
|
126
|
+
if (typeof raw !== 'boolean') out.push({ field, kind: 'not-a-number', value: raw });
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
if (typeof raw !== 'string') out.push({ field, kind: 'not-a-number', value: raw });
|
|
130
|
+
else if (raw.trim().length === 0) out.push({ field, kind: 'blank-value' });
|
|
131
|
+
}
|
|
132
|
+
return out;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Compute the ratio from token totals, or state why not.
|
|
137
|
+
*
|
|
138
|
+
* Zero cache-creation tokens is the interesting case: the ratio is undefined,
|
|
139
|
+
* not infinite and not zero, and the honest report says so.
|
|
140
|
+
*/
|
|
141
|
+
export function readWriteRatio(cacheRead: number, cacheCreation: number): CacheFieldValue {
|
|
142
|
+
if (!Number.isFinite(cacheRead) || !Number.isFinite(cacheCreation)) {
|
|
143
|
+
return unavailable('the session source carries no cache_read/cache_creation token fields');
|
|
144
|
+
}
|
|
145
|
+
if (cacheCreation === 0) {
|
|
146
|
+
return unavailable('no cache-creation tokens were recorded in this window, so the ratio is undefined rather than zero');
|
|
147
|
+
}
|
|
148
|
+
return cacheRead / cacheCreation;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/** The STABLE cohort as a fraction of dispatches carrying the field, or why not. */
|
|
152
|
+
export function stablePrefixShare(stableN: number, unstableN: number): CacheFieldValue {
|
|
153
|
+
const total = stableN + unstableN;
|
|
154
|
+
if (total === 0) {
|
|
155
|
+
return unavailable('no dispatch in this window recorded both payload_hash and cache_hit');
|
|
156
|
+
}
|
|
157
|
+
return stableN / total;
|
|
158
|
+
}
|
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The missing seam: two candidate records over one observation, and one signed
|
|
3
|
+
* delta per trial.
|
|
4
|
+
*
|
|
5
|
+
* A recorded search over this tree established that nothing read two
|
|
6
|
+
* {@link CandidateRecord}s and emitted a delta, a {@link PairedVerdict} or a
|
|
7
|
+
* {@link MetricVector}. The only live `decidePairedVerdict` caller runs over
|
|
8
|
+
* benchmark task pairs, the only paired-row construction is a deserialiser, and
|
|
9
|
+
* the evaluation cascade takes its vector from the caller. So the comparison a
|
|
10
|
+
* two-arm proposer experiment needs had no producer at all: the verdict
|
|
11
|
+
* function existed and nothing could feed it from candidates.
|
|
12
|
+
*
|
|
13
|
+
* This module is that producer, and it is deliberately the only thing it is.
|
|
14
|
+
*
|
|
15
|
+
* Everything about the SHAPE of the comparison is derived from constants
|
|
16
|
+
* committed before either arm existed: the sign convention from
|
|
17
|
+
* {@link PairedInput}'s own contract, the direction handling from
|
|
18
|
+
* {@link MetricDirection}, the tie epsilon from the value the A/B report
|
|
19
|
+
* already uses, the aggregation and the significance bar from
|
|
20
|
+
* {@link decidePairedVerdict}. What is NOT here is the outcome scalar itself.
|
|
21
|
+
* {@link TrialOutcome} is supplied by a caller, because choosing what a trial
|
|
22
|
+
* MEASURES is the one decision in this experiment that could favour an arm, and
|
|
23
|
+
* a module that invented one would be answering it in the wrong place. The
|
|
24
|
+
* artifact-count delta is supplied on the same terms and for a sharper reason:
|
|
25
|
+
* the pairing makes every pair-derived path count differ by exactly 0, so a
|
|
26
|
+
* derivation here would report a constant and call it a measurement.
|
|
27
|
+
*
|
|
28
|
+
* The honest consequence, stated rather than left to be discovered: this
|
|
29
|
+
* producer has no live population today. No shipped evaluator emits a
|
|
30
|
+
* {@link TrialOutcome} for a candidate over the frozen corpus, so every test
|
|
31
|
+
* here supplies its own. A producer with no population is exactly as honest as
|
|
32
|
+
* a guard with no caller, and the same disclosure applies.
|
|
33
|
+
*/
|
|
34
|
+
|
|
35
|
+
import type { CandidateRecord } from './candidate_record.js';
|
|
36
|
+
import {
|
|
37
|
+
ARTIFACT_COUNT_METRIC,
|
|
38
|
+
buildVector,
|
|
39
|
+
type MetricDirection,
|
|
40
|
+
type MetricRow,
|
|
41
|
+
type MetricVector,
|
|
42
|
+
} from './evaluation_vector.js';
|
|
43
|
+
import { decidePairedVerdict, type PairedVerdict } from './paired_verdict.js';
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Deltas within this of zero are ties.
|
|
47
|
+
*
|
|
48
|
+
* NOT chosen here: it is the value the A/B report's own direction counts
|
|
49
|
+
* already use, and its reason is recorded there, that two definitions of a tie
|
|
50
|
+
* in one report is how the numbers start disagreeing. Restating that value
|
|
51
|
+
* rather than picking a fresh one keeps a candidate comparison and a bench
|
|
52
|
+
* comparison answering to the same notion of no difference.
|
|
53
|
+
*/
|
|
54
|
+
export const TIE_EPSILON = 1e-9;
|
|
55
|
+
|
|
56
|
+
export class PairingError extends Error {
|
|
57
|
+
constructor(message: string) {
|
|
58
|
+
super(message);
|
|
59
|
+
this.name = 'PairingError';
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* The pairing key, derived from the record alone.
|
|
65
|
+
*
|
|
66
|
+
* A record carries no observation field, so the key is reconstructed from what
|
|
67
|
+
* it does carry: the mutation dimension and the sorted mutation paths. That is
|
|
68
|
+
* exactly the observation identity for this experiment, because each fixed
|
|
69
|
+
* recipe owns one dimension and each observation names one subject. Keying on
|
|
70
|
+
* the candidate id would pair nothing, since an id hashes the mutated bytes, so
|
|
71
|
+
* two arms that produced different text have different ids by construction,
|
|
72
|
+
* which is the whole reason there is something to compare.
|
|
73
|
+
*/
|
|
74
|
+
export function pairingKey(record: CandidateRecord): string {
|
|
75
|
+
const paths = record.mutations.map((m) => m.path).sort((a, b) => (a < b ? -1 : a > b ? 1 : 0));
|
|
76
|
+
return `${record.dimension} ${paths.join(' ')}`;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export interface CandidatePair {
|
|
80
|
+
readonly key: string;
|
|
81
|
+
readonly control: CandidateRecord;
|
|
82
|
+
readonly treatment: CandidateRecord;
|
|
83
|
+
/**
|
|
84
|
+
* The arms produced byte-identical mutations.
|
|
85
|
+
*
|
|
86
|
+
* Not an error and not silently dropped. It means every trial on this pair
|
|
87
|
+
* is a structural tie, so the pair contributes to the trial count and to no
|
|
88
|
+
* discordant count, and a reader who sees `underpowered` without this flag
|
|
89
|
+
* would look for the cause in the measurement rather than in the arms.
|
|
90
|
+
*/
|
|
91
|
+
readonly identical_arms: boolean;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function serialiseMutations(r: CandidateRecord): string {
|
|
95
|
+
return [...r.mutations]
|
|
96
|
+
.sort((a, b) => (a.path < b.path ? -1 : a.path > b.path ? 1 : 0))
|
|
97
|
+
.map((m) => `${m.path} ${m.content}`)
|
|
98
|
+
.join('');
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Pair two arms' record sets, refusing every shape where half a pair exists.
|
|
103
|
+
*
|
|
104
|
+
* An unmatched record on either side is refused rather than dropped. Dropping
|
|
105
|
+
* it would make the trial count depend on which observations each arm happened
|
|
106
|
+
* to satisfy, and a comparison whose denominator is chosen by one of the arms
|
|
107
|
+
* is not a paired comparison.
|
|
108
|
+
*/
|
|
109
|
+
export function pairCandidates(
|
|
110
|
+
control: readonly CandidateRecord[],
|
|
111
|
+
treatment: readonly CandidateRecord[],
|
|
112
|
+
): CandidatePair[] {
|
|
113
|
+
const byKey = new Map<string, CandidateRecord>();
|
|
114
|
+
for (const r of treatment) {
|
|
115
|
+
const k = pairingKey(r);
|
|
116
|
+
if (byKey.has(k)) {
|
|
117
|
+
throw new PairingError(
|
|
118
|
+
`treatment arm emitted two records for '${k}': one observation is one candidate ` +
|
|
119
|
+
'per arm, and a second would make the pairing pick a winner before any trial ran',
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
byKey.set(k, r);
|
|
123
|
+
}
|
|
124
|
+
const out: CandidatePair[] = [];
|
|
125
|
+
const seen = new Set<string>();
|
|
126
|
+
for (const c of control) {
|
|
127
|
+
const k = pairingKey(c);
|
|
128
|
+
if (seen.has(k)) {
|
|
129
|
+
throw new PairingError(`control arm emitted two records for '${k}'`);
|
|
130
|
+
}
|
|
131
|
+
seen.add(k);
|
|
132
|
+
const t = byKey.get(k);
|
|
133
|
+
if (t === undefined) {
|
|
134
|
+
throw new PairingError(
|
|
135
|
+
`no treatment record for '${k}': half a pair is no result, not a partial one`,
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
out.push({
|
|
139
|
+
key: k,
|
|
140
|
+
control: c,
|
|
141
|
+
treatment: t,
|
|
142
|
+
identical_arms: serialiseMutations(c) === serialiseMutations(t),
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
for (const k of byKey.keys()) {
|
|
146
|
+
if (!seen.has(k)) {
|
|
147
|
+
throw new PairingError(
|
|
148
|
+
`no control record for '${k}': the treatment arm ran an observation the control did not`,
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
return out;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* One measured trial: the same scalar, on both arms, under the same conditions.
|
|
157
|
+
*
|
|
158
|
+
* Both values are required. An absent arm is refused rather than imputed,
|
|
159
|
+
* because imputing it would put a number the run did not observe into a
|
|
160
|
+
* denominator that decides a verdict.
|
|
161
|
+
*/
|
|
162
|
+
export interface TrialOutcome {
|
|
163
|
+
/**
|
|
164
|
+
* Unique across the WHOLE comparison, not merely within its pair.
|
|
165
|
+
*
|
|
166
|
+
* Stated rather than left implicit, because the two readings differ in what
|
|
167
|
+
* they refuse and the ambiguity was the defect. `pairedDeltas` refuses a
|
|
168
|
+
* repeat within one call and cannot see across pairs; {@link compareArms}
|
|
169
|
+
* pools every pair's deltas into ONE sample, and the denominator of that
|
|
170
|
+
* sample is what the sign test adjudicates. A caller that derives the
|
|
171
|
+
* outcomes map wrongly — the realistic shape being the same measurement
|
|
172
|
+
* array reached under two keys — then contributes one set of deltas twice.
|
|
173
|
+
* Under a pair-scoped reading that is undetectable; under this one it is a
|
|
174
|
+
* refusal, and the cost is that a caller names its trials per pair.
|
|
175
|
+
*/
|
|
176
|
+
readonly trial_id: string;
|
|
177
|
+
readonly control: number;
|
|
178
|
+
readonly treatment: number;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* The signed deltas for one pair. Positive favours the TREATMENT, always.
|
|
183
|
+
*
|
|
184
|
+
* The convention is not this module's to set: the verdict's own input contract
|
|
185
|
+
* states one signed delta per trial with positive favouring the treatment, so
|
|
186
|
+
* the direction handling here is a translation of {@link MetricDirection} into
|
|
187
|
+
* that fixed convention rather than a second convention beside it. For a
|
|
188
|
+
* lower-better metric the subtraction is reversed, so a caller never has to
|
|
189
|
+
* remember which endpoint it is looking at.
|
|
190
|
+
*/
|
|
191
|
+
export function pairedDeltas(
|
|
192
|
+
outcomes: readonly TrialOutcome[],
|
|
193
|
+
direction: MetricDirection,
|
|
194
|
+
): number[] {
|
|
195
|
+
if (outcomes.length === 0) {
|
|
196
|
+
throw new PairingError(
|
|
197
|
+
'no trials: a verdict over zero trials is a fabricated result, not an empty one',
|
|
198
|
+
);
|
|
199
|
+
}
|
|
200
|
+
const ids = new Set<string>();
|
|
201
|
+
for (const o of outcomes) {
|
|
202
|
+
if (ids.has(o.trial_id)) {
|
|
203
|
+
throw new PairingError(
|
|
204
|
+
`duplicate trial id '${o.trial_id}': one trial counted twice inflates the discordant count`,
|
|
205
|
+
);
|
|
206
|
+
}
|
|
207
|
+
ids.add(o.trial_id);
|
|
208
|
+
if (!Number.isFinite(o.control) || !Number.isFinite(o.treatment)) {
|
|
209
|
+
throw new PairingError(
|
|
210
|
+
`trial '${o.trial_id}' carries a non-finite outcome: an unmeasured trial is absent, never zero`,
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
return outcomes.map((o) =>
|
|
215
|
+
direction === 'higher-better' ? o.treatment - o.control : o.control - o.treatment,
|
|
216
|
+
);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
export interface PairComparison {
|
|
220
|
+
readonly pairs: readonly CandidatePair[];
|
|
221
|
+
readonly trials: number;
|
|
222
|
+
readonly verdict: PairedVerdict;
|
|
223
|
+
/** Pairs whose arms produced identical bytes, so every one of their trials tied. */
|
|
224
|
+
readonly identical_pairs: readonly string[];
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
export interface ComparisonInput {
|
|
228
|
+
readonly control: readonly CandidateRecord[];
|
|
229
|
+
readonly treatment: readonly CandidateRecord[];
|
|
230
|
+
/** Trials keyed by pairing key. Every pair must carry at least one. */
|
|
231
|
+
readonly outcomes: Readonly<Record<string, readonly TrialOutcome[]>>;
|
|
232
|
+
readonly direction: MetricDirection;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* The comparison, pooled across pairs.
|
|
237
|
+
*
|
|
238
|
+
* Pooling is what raises the experiment off the discordant floor: with one
|
|
239
|
+
* delta per pair the trial count equals the corpus size, which sits exactly on
|
|
240
|
+
* the derived floor, so a single dissent makes a pass arithmetically
|
|
241
|
+
* unreachable before the run starts. Pooling per-trial deltas across pairs
|
|
242
|
+
* makes the count the number of TRIALS rather than the number of pairs.
|
|
243
|
+
*
|
|
244
|
+
* The assumption that buys it is stated rather than assumed away: the exact
|
|
245
|
+
* sign test treats each delta as an independent draw, and trials on the same
|
|
246
|
+
* pair share a subject. A caller that cannot defend that independence should
|
|
247
|
+
* pass one aggregate delta per pair and accept the smaller sample, which this
|
|
248
|
+
* function supports by simply being given fewer trials.
|
|
249
|
+
*/
|
|
250
|
+
export function compareArms(input: ComparisonInput): PairComparison {
|
|
251
|
+
const pairs = pairCandidates(input.control, input.treatment);
|
|
252
|
+
const keys = new Set(pairs.map((p) => p.key));
|
|
253
|
+
for (const k of Object.keys(input.outcomes)) {
|
|
254
|
+
if (!keys.has(k)) {
|
|
255
|
+
throw new PairingError(
|
|
256
|
+
`outcomes carry key '${k}', which matches no pair: the key is a formatted string ` +
|
|
257
|
+
'the caller derives rather than receives, so an unmatched one is a mis-derived ' +
|
|
258
|
+
'or stale measurement set, and pooling only the keys that happened to match ' +
|
|
259
|
+
'is the same silently smaller denominator the opposite refusal names',
|
|
260
|
+
);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
const deltas: number[] = [];
|
|
264
|
+
const seenTrials = new Set<string>();
|
|
265
|
+
for (const p of pairs) {
|
|
266
|
+
const trials = input.outcomes[p.key];
|
|
267
|
+
if (trials === undefined || trials.length === 0) {
|
|
268
|
+
throw new PairingError(
|
|
269
|
+
`pair '${p.key}' has no trials: a pair present in the arms and absent from the ` +
|
|
270
|
+
'measurements silently shrinks the denominator',
|
|
271
|
+
);
|
|
272
|
+
}
|
|
273
|
+
for (const t of trials) {
|
|
274
|
+
if (seenTrials.has(t.trial_id)) {
|
|
275
|
+
throw new PairingError(
|
|
276
|
+
`trial id '${t.trial_id}' appears on more than one pair: the refusal in ` +
|
|
277
|
+
'pairedDeltas is scoped to one pair and cannot see a reuse across pairs, ' +
|
|
278
|
+
'and one trial counted twice inflates the discordant count either way',
|
|
279
|
+
);
|
|
280
|
+
}
|
|
281
|
+
seenTrials.add(t.trial_id);
|
|
282
|
+
}
|
|
283
|
+
deltas.push(...pairedDeltas(trials, input.direction));
|
|
284
|
+
}
|
|
285
|
+
return {
|
|
286
|
+
pairs,
|
|
287
|
+
trials: deltas.length,
|
|
288
|
+
verdict: decidePairedVerdict({ deltas, tieEpsilon: TIE_EPSILON }),
|
|
289
|
+
identical_pairs: pairs.filter((p) => p.identical_arms).map((p) => p.key),
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* The comparison as a metric vector the evaluation cascade can consume.
|
|
295
|
+
*
|
|
296
|
+
* The artifact-count row is not optional: {@link buildVector} refuses a vector
|
|
297
|
+
* without it, so a candidate cannot reach the promotion verdict without its
|
|
298
|
+
* artifact delta having been measured. The delta is SUPPLIED here rather than
|
|
299
|
+
* derived, and the reason is the same one that keeps {@link TrialOutcome} out
|
|
300
|
+
* of this module.
|
|
301
|
+
*
|
|
302
|
+
* Why this module cannot derive it. {@link pairingKey} folds the sorted mutation-path list into the key and
|
|
303
|
+
* {@link pairCandidates} forms a pair only on an exact key match, so control
|
|
304
|
+
* and treatment of every pair carry identical path multisets by construction.
|
|
305
|
+
* Any count taken over those paths therefore differs by exactly 0 for every
|
|
306
|
+
* pair and every candidate — not usually, but always. A row filled that way
|
|
307
|
+
* satisfies the builder's SHAPE check while making the ceiling branch in
|
|
308
|
+
* `promotionVerdict` unreachable, which defeats the invariant the shape
|
|
309
|
+
* check exists to carry: the vector would be refused for omitting the row and
|
|
310
|
+
* accepted for carrying a constant.
|
|
311
|
+
*
|
|
312
|
+
* The quantity the gate actually names is baseline-relative growth — how many
|
|
313
|
+
* artifacts the candidate ADDS to the tree it is a variant of. That is a fact
|
|
314
|
+
* about the candidate against an unmutated baseline, and this module sees
|
|
315
|
+
* neither the baseline nor the filesystem. So the caller that knows which
|
|
316
|
+
* mutated paths are new files passes the count, and a caller that has not
|
|
317
|
+
* measured it has nothing to pass.
|
|
318
|
+
*
|
|
319
|
+
* @param artifactDelta signed count of artifacts the candidate adds against
|
|
320
|
+
* its baseline. Must be a finite integer; a fractional or non-finite value is
|
|
321
|
+
* refused rather than rounded, because a rounded ceiling comparison is a
|
|
322
|
+
* verdict on a number nobody measured.
|
|
323
|
+
* @throws {PairingError} when `artifactDelta` is not a finite integer.
|
|
324
|
+
*/
|
|
325
|
+
export function comparisonVector(
|
|
326
|
+
candidate_id: string,
|
|
327
|
+
metric: string,
|
|
328
|
+
direction: MetricDirection,
|
|
329
|
+
comparison: PairComparison,
|
|
330
|
+
artifactDelta: number,
|
|
331
|
+
): MetricVector {
|
|
332
|
+
if (!Number.isInteger(artifactDelta)) {
|
|
333
|
+
throw new PairingError(
|
|
334
|
+
`artifact delta ${String(artifactDelta)} is not a finite integer: the artifact-count ` +
|
|
335
|
+
'row is a count of artifacts, and a gate that compared a fraction against its ' +
|
|
336
|
+
'ceiling would be adjudicating a quantity the run never observed',
|
|
337
|
+
);
|
|
338
|
+
}
|
|
339
|
+
const rows: MetricRow[] = [
|
|
340
|
+
{ kind: 'paired', metric, direction, verdict: comparison.verdict },
|
|
341
|
+
{
|
|
342
|
+
kind: 'counted',
|
|
343
|
+
metric: ARTIFACT_COUNT_METRIC,
|
|
344
|
+
direction: 'lower-better',
|
|
345
|
+
delta: artifactDelta,
|
|
346
|
+
},
|
|
347
|
+
];
|
|
348
|
+
return buildVector(candidate_id, rows);
|
|
349
|
+
}
|