@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,279 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reviewer assignment under a budget `k`, with provider diversity preserved.
|
|
3
|
+
*
|
|
4
|
+
* `road-to-inbox-harvest-2026-08-e-council-topology-evidence` steps 8.2 and 8.3.
|
|
5
|
+
*
|
|
6
|
+
* ## What the shipped path does today, measured before anything was designed
|
|
7
|
+
*
|
|
8
|
+
* `orchestrator.ts:1509-1560` gives **every** reviewer **every** other member's
|
|
9
|
+
* answer: N reviewers × (N−1) candidates = **N(N−1) reviewed pairs**, batched
|
|
10
|
+
* into N provider calls. The pair count is the quantity that grows
|
|
11
|
+
* quadratically and the one that drives prompt size, latency and — once the
|
|
12
|
+
* cross-exam of 8.1 makes pairs individually addressable — call count. At N=8
|
|
13
|
+
* that is 56 pairs; at N=16 it is 240.
|
|
14
|
+
*
|
|
15
|
+
* ## The construction, and why it is a circulant
|
|
16
|
+
*
|
|
17
|
+
* Order the members so provider families interleave, then let the candidate at
|
|
18
|
+
* position `i` be reviewed by positions `i+1 … i+k` (mod N). This gives, for
|
|
19
|
+
* free and without search:
|
|
20
|
+
*
|
|
21
|
+
* - **no self-review** — the offsets start at 1 and stop before N;
|
|
22
|
+
* - **exact balance** — every candidate is reviewed exactly `k` times and
|
|
23
|
+
* every reviewer reviews exactly `k` candidates, so no member's context
|
|
24
|
+
* window carries more than another's;
|
|
25
|
+
* - **O(N×k) pairs** — `N·k`, against the unconditional `N(N−1)`;
|
|
26
|
+
* - **determinism** — a permutation seeded from the question text, replayable
|
|
27
|
+
* from the artefact, never `Math.random` and never `Date`, which is the
|
|
28
|
+
* same discipline `orchestrator.ts:1533-1543` already states for its own
|
|
29
|
+
* shuffle.
|
|
30
|
+
*
|
|
31
|
+
* Interleaving alone does NOT guarantee 8.3 when families are unequal (six of
|
|
32
|
+
* one family and two of another interleaves to `A B A B A A A A`, whose tail is
|
|
33
|
+
* all one family), so a **repair pass** follows: any candidate whose reviewer
|
|
34
|
+
* set is entirely same-family is fixed by a 2-swap with another candidate. A
|
|
35
|
+
* swap exchanges one reviewer between two candidates, so every load count is
|
|
36
|
+
* unchanged by construction — balance survives the repair.
|
|
37
|
+
*
|
|
38
|
+
* ## Honest scope
|
|
39
|
+
*
|
|
40
|
+
* This is the assignment; **nothing calls it yet**. `k = N−1` reproduces the
|
|
41
|
+
* shipped all-pairs behaviour exactly, which is what makes it a safe default
|
|
42
|
+
* and what lets the comparison below be arithmetic rather than a claim about a
|
|
43
|
+
* change nobody has made.
|
|
44
|
+
*
|
|
45
|
+
* Under the engine's current one-advisor-per-provider invariant
|
|
46
|
+
* (`chairman.ts:16-18`) every member is its own family, so the diversity
|
|
47
|
+
* constraint is **vacuously satisfied in production today**. It is implemented
|
|
48
|
+
* and tested against multi-member-per-family sets anyway, because Phase 9's
|
|
49
|
+
* advisor fan-out is what would produce them and the cheapest moment to have
|
|
50
|
+
* the property is before it can be violated.
|
|
51
|
+
*
|
|
52
|
+
* Pure and offline: no provider call, no file read.
|
|
53
|
+
*/
|
|
54
|
+
import { deterministic_shuffle_indices } from './blind_review.js';
|
|
55
|
+
|
|
56
|
+
/** A council member, as assignment needs it. */
|
|
57
|
+
export interface ReviewMember {
|
|
58
|
+
/** Member name — the identity used in the assignment maps. */
|
|
59
|
+
readonly name: string;
|
|
60
|
+
/** Provider family (`anthropic`, `openai`, …). Diversity is defined over this. */
|
|
61
|
+
readonly family: string;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** Candidate name → the reviewers assigned to it, in deterministic order. */
|
|
65
|
+
export type Assignment = ReadonlyMap<string, readonly string[]>;
|
|
66
|
+
|
|
67
|
+
export interface AssignmentResult {
|
|
68
|
+
readonly assignment: Assignment;
|
|
69
|
+
/** Reviewed pairs — the quantity 8.2 compares against both curves. */
|
|
70
|
+
readonly pairs: number;
|
|
71
|
+
/** Reviewers per candidate actually used, i.e. `min(k, N-1)`. */
|
|
72
|
+
readonly effectiveK: number;
|
|
73
|
+
/** How many 2-swaps the diversity repair performed. */
|
|
74
|
+
readonly diversityRepairs: number;
|
|
75
|
+
/**
|
|
76
|
+
* Candidates left with an all-same-family reviewer set.
|
|
77
|
+
*
|
|
78
|
+
* "Available" in 8.3 has to mean available UNDER THE BALANCE CONSTRAINT,
|
|
79
|
+
* not merely "a member of another family exists". Six members of family A
|
|
80
|
+
* and two of family B at `k = 1` gives eight candidates and only two
|
|
81
|
+
* B-reviewer slots, so four A candidates MUST be reviewed within their own
|
|
82
|
+
* family — no assignment avoids it. {@link diversityCeiling} is that bound
|
|
83
|
+
* made computable, and this list is the shortfall it predicts.
|
|
84
|
+
*/
|
|
85
|
+
readonly unrepairable: readonly string[];
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* The most candidates any balanced assignment can make diverse at this `(members, k)`.
|
|
90
|
+
*
|
|
91
|
+
* A candidate of family `F` needs a reviewer from outside `F`; there are
|
|
92
|
+
* `n - |F|` such members and each reviews exactly `k` candidates, so family `F`
|
|
93
|
+
* can place at most `min(|F|, k * (n - |F|))` diverse candidates. Summed over
|
|
94
|
+
* families and capped at `n`.
|
|
95
|
+
*
|
|
96
|
+
* An UPPER BOUND, and stated as one: reviewer capacity is shared across
|
|
97
|
+
* families, so a contrived family profile could in principle fall short of it.
|
|
98
|
+
* Measured over 7 family profiles × every `k` in `1..n-1`, the greedy repair in
|
|
99
|
+
* {@link assignReviewers} attains it in every case, and the test sweeps a wider
|
|
100
|
+
* set — so the bound is treated as tight where it has been checked and as a
|
|
101
|
+
* bound everywhere else.
|
|
102
|
+
*/
|
|
103
|
+
export function diversityCeiling(members: readonly ReviewMember[], k: number): number {
|
|
104
|
+
const n = members.length;
|
|
105
|
+
const eff = Math.max(0, Math.min(k, n - 1));
|
|
106
|
+
const sizes = new Map<string, number>();
|
|
107
|
+
for (const m of members) sizes.set(m.family, (sizes.get(m.family) ?? 0) + 1);
|
|
108
|
+
let total = 0;
|
|
109
|
+
for (const size of sizes.values()) total += Math.min(size, eff * (n - size));
|
|
110
|
+
return Math.min(total, n);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/** Both curves 8.2 names, at one `(N, k)`. Pure arithmetic. */
|
|
114
|
+
export function costCurves(n: number, k: number): {
|
|
115
|
+
n: number;
|
|
116
|
+
k: number;
|
|
117
|
+
pairs_quadratic: number;
|
|
118
|
+
pairs_linear: number;
|
|
119
|
+
ratio: number;
|
|
120
|
+
} {
|
|
121
|
+
const eff = Math.max(0, Math.min(k, n - 1));
|
|
122
|
+
const quad = n * (n - 1);
|
|
123
|
+
const lin = n * eff;
|
|
124
|
+
return { n, k: eff, pairs_quadratic: quad, pairs_linear: lin, ratio: quad === 0 ? 1 : lin / quad };
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Interleave members so that consecutive positions come from different families
|
|
129
|
+
* wherever the family sizes allow it: repeatedly take one member from the
|
|
130
|
+
* currently largest remaining family, preferring one different from the last
|
|
131
|
+
* pick.
|
|
132
|
+
*
|
|
133
|
+
* TIES BREAK ON INPUT ORDER, NOT ON FAMILY NAME, and the difference is
|
|
134
|
+
* load-bearing rather than stylistic. Under the engine's one-advisor-per-
|
|
135
|
+
* provider invariant every family has size 1, so every comparison is a tie — a
|
|
136
|
+
* name tie-break would then sort the whole council alphabetically and DISCARD
|
|
137
|
+
* the seeded permutation entirely, making `assignReviewers` produce the same
|
|
138
|
+
* assignment for every seed. Measured: the seed-sensitivity test reds under the
|
|
139
|
+
* name tie-break at N=8. Bucket iteration order is the (already permuted) input
|
|
140
|
+
* order and `Array.prototype.sort` is stable, so input-order ties keep the
|
|
141
|
+
* result a pure function of the input while letting the seed through.
|
|
142
|
+
*/
|
|
143
|
+
export function interleaveByFamily(members: readonly ReviewMember[]): ReviewMember[] {
|
|
144
|
+
const buckets = new Map<string, ReviewMember[]>();
|
|
145
|
+
for (const m of members) {
|
|
146
|
+
const b = buckets.get(m.family);
|
|
147
|
+
if (b) b.push(m);
|
|
148
|
+
else buckets.set(m.family, [m]);
|
|
149
|
+
}
|
|
150
|
+
const out: ReviewMember[] = [];
|
|
151
|
+
let lastFamily = '';
|
|
152
|
+
while (out.length < members.length) {
|
|
153
|
+
const eligible = [...buckets.entries()].filter(([, v]) => v.length > 0);
|
|
154
|
+
// Prefer a family different from the previous pick; fall back to any.
|
|
155
|
+
const pool = eligible.filter(([f]) => f !== lastFamily);
|
|
156
|
+
const pick = (pool.length > 0 ? pool : eligible).sort((a, b) => b[1].length - a[1].length)[0];
|
|
157
|
+
if (pick === undefined) break;
|
|
158
|
+
out.push((pick[1] as ReviewMember[]).shift() as ReviewMember);
|
|
159
|
+
lastFamily = pick[0] as string;
|
|
160
|
+
}
|
|
161
|
+
return out;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/** Is this candidate reviewed by at least one different family? */
|
|
165
|
+
function _diverse(candidate: ReviewMember, reviewers: readonly string[], byName: ReadonlyMap<string, ReviewMember>): boolean {
|
|
166
|
+
return reviewers.some((r) => (byName.get(r) as ReviewMember).family !== candidate.family);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Assign `k` reviewers per candidate: balanced, self-review-free, diverse where
|
|
171
|
+
* a cross-family reviewer exists, deterministic in `seed`.
|
|
172
|
+
*
|
|
173
|
+
* `k >= n-1` reproduces the shipped all-pairs behaviour exactly.
|
|
174
|
+
*/
|
|
175
|
+
export function assignReviewers(
|
|
176
|
+
members: readonly ReviewMember[],
|
|
177
|
+
k: number,
|
|
178
|
+
seed: string,
|
|
179
|
+
): AssignmentResult {
|
|
180
|
+
const n = members.length;
|
|
181
|
+
const effectiveK = Math.max(0, Math.min(k, n - 1));
|
|
182
|
+
const byName = new Map(members.map((m) => [m.name, m]));
|
|
183
|
+
if (n < 2 || effectiveK === 0) {
|
|
184
|
+
return {
|
|
185
|
+
assignment: new Map(members.map((m) => [m.name, [] as string[]])),
|
|
186
|
+
pairs: 0,
|
|
187
|
+
effectiveK: 0,
|
|
188
|
+
diversityRepairs: 0,
|
|
189
|
+
unrepairable: [],
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Seeded permutation first, then the family interleave over it: the seed
|
|
194
|
+
// decides WHICH member sits where within a family, the interleave decides
|
|
195
|
+
// that consecutive positions differ in family.
|
|
196
|
+
const perm = deterministic_shuffle_indices(seed, n);
|
|
197
|
+
const ordered = interleaveByFamily(perm.map((i) => members[i] as ReviewMember));
|
|
198
|
+
|
|
199
|
+
const assignment = new Map<string, string[]>();
|
|
200
|
+
for (let i = 0; i < n; i++) {
|
|
201
|
+
const candidate = ordered[i] as ReviewMember;
|
|
202
|
+
const reviewers: string[] = [];
|
|
203
|
+
for (let d = 1; d <= effectiveK; d++) {
|
|
204
|
+
reviewers.push((ordered[(i + d) % n] as ReviewMember).name);
|
|
205
|
+
}
|
|
206
|
+
assignment.set(candidate.name, reviewers);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// ── diversity repair ────────────────────────────────────────────────────
|
|
210
|
+
// A 2-swap exchanges one reviewer between two candidates, so every
|
|
211
|
+
// reviewer's load and every candidate's count are invariant.
|
|
212
|
+
let diversityRepairs = 0;
|
|
213
|
+
const unrepairable: string[] = [];
|
|
214
|
+
const familyCounts = new Map<string, number>();
|
|
215
|
+
for (const m of members) familyCounts.set(m.family, (familyCounts.get(m.family) ?? 0) + 1);
|
|
216
|
+
|
|
217
|
+
for (const candidate of ordered) {
|
|
218
|
+
const mine = assignment.get(candidate.name) as string[];
|
|
219
|
+
if (_diverse(candidate, mine, byName)) continue;
|
|
220
|
+
// Does a cross-family reviewer even exist for this candidate?
|
|
221
|
+
if ((familyCounts.get(candidate.family) ?? 0) === n) {
|
|
222
|
+
unrepairable.push(candidate.name);
|
|
223
|
+
continue;
|
|
224
|
+
}
|
|
225
|
+
let fixed = false;
|
|
226
|
+
for (const other of ordered) {
|
|
227
|
+
if (other.name === candidate.name || fixed) continue;
|
|
228
|
+
const theirs = assignment.get(other.name) as string[];
|
|
229
|
+
for (const give of mine) {
|
|
230
|
+
for (const take of theirs) {
|
|
231
|
+
if (fixed) break;
|
|
232
|
+
if (take === candidate.name || give === other.name) continue; // no self-review
|
|
233
|
+
if (theirs.includes(give) || mine.includes(take)) continue; // no duplicate reviewer
|
|
234
|
+
const takeM = byName.get(take) as ReviewMember;
|
|
235
|
+
if (takeM.family === candidate.family) continue; // does not help
|
|
236
|
+
const theirsAfter = theirs.map((r) => (r === take ? give : r));
|
|
237
|
+
if (!_diverse(other, theirsAfter, byName)) continue; // must not break them
|
|
238
|
+
mine[mine.indexOf(give)] = take;
|
|
239
|
+
assignment.set(other.name, theirsAfter);
|
|
240
|
+
diversityRepairs++;
|
|
241
|
+
fixed = true;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
if (!fixed) unrepairable.push(candidate.name);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
let pairs = 0;
|
|
249
|
+
for (const v of assignment.values()) pairs += v.length;
|
|
250
|
+
return { assignment, pairs, effectiveK, diversityRepairs, unrepairable };
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/** Reviewer → how many candidates it reviews. The balance figure. */
|
|
254
|
+
export function reviewerLoads(assignment: Assignment): Map<string, number> {
|
|
255
|
+
const loads = new Map<string, number>();
|
|
256
|
+
for (const reviewers of assignment.values()) {
|
|
257
|
+
for (const r of reviewers) loads.set(r, (loads.get(r) ?? 0) + 1);
|
|
258
|
+
}
|
|
259
|
+
return loads;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/** Candidates reviewed only by their own family, given the members. */
|
|
263
|
+
export function nonDiverseCandidates(assignment: Assignment, members: readonly ReviewMember[]): string[] {
|
|
264
|
+
const byName = new Map(members.map((m) => [m.name, m]));
|
|
265
|
+
const out: string[] = [];
|
|
266
|
+
for (const [name, reviewers] of assignment) {
|
|
267
|
+
const c = byName.get(name);
|
|
268
|
+
if (c === undefined || reviewers.length === 0) continue;
|
|
269
|
+
if (!_diverse(c, reviewers, byName)) out.push(name);
|
|
270
|
+
}
|
|
271
|
+
return out.sort();
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/** Was a cross-family reviewer available for this candidate at all? */
|
|
275
|
+
export function crossFamilyAvailable(candidate: string, members: readonly ReviewMember[]): boolean {
|
|
276
|
+
const c = members.find((m) => m.name === candidate);
|
|
277
|
+
if (c === undefined) return false;
|
|
278
|
+
return members.some((m) => m.name !== candidate && m.family !== c.family);
|
|
279
|
+
}
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The offline routing-training row schema — step 11.1.
|
|
3
|
+
*
|
|
4
|
+
* `road-to-inbox-harvest-2026-08-e-council-topology-evidence` step 11.1:
|
|
5
|
+
* *"Collect offline training rows from benchmark and dogfood evidence only,
|
|
6
|
+
* without requiring raw private prompt content"*, verified by *"the row schema
|
|
7
|
+
* has no field capable of holding prompt text"*.
|
|
8
|
+
*
|
|
9
|
+
* ## PII-exclusion by construction, not by scrubbing
|
|
10
|
+
*
|
|
11
|
+
* The privacy property is a property of the TYPE, not of a sanitisation pass
|
|
12
|
+
* that can fail: **no field accepts free text**. Every field is an integer, a
|
|
13
|
+
* boolean, or an enum whose legal values are a declared closed set. A row that
|
|
14
|
+
* cannot hold a sentence has no scrubber to forget to run — the same principle
|
|
15
|
+
* `domain-safety-pii` § Surface 2 applies to logs and
|
|
16
|
+
* `artifact-engagement-recording` applies to telemetry.
|
|
17
|
+
*
|
|
18
|
+
* There is deliberately no `payload`, `notes`, `extra`, `context`,
|
|
19
|
+
* `promptText`, `Record<string, unknown>` or `unknown`-typed field, and
|
|
20
|
+
* {@link auditRowSchema} rejects one if a later edit adds it.
|
|
21
|
+
*
|
|
22
|
+
* ## Two layers, because one is not enough
|
|
23
|
+
*
|
|
24
|
+
* 1. {@link ROW_FIELDS} is the declared manifest — name plus kind plus, for
|
|
25
|
+
* an enum, the closed value set. {@link auditRowSchema} walks a row
|
|
26
|
+
* against it and refuses any field the manifest does not declare, any
|
|
27
|
+
* value outside a declared enum, and any string-shaped value at all.
|
|
28
|
+
* 2. The interface itself is checked from the OUTSIDE by
|
|
29
|
+
* `tests/scripts/ai_council/routing_training_row.test.ts`, which greps the
|
|
30
|
+
* module source for a bare `string` / `any` / `unknown` /
|
|
31
|
+
* `Record<string, …>` field declaration. The manifest alone cannot see a
|
|
32
|
+
* field somebody adds to the interface and forgets to declare.
|
|
33
|
+
*
|
|
34
|
+
* ## Honest scope — the step is NOT closed by this file
|
|
35
|
+
*
|
|
36
|
+
* This is the schema. **No rows have been collected**, and the benchmark half
|
|
37
|
+
* of the evidence 11.1 names does not exist: `blocker: phase-2-benchmark-cost`
|
|
38
|
+
* records that `topology_bench_manifest.ts` `main()` only `--emit`s JSON and
|
|
39
|
+
* contains no provider dispatch. The verify clause is discharged; the step's
|
|
40
|
+
* collection half is not, which is why 11.1 stays unchecked.
|
|
41
|
+
*
|
|
42
|
+
* Pure and offline: type declarations, one manifest, one validator.
|
|
43
|
+
*/
|
|
44
|
+
import { COUNCIL_TOPOLOGIES } from './topology_vocabulary.js';
|
|
45
|
+
import type { CouncilTopology } from './topology_vocabulary.js';
|
|
46
|
+
import type { ImpactClass } from './necessity.js';
|
|
47
|
+
|
|
48
|
+
/** Impact classes, as a runtime closed set. Mirrors `necessity.ts:545-550`. */
|
|
49
|
+
export const IMPACT_CLASSES = Object.freeze([
|
|
50
|
+
'trivial',
|
|
51
|
+
'low_impact',
|
|
52
|
+
'medium_impact',
|
|
53
|
+
'high_impact',
|
|
54
|
+
'user_required',
|
|
55
|
+
] as const);
|
|
56
|
+
|
|
57
|
+
/** Where the row came from. 11.1 admits exactly these two and no third. */
|
|
58
|
+
export const EVIDENCE_SOURCES = Object.freeze(['benchmark', 'dogfood'] as const);
|
|
59
|
+
export type EvidenceSource = (typeof EVIDENCE_SOURCES)[number];
|
|
60
|
+
|
|
61
|
+
/** Bucketed magnitudes. Buckets, never raw sizes — a raw byte count of a prompt is a weak fingerprint. */
|
|
62
|
+
export const MAGNITUDE_BUCKETS = Object.freeze(['xs', 's', 'm', 'l', 'xl'] as const);
|
|
63
|
+
export type MagnitudeBucket = (typeof MAGNITUDE_BUCKETS)[number];
|
|
64
|
+
|
|
65
|
+
/** What the run did to the verdict. The label a challenger would learn against. */
|
|
66
|
+
export const ROW_OUTCOMES = Object.freeze(['verdict-changed', 'verdict-unchanged', 'no-verdict'] as const);
|
|
67
|
+
export type RowOutcome = (typeof ROW_OUTCOMES)[number];
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* One training row.
|
|
71
|
+
*
|
|
72
|
+
* EVERY field is an integer, a boolean, or one of the closed enums above.
|
|
73
|
+
* Adding a `string`-typed field here is the violation this schema exists to
|
|
74
|
+
* make impossible, and the test file greps for exactly that.
|
|
75
|
+
*/
|
|
76
|
+
export interface RoutingTrainingRow {
|
|
77
|
+
readonly evidenceSource: EvidenceSource;
|
|
78
|
+
readonly topology: CouncilTopology;
|
|
79
|
+
readonly impactClass: ImpactClass;
|
|
80
|
+
readonly artifactSizeBucket: MagnitudeBucket;
|
|
81
|
+
readonly initialDisagreementBucket: MagnitudeBucket;
|
|
82
|
+
readonly latencyBucket: MagnitudeBucket;
|
|
83
|
+
readonly outcome: RowOutcome;
|
|
84
|
+
readonly memberCount: number;
|
|
85
|
+
readonly providerFamilyCount: number;
|
|
86
|
+
readonly roundsConfigured: number;
|
|
87
|
+
readonly roundsCompleted: number;
|
|
88
|
+
readonly priorRunFreshnessDays: number;
|
|
89
|
+
readonly estimatedCalls: number;
|
|
90
|
+
readonly observedCalls: number;
|
|
91
|
+
readonly estimatedCostCents: number;
|
|
92
|
+
readonly observedCostCents: number;
|
|
93
|
+
readonly stoppedEarly: boolean;
|
|
94
|
+
readonly minorityRetained: boolean;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** A declared field. `enum` carries its closed value set; nothing else may be a string. */
|
|
98
|
+
export type FieldKind = 'integer' | 'boolean' | 'enum';
|
|
99
|
+
|
|
100
|
+
export interface RowField {
|
|
101
|
+
readonly name: keyof RoutingTrainingRow & string;
|
|
102
|
+
readonly kind: FieldKind;
|
|
103
|
+
/** Present iff `kind === 'enum'`. The ONLY legal string values in a row. */
|
|
104
|
+
readonly values?: readonly string[];
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/** The manifest. Order is the serialisation order; the set is the schema. */
|
|
108
|
+
export const ROW_FIELDS: readonly RowField[] = Object.freeze([
|
|
109
|
+
{ name: 'evidenceSource', kind: 'enum', values: EVIDENCE_SOURCES },
|
|
110
|
+
{ name: 'topology', kind: 'enum', values: COUNCIL_TOPOLOGIES },
|
|
111
|
+
{ name: 'impactClass', kind: 'enum', values: IMPACT_CLASSES },
|
|
112
|
+
{ name: 'artifactSizeBucket', kind: 'enum', values: MAGNITUDE_BUCKETS },
|
|
113
|
+
{ name: 'initialDisagreementBucket', kind: 'enum', values: MAGNITUDE_BUCKETS },
|
|
114
|
+
{ name: 'latencyBucket', kind: 'enum', values: MAGNITUDE_BUCKETS },
|
|
115
|
+
{ name: 'outcome', kind: 'enum', values: ROW_OUTCOMES },
|
|
116
|
+
{ name: 'memberCount', kind: 'integer' },
|
|
117
|
+
{ name: 'providerFamilyCount', kind: 'integer' },
|
|
118
|
+
{ name: 'roundsConfigured', kind: 'integer' },
|
|
119
|
+
{ name: 'roundsCompleted', kind: 'integer' },
|
|
120
|
+
{ name: 'priorRunFreshnessDays', kind: 'integer' },
|
|
121
|
+
{ name: 'estimatedCalls', kind: 'integer' },
|
|
122
|
+
{ name: 'observedCalls', kind: 'integer' },
|
|
123
|
+
{ name: 'estimatedCostCents', kind: 'integer' },
|
|
124
|
+
{ name: 'observedCostCents', kind: 'integer' },
|
|
125
|
+
{ name: 'stoppedEarly', kind: 'boolean' },
|
|
126
|
+
{ name: 'minorityRetained', kind: 'boolean' },
|
|
127
|
+
] as const);
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* The longest a legal enum value may be. A closed set whose members are all
|
|
131
|
+
* short is not a text field; a "closed set" containing a paragraph is one
|
|
132
|
+
* wearing a disguise, and this is the cheapest thing that tells them apart.
|
|
133
|
+
*/
|
|
134
|
+
export const MAX_ENUM_VALUE_LENGTH = 40;
|
|
135
|
+
|
|
136
|
+
/** Every reason a row or the schema itself is rejected. Empty means clean. */
|
|
137
|
+
export function auditRowSchema(row: Record<string, unknown>): string[] {
|
|
138
|
+
const problems: string[] = [];
|
|
139
|
+
const declared = new Map(ROW_FIELDS.map((f) => [f.name as string, f]));
|
|
140
|
+
|
|
141
|
+
for (const f of ROW_FIELDS) {
|
|
142
|
+
if (f.kind === 'enum') {
|
|
143
|
+
if (f.values === undefined || f.values.length === 0) {
|
|
144
|
+
problems.push(`field \`${f.name}\` is an enum with no declared value set — that is a free-text field`);
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
for (const v of f.values) {
|
|
148
|
+
if (v.length > MAX_ENUM_VALUE_LENGTH) {
|
|
149
|
+
problems.push(
|
|
150
|
+
`field \`${f.name}\` declares the value \`${v.slice(0, 20)}…\` (${String(v.length)} chars), ` +
|
|
151
|
+
`over the ${String(MAX_ENUM_VALUE_LENGTH)}-char enum limit — a closed set of paragraphs is a text field`,
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
} else if (f.values !== undefined) {
|
|
156
|
+
problems.push(`field \`${f.name}\` is \`${f.kind}\` but declares an enum value set`);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
for (const [key, value] of Object.entries(row)) {
|
|
161
|
+
const f = declared.get(key);
|
|
162
|
+
if (f === undefined) {
|
|
163
|
+
problems.push(`row carries \`${key}\`, which the schema does not declare — undeclared fields are rejected`);
|
|
164
|
+
continue;
|
|
165
|
+
}
|
|
166
|
+
if (f.kind === 'integer') {
|
|
167
|
+
if (typeof value !== 'number' || !Number.isInteger(value)) {
|
|
168
|
+
problems.push(`field \`${key}\` must be an integer, got ${typeof value}`);
|
|
169
|
+
}
|
|
170
|
+
} else if (f.kind === 'boolean') {
|
|
171
|
+
if (typeof value !== 'boolean') problems.push(`field \`${key}\` must be a boolean, got ${typeof value}`);
|
|
172
|
+
} else if (typeof value !== 'string' || !(f.values as readonly string[]).includes(value)) {
|
|
173
|
+
problems.push(
|
|
174
|
+
`field \`${key}\` must be one of ${(f.values as readonly string[]).join(' | ')} — ` +
|
|
175
|
+
'an arbitrary string is exactly the prompt-text carrier this schema forbids',
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
for (const f of ROW_FIELDS) {
|
|
181
|
+
if (!Object.prototype.hasOwnProperty.call(row, f.name)) {
|
|
182
|
+
problems.push(`row is missing declared field \`${f.name}\``);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
return problems;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/** Serialise a validated row. Throws rather than emitting a row that failed the audit. */
|
|
189
|
+
export function serialiseRow(row: RoutingTrainingRow): string {
|
|
190
|
+
const problems = auditRowSchema(row as unknown as Record<string, unknown>);
|
|
191
|
+
if (problems.length > 0) {
|
|
192
|
+
throw new Error(`routing training row rejected:\n - ${problems.join('\n - ')}`);
|
|
193
|
+
}
|
|
194
|
+
const ordered: Record<string, unknown> = {};
|
|
195
|
+
for (const f of ROW_FIELDS) ordered[f.name] = (row as unknown as Record<string, unknown>)[f.name];
|
|
196
|
+
return JSON.stringify(ordered);
|
|
197
|
+
}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One synthesis-strategy interface behind the five candidates — step 5.1.
|
|
3
|
+
*
|
|
4
|
+
* `road-to-inbox-harvest-2026-08-e-council-topology-evidence` step 5.1: *"One
|
|
5
|
+
* synthesis-strategy interface behind the candidates (host convener, dedicated
|
|
6
|
+
* external judge, strongest configured model, top-ranked member, dual synthesis
|
|
7
|
+
* + adjudication) — no user-facing mode proliferation"*, verified by *"the
|
|
8
|
+
* user-facing surface gains no new mode names"*.
|
|
9
|
+
*
|
|
10
|
+
* ## The whole point is that this is INTERNAL
|
|
11
|
+
*
|
|
12
|
+
* Five candidates, one interface, and **zero** new flags, choices or synthesis
|
|
13
|
+
* mode names. Every strategy resolves from configuration the engine already
|
|
14
|
+
* reads — `ai_council.chairman.mode` (`host` | `member` | `auto`), the
|
|
15
|
+
* configured member name, and the optional `members.<name>.tier` — so a
|
|
16
|
+
* consumer's surface is byte-identical before and after this module. That is
|
|
17
|
+
* 12.4's rule ("consumer surfaces request capabilities, never topology names")
|
|
18
|
+
* applied to synthesis, and the test file asserts it against the CLI's own
|
|
19
|
+
* option tables rather than trusting this paragraph.
|
|
20
|
+
*
|
|
21
|
+
* ## Selection is NOT reimplemented
|
|
22
|
+
*
|
|
23
|
+
* Every strategy delegates to `select_chairman` (`chairman.ts:42`). A second
|
|
24
|
+
* selection path would be two answers to "who chairs", and the second is the one
|
|
25
|
+
* nobody updates — the same defect 1A.1 forbids for the question hash. What this
|
|
26
|
+
* module adds is a NAME for what the existing selection is doing in each
|
|
27
|
+
* configuration, so Phase 5's showdown has something to compare.
|
|
28
|
+
*
|
|
29
|
+
* ## One candidate is deliberately unreachable, and saying so is the honest part
|
|
30
|
+
*
|
|
31
|
+
* `dual_adjudicated` (dual synthesis plus adjudication) cannot be selected from
|
|
32
|
+
* today's configuration: nothing in `ai_council.chairman` expresses "run two
|
|
33
|
+
* synthesisers and adjudicate". Reaching it needs a new input — which is exactly
|
|
34
|
+
* what 5.1's verify clause forbids adding to the user surface. It is therefore
|
|
35
|
+
* declared in the vocabulary, reported as `reachable: false`, and left
|
|
36
|
+
* unselectable rather than smuggled in behind a flag.
|
|
37
|
+
*
|
|
38
|
+
* Pure: resolution and description only. The billable dispatch stays in
|
|
39
|
+
* `council_cli.ts` `_maybe_run_chairman`, exactly where it already lives.
|
|
40
|
+
*/
|
|
41
|
+
import { select_chairman } from './chairman.js';
|
|
42
|
+
import type { ChairmanCandidate, ChairmanSelection } from './chairman.js';
|
|
43
|
+
|
|
44
|
+
/** The five candidates 5.1 enumerates, in the order it enumerates them. */
|
|
45
|
+
export const SYNTHESIS_STRATEGIES = Object.freeze([
|
|
46
|
+
'host_convener',
|
|
47
|
+
'external_judge',
|
|
48
|
+
'strongest_model',
|
|
49
|
+
'top_ranked_member',
|
|
50
|
+
'dual_adjudicated',
|
|
51
|
+
] as const);
|
|
52
|
+
|
|
53
|
+
export type SynthesisStrategyId = (typeof SYNTHESIS_STRATEGIES)[number];
|
|
54
|
+
|
|
55
|
+
/** Declared arity, so an append reds the closure assertion in the test. */
|
|
56
|
+
export const SYNTHESIS_STRATEGY_ARITY = 5;
|
|
57
|
+
|
|
58
|
+
/** Exactly the configuration the engine ALREADY reads. No new key appears here. */
|
|
59
|
+
export interface SynthesisContext {
|
|
60
|
+
/** `ai_council.chairman.mode` — `host` | `member` | `auto`. */
|
|
61
|
+
readonly chairmanMode: string;
|
|
62
|
+
/** `ai_council.chairman.member` when the mode is `member`. */
|
|
63
|
+
readonly configuredMember: string | null;
|
|
64
|
+
/** Member names that produced a real response this session. */
|
|
65
|
+
readonly deliberated: ReadonlySet<string>;
|
|
66
|
+
/** Enabled members in config order, with their optional `tier`. */
|
|
67
|
+
readonly candidates: readonly ChairmanCandidate[];
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** The ONE interface all five candidates sit behind. */
|
|
71
|
+
export interface SynthesisStrategy {
|
|
72
|
+
readonly id: SynthesisStrategyId;
|
|
73
|
+
/** Internal description. Never rendered on a user-facing surface. */
|
|
74
|
+
readonly describe: string;
|
|
75
|
+
/** Can today's configuration reach it at all? */
|
|
76
|
+
readonly reachable: boolean;
|
|
77
|
+
/** Extra billable calls beyond the deliberation, at this strategy. */
|
|
78
|
+
readonly extraCalls: number;
|
|
79
|
+
/** Delegates to `select_chairman` — never a second selection path. */
|
|
80
|
+
readonly resolve: (ctx: SynthesisContext) => ChairmanSelection;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const _viaChairman = (ctx: SynthesisContext): ChairmanSelection =>
|
|
84
|
+
select_chairman(ctx.chairmanMode, ctx.configuredMember, ctx.deliberated, ctx.candidates);
|
|
85
|
+
|
|
86
|
+
export const STRATEGIES: Readonly<Record<SynthesisStrategyId, SynthesisStrategy>> = Object.freeze({
|
|
87
|
+
host_convener: {
|
|
88
|
+
id: 'host_convener',
|
|
89
|
+
describe: 'the host that framed the artefact writes the synthesis — today default, zero extra calls',
|
|
90
|
+
reachable: true,
|
|
91
|
+
extraCalls: 0,
|
|
92
|
+
resolve: _viaChairman,
|
|
93
|
+
},
|
|
94
|
+
external_judge: {
|
|
95
|
+
id: 'external_judge',
|
|
96
|
+
describe: 'a named non-deliberating member synthesises — chairman.mode=member',
|
|
97
|
+
reachable: true,
|
|
98
|
+
extraCalls: 1,
|
|
99
|
+
resolve: _viaChairman,
|
|
100
|
+
},
|
|
101
|
+
strongest_model: {
|
|
102
|
+
id: 'strongest_model',
|
|
103
|
+
describe: 'the highest-tier non-deliberating member synthesises — chairman.mode=auto with tiers configured',
|
|
104
|
+
reachable: true,
|
|
105
|
+
extraCalls: 1,
|
|
106
|
+
resolve: _viaChairman,
|
|
107
|
+
},
|
|
108
|
+
top_ranked_member: {
|
|
109
|
+
id: 'top_ranked_member',
|
|
110
|
+
describe:
|
|
111
|
+
'the first non-deliberating member in config order synthesises — chairman.mode=auto with no tiers, ' +
|
|
112
|
+
'where config order is the only ranking the engine trusts',
|
|
113
|
+
reachable: true,
|
|
114
|
+
extraCalls: 1,
|
|
115
|
+
resolve: _viaChairman,
|
|
116
|
+
},
|
|
117
|
+
dual_adjudicated: {
|
|
118
|
+
id: 'dual_adjudicated',
|
|
119
|
+
describe:
|
|
120
|
+
'two synthesisers plus an adjudication pass — NOT reachable from today’s configuration; ' +
|
|
121
|
+
'reaching it needs a new input, which 5.1’s verify clause forbids adding to the user surface',
|
|
122
|
+
reachable: false,
|
|
123
|
+
extraCalls: 3,
|
|
124
|
+
resolve: _viaChairman,
|
|
125
|
+
},
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Which strategy today's configuration IS, named.
|
|
130
|
+
*
|
|
131
|
+
* Reads only `chairmanMode` and whether any candidate carries a tier — both
|
|
132
|
+
* already-read inputs. Returns `null` for a mode the config loader would have
|
|
133
|
+
* rejected, rather than guessing a strategy for an unknown value.
|
|
134
|
+
*/
|
|
135
|
+
export function resolveSynthesisStrategy(ctx: SynthesisContext): SynthesisStrategy | null {
|
|
136
|
+
if (ctx.chairmanMode === 'host') return STRATEGIES.host_convener;
|
|
137
|
+
if (ctx.chairmanMode === 'member') return STRATEGIES.external_judge;
|
|
138
|
+
if (ctx.chairmanMode === 'auto') {
|
|
139
|
+
const tiered = ctx.candidates.some((c) => c.tier !== null && c.tier !== undefined);
|
|
140
|
+
return tiered ? STRATEGIES.strongest_model : STRATEGIES.top_ranked_member;
|
|
141
|
+
}
|
|
142
|
+
return null;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/** Strategies a consumer can actually land on today. `dual_adjudicated` is excluded. */
|
|
146
|
+
export function reachableStrategies(): SynthesisStrategyId[] {
|
|
147
|
+
return SYNTHESIS_STRATEGIES.filter((id) => STRATEGIES[id].reachable);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* The config keys this module reads — asserted by the test to contain no key
|
|
152
|
+
* the engine did not already read, so "no new user-facing input" is checkable
|
|
153
|
+
* rather than promised.
|
|
154
|
+
*/
|
|
155
|
+
export const CONFIG_KEYS_READ: readonly string[] = Object.freeze([
|
|
156
|
+
'ai_council.chairman.mode',
|
|
157
|
+
'ai_council.chairman.member',
|
|
158
|
+
'ai_council.members.<name>.tier',
|
|
159
|
+
]);
|