@event4u/agent-config 14.12.0 → 14.13.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 +234 -0
- 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 +109 -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-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 +247 -3
- package/dist/agent-src/scripts/guarded_baseline.ts +328 -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 +400 -0
- package/dist/agent-src/scripts/ticket_bundles.ts +84 -0
- package/dist/agent-src/scripts/update_roadmap_progress.ts +8 -4
- 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 +42 -10
- 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 +57 -57
- 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 +1836 -712
- 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 +4818 -2645
- 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/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 +247 -3
- package/src/agent-src/scripts/guarded_baseline.ts +328 -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 +400 -0
- package/src/agent-src/scripts/ticket_bundles.ts +84 -0
- package/src/agent-src/scripts/update_roadmap_progress.ts +8 -4
- 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 +38 -36
- package/src/config/evaluator-budgets.json +14 -14
- package/src/config/gate-coverage.yml +579 -63
- package/src/config/gate-reachability-exemptions.json +21 -0
- package/src/config/gate-violation-baselines.json +37 -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 +8 -8
- 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 +126 -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 +27 -0
- package/src/scripts/_lib/activation_ladder.ts +211 -0
- package/src/scripts/_lib/activation_states.ts +88 -0
- package/src/scripts/_lib/adherence_detectors.ts +143 -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_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/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/council_settings_block.ts +6 -0
- package/src/scripts/_lib/curator_ops.ts +172 -0
- package/src/scripts/_lib/delivery_arm_experiment.ts +266 -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/eval_publication.ts +354 -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/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 +229 -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/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/prefix_stable_surfaces.ts +123 -0
- package/src/scripts/_lib/privacy_class.ts +47 -0
- package/src/scripts/_lib/regression_neighbourhood.ts +259 -0
- 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/runtime_journal.ts +1499 -0
- package/src/scripts/_lib/runtime_wiring_checks.ts +430 -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/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/disagreement_signal.ts +338 -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/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/response_record.ts +50 -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 +126 -0
- 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 +142 -1
- 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_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/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 +980 -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 +98 -11
- 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/concern_registry.ts +4 -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/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/lint_adr_sweep_routing.ts +12 -0
- package/src/scripts/lint_canonical_terms.ts +604 -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_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/mcp_server/consumer_tool_catalog.json +1 -1
- package/src/scripts/mcp_server/tool_catalog_source.ts +1 -1
- package/src/scripts/measure_docs_dead_links.ts +348 -0
- package/src/scripts/measure_host_capture.ts +461 -0
- package/src/scripts/migration_status.ts +3 -3
- package/src/scripts/model_rule_injection.ts +60 -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_turnaround.ts +384 -0
- package/src/scripts/probe_unblocked_ask.ts +279 -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,601 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The candidate record: its arity, its mutation alphabet, and its lifecycle.
|
|
3
|
+
*
|
|
4
|
+
* `road-to-governed-harness-evolution` Phase 3, steps 3.2, 3.3 and 3.4.
|
|
5
|
+
*
|
|
6
|
+
* Three invariants live here because they are three properties of ONE record
|
|
7
|
+
* and separating them would let a caller satisfy each in isolation while
|
|
8
|
+
* violating the conjunction:
|
|
9
|
+
*
|
|
10
|
+
* 3.2 ONE PRIMARY DIMENSION PER CANDIDATE. If routing and body both change
|
|
11
|
+
* and the score moves, the credit is ambiguous and the Phase 4 metric
|
|
12
|
+
* vector cannot be read per candidate. Multi-dimension consolidation is a
|
|
13
|
+
* DISTINCT record type ({@link ConsolidationRecord}), not a candidate
|
|
14
|
+
* with a wider field.
|
|
15
|
+
*
|
|
16
|
+
* 3.3 THE MUTATION ALPHABET IS EXACTLY THREE — `activation`, `routing`,
|
|
17
|
+
* `content`. Precedence, composition, verification, tool strategy, budget
|
|
18
|
+
* and scope are named and unimplemented until the three carry.
|
|
19
|
+
*
|
|
20
|
+
* 3.4 A LIFECYCLE STATE ENUM, so `mutated` and `accepted` cannot be confused.
|
|
21
|
+
* Both parent designs made this the structural guard, one tracing the
|
|
22
|
+
* defect to a reference implementation passing `mutated` in where
|
|
23
|
+
* `accepted` was expected.
|
|
24
|
+
*
|
|
25
|
+
* ## E10 was a SPLIT, and this file takes the conservative side
|
|
26
|
+
*
|
|
27
|
+
* AI council 2026-08-30, anthropic + openai — **not convergent, 1/1**. The
|
|
28
|
+
* anthropic seat argued for FOUR dimensions, adding `verification` now, on an
|
|
29
|
+
* irreversibility argument: dimensions are metadata on candidate records, so
|
|
30
|
+
* adding one later makes prior candidates permanently unclassifiable. The
|
|
31
|
+
* openai seat argued for THREE, adding `verification` only when a demonstrable
|
|
32
|
+
* need appears.
|
|
33
|
+
*
|
|
34
|
+
* Per this repository's escalation handling a split takes the **conservative**
|
|
35
|
+
* side, so the alphabet is three — which is what step 3.3 already specified, so
|
|
36
|
+
* its verify clause ("the schema rejects a mutation naming a fourth dimension")
|
|
37
|
+
* stands unchanged. This is recorded as a split, not as a verdict.
|
|
38
|
+
*
|
|
39
|
+
* **The losing seat's concern is a design constraint this schema satisfies.**
|
|
40
|
+
* Adding a dimension later must be an ADDITIVE change, never a migration that
|
|
41
|
+
* orphans historical records. Two mechanisms, and both are load-bearing:
|
|
42
|
+
*
|
|
43
|
+
* 1. {@link CANDIDATE_RECORD_VERSION} is written into every record, so a
|
|
44
|
+
* later reader can tell which alphabet a record was authored against
|
|
45
|
+
* rather than inferring it from the values present.
|
|
46
|
+
* 2. The unknown-dimension refusal lives in the VALIDATOR
|
|
47
|
+
* ({@link parseCandidateRecord}) and NOT in the reader
|
|
48
|
+
* ({@link readCandidateRecord}). A historical record naming a dimension
|
|
49
|
+
* this build does not know stays READABLE — it comes back flagged, not
|
|
50
|
+
* as a throw. Putting the refusal in the reader is what would make the
|
|
51
|
+
* record unreadable, which is exactly the irreversibility the anthropic
|
|
52
|
+
* seat named.
|
|
53
|
+
*
|
|
54
|
+
* Nothing here writes, spends, or fetches. Pure functions over declared state.
|
|
55
|
+
*/
|
|
56
|
+
|
|
57
|
+
/** Schema version stamped into every record. Bump when a field's meaning moves. */
|
|
58
|
+
export const CANDIDATE_RECORD_VERSION = 1;
|
|
59
|
+
|
|
60
|
+
// --- 3.3 — the mutation alphabet --------------------------------------------
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* The three implemented mutation dimensions.
|
|
64
|
+
*
|
|
65
|
+
* The six named-and-unimplemented ones — precedence, composition, verification,
|
|
66
|
+
* tool strategy, budget, scope — are deliberately absent rather than present
|
|
67
|
+
* and disabled. A member that exists but is refused downstream reads as a
|
|
68
|
+
* capability to a caller and as a gap to a reviewer; absence reads the same to
|
|
69
|
+
* both.
|
|
70
|
+
*/
|
|
71
|
+
export const MUTATION_DIMENSIONS = ['activation', 'routing', 'content'] as const;
|
|
72
|
+
export type MutationDimension = (typeof MUTATION_DIMENSIONS)[number];
|
|
73
|
+
|
|
74
|
+
export function isMutationDimension(v: unknown): v is MutationDimension {
|
|
75
|
+
return typeof v === 'string' && (MUTATION_DIMENSIONS as readonly string[]).includes(v);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// --- 3.4 — the lifecycle ----------------------------------------------------
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* The candidate lifecycle, in spine order followed by the two off-spine exits.
|
|
82
|
+
*
|
|
83
|
+
* The ordering of the first seven is the ONLY thing that makes "a transition
|
|
84
|
+
* skipping a stage" decidable, so it is the array's contract and not an
|
|
85
|
+
* incidental listing order.
|
|
86
|
+
*/
|
|
87
|
+
export const LIFECYCLE_SPINE = [
|
|
88
|
+
'proposed',
|
|
89
|
+
'diagnostic-evaluated',
|
|
90
|
+
'selection-evaluated',
|
|
91
|
+
'promotion-eligible',
|
|
92
|
+
'sealed-evaluated',
|
|
93
|
+
'promotion-proposed',
|
|
94
|
+
'promoted',
|
|
95
|
+
] as const;
|
|
96
|
+
|
|
97
|
+
export const LIFECYCLE_STATES = [...LIFECYCLE_SPINE, 'rejected', 'retired'] as const;
|
|
98
|
+
export type LifecycleState = (typeof LIFECYCLE_STATES)[number];
|
|
99
|
+
|
|
100
|
+
export function isLifecycleState(v: unknown): v is LifecycleState {
|
|
101
|
+
return typeof v === 'string' && (LIFECYCLE_STATES as readonly string[]).includes(v);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* The ONE state that means a candidate was accepted.
|
|
106
|
+
*
|
|
107
|
+
* Exported as a named constant rather than compared inline so that a grep for
|
|
108
|
+
* the acceptance decision finds one site. Step 3.4's exit criterion is that no
|
|
109
|
+
* code path reads a candidate as accepted from the mere fact that it exists;
|
|
110
|
+
* one accessor is what makes that auditable.
|
|
111
|
+
*/
|
|
112
|
+
export const ACCEPTED_STATE: LifecycleState = 'promoted';
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Is this candidate accepted?
|
|
116
|
+
*
|
|
117
|
+
* Existence is not acceptance and neither is any evaluated state:
|
|
118
|
+
* `sealed-evaluated` means it was measured, `promotion-eligible` means it may
|
|
119
|
+
* be proposed, `promotion-proposed` means a human has been asked. Only
|
|
120
|
+
* {@link ACCEPTED_STATE} answers yes.
|
|
121
|
+
*/
|
|
122
|
+
export function isAccepted(record: { readonly lifecycle: LifecycleState }): boolean {
|
|
123
|
+
return record.lifecycle === ACCEPTED_STATE;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export class LifecycleTransitionError extends Error {
|
|
127
|
+
readonly from: LifecycleState;
|
|
128
|
+
readonly to: LifecycleState;
|
|
129
|
+
constructor(from: LifecycleState, to: LifecycleState, why: string) {
|
|
130
|
+
super(`lifecycle transition '${from}' -> '${to}' refused: ${why}`);
|
|
131
|
+
this.name = 'LifecycleTransitionError';
|
|
132
|
+
this.from = from;
|
|
133
|
+
this.to = to;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* A named human who approved a promotion. There is no anonymous form.
|
|
139
|
+
*
|
|
140
|
+
* See {@link assertTransition} for why this type exists at all — it is the
|
|
141
|
+
* mechanical half of Phase 0's carried non-promotion condition.
|
|
142
|
+
*/
|
|
143
|
+
export interface HumanApproval {
|
|
144
|
+
/** A person, not a process. Empty or whitespace-only is refused. */
|
|
145
|
+
readonly approver: string;
|
|
146
|
+
/** ISO-8601 date the approval was given. */
|
|
147
|
+
readonly approvedAt: string;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function spineIndex(s: LifecycleState): number {
|
|
151
|
+
return (LIFECYCLE_SPINE as readonly string[]).indexOf(s);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Refuse any transition that is not exactly one legal step.
|
|
156
|
+
*
|
|
157
|
+
* The legal moves, and nothing else:
|
|
158
|
+
*
|
|
159
|
+
* - one step FORWARD along the spine — `proposed → diagnostic-evaluated`,
|
|
160
|
+
* and so on. A jump of two or more is refused, which is step 3.4's
|
|
161
|
+
* "a state transition skipping a stage is refused" verbatim.
|
|
162
|
+
* - any non-terminal state → `rejected`. A candidate can die at any stage,
|
|
163
|
+
* and forcing it down the spine first would manufacture evaluated states
|
|
164
|
+
* for candidates that were never evaluated.
|
|
165
|
+
* - `promoted` → `retired`, and only from there. RETIRE is a first-class
|
|
166
|
+
* anti-sprawl operation (E6, 2/2 convergent on the 7-op set) and it acts
|
|
167
|
+
* on something that was promoted; retiring a candidate that never landed
|
|
168
|
+
* is a rejection wearing the wrong name.
|
|
169
|
+
*
|
|
170
|
+
* Backwards moves are refused. Self-transitions are refused — a no-op that
|
|
171
|
+
* type-checks is how a stalled pipeline looks like a progressing one.
|
|
172
|
+
*
|
|
173
|
+
* ## The promotion gate — Phase 0's carried condition, discharged here
|
|
174
|
+
*
|
|
175
|
+
* Phase 0 carried a council condition UNMET (AI council 2026-08-29, anthropic +
|
|
176
|
+
* openai, 2/2): the non-promotion property of Phases 1–6 must be MECHANICALLY
|
|
177
|
+
* ENFORCED, not merely stated. It was carried unmet honestly, because nothing
|
|
178
|
+
* in the tree promoted anything and a gate over a population of zero exits
|
|
179
|
+
* green while looking like enforcement. The roadmap named where the population
|
|
180
|
+
* stops being empty: *"the 3.4 lifecycle enum's `promoted` transition and the
|
|
181
|
+
* 3.6 verb set are where the population stops being empty, so the check lands
|
|
182
|
+
* there"*.
|
|
183
|
+
*
|
|
184
|
+
* This is that check for the transition half: **`→ promoted` requires a named
|
|
185
|
+
* human approver.** No default, no service account, no `approver: 'ci'` — a
|
|
186
|
+
* blank or whitespace-only name is refused, so the cheapest way to satisfy the
|
|
187
|
+
* gate is to actually name someone.
|
|
188
|
+
*
|
|
189
|
+
* The verb half (3.6) is out of this change's scope and is NOT claimed
|
|
190
|
+
* discharged. What holds now is narrower and worth stating exactly: no code
|
|
191
|
+
* path can move a candidate into `promoted` without a name, on any surface that
|
|
192
|
+
* calls this function.
|
|
193
|
+
*
|
|
194
|
+
* @throws {LifecycleTransitionError} on any refused move.
|
|
195
|
+
*/
|
|
196
|
+
export function assertTransition(
|
|
197
|
+
from: LifecycleState,
|
|
198
|
+
to: LifecycleState,
|
|
199
|
+
approval?: HumanApproval,
|
|
200
|
+
): void {
|
|
201
|
+
if (from === to) {
|
|
202
|
+
throw new LifecycleTransitionError(from, to, 'a self-transition records no progress');
|
|
203
|
+
}
|
|
204
|
+
if (from === 'rejected' || from === 'retired') {
|
|
205
|
+
throw new LifecycleTransitionError(from, to, `'${from}' is terminal`);
|
|
206
|
+
}
|
|
207
|
+
if (to === 'rejected') {
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
if (to === 'retired') {
|
|
211
|
+
if (from !== 'promoted') {
|
|
212
|
+
throw new LifecycleTransitionError(
|
|
213
|
+
from,
|
|
214
|
+
to,
|
|
215
|
+
"only a promoted candidate can be retired; a candidate that never landed is 'rejected'",
|
|
216
|
+
);
|
|
217
|
+
}
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
const fromIdx = spineIndex(from);
|
|
221
|
+
const toIdx = spineIndex(to);
|
|
222
|
+
if (fromIdx < 0 || toIdx < 0) {
|
|
223
|
+
throw new LifecycleTransitionError(from, to, 'not a spine state');
|
|
224
|
+
}
|
|
225
|
+
if (toIdx < fromIdx) {
|
|
226
|
+
throw new LifecycleTransitionError(from, to, 'the lifecycle does not run backwards');
|
|
227
|
+
}
|
|
228
|
+
if (toIdx - fromIdx > 1) {
|
|
229
|
+
const skipped = LIFECYCLE_SPINE.slice(fromIdx + 1, toIdx).join(', ');
|
|
230
|
+
throw new LifecycleTransitionError(from, to, `it skips ${skipped}`);
|
|
231
|
+
}
|
|
232
|
+
if (to === 'promoted') {
|
|
233
|
+
assertHumanApproval(approval);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
function assertHumanApproval(approval: HumanApproval | undefined): void {
|
|
238
|
+
if (approval === undefined || approval.approver.trim() === '') {
|
|
239
|
+
throw new LifecycleTransitionError(
|
|
240
|
+
'promotion-proposed',
|
|
241
|
+
'promoted',
|
|
242
|
+
'promotion into canonical agent-config requires a NAMED human approver. ' +
|
|
243
|
+
'This is the mechanical half of the non-promotion property the ' +
|
|
244
|
+
'merge-authority council required (2026-08-29, anthropic + openai, 2/2); ' +
|
|
245
|
+
'an unnamed or blank approver is refused rather than defaulted.',
|
|
246
|
+
);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// --- Path ownership ---------------------------------------------------------
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* The only paths a candidate mutation may write.
|
|
254
|
+
*
|
|
255
|
+
* Declared here rather than imported from `bench_ab_clone` so this module stays
|
|
256
|
+
* usable without the bench, and pinned equal to that script's `WITH_SURFACES`
|
|
257
|
+
* and to `bench_ab_integrity`'s `ALLOWED_DELTA_PATHS` by a parity test. Three
|
|
258
|
+
* copies of a list is a drift hazard; a test that fails when they diverge is
|
|
259
|
+
* the cheapest form of one source of truth available without a refactor that
|
|
260
|
+
* would touch the byte-exact CLI contract of both scripts.
|
|
261
|
+
*/
|
|
262
|
+
export const CANDIDATE_OWNED_PATHS: readonly string[] = ['.claude', '.augment', 'AGENTS.md', 'CLAUDE.md'];
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* May a candidate mutation write this repo-relative path?
|
|
266
|
+
*
|
|
267
|
+
* Refuses absolute paths, empty paths, and any traversal segment — `..`
|
|
268
|
+
* anywhere in the path is refused BEFORE the head component is inspected,
|
|
269
|
+
* because `.claude/../src/x.ts` has an owned head and an unowned target.
|
|
270
|
+
*/
|
|
271
|
+
export function isCandidateOwnedPath(rel: string): boolean {
|
|
272
|
+
if (rel === '' || rel.startsWith('/') || /^[A-Za-z]:[\\/]/.test(rel)) {
|
|
273
|
+
return false;
|
|
274
|
+
}
|
|
275
|
+
const parts = rel.split(/[\\/]/).filter((p) => p !== '' && p !== '.');
|
|
276
|
+
if (parts.length === 0 || parts.includes('..')) {
|
|
277
|
+
return false;
|
|
278
|
+
}
|
|
279
|
+
const head = parts[0] as string;
|
|
280
|
+
if (parts.length === 1) {
|
|
281
|
+
return CANDIDATE_OWNED_PATHS.includes(head);
|
|
282
|
+
}
|
|
283
|
+
// A nested path is owned only when its head is an owned DIRECTORY. `AGENTS.md`
|
|
284
|
+
// is a file, so `AGENTS.md/x` is not ownership, it is nonsense.
|
|
285
|
+
return head === '.claude' || head === '.augment';
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
export class PathOwnershipError extends Error {
|
|
289
|
+
readonly path: string;
|
|
290
|
+
constructor(p: string) {
|
|
291
|
+
super(
|
|
292
|
+
`path ownership: '${p}' is outside the candidate surface ` +
|
|
293
|
+
`(${CANDIDATE_OWNED_PATHS.join(', ')}). A candidate that writes a task-target ` +
|
|
294
|
+
'file is not a harness variant — it is a different experiment, and the ' +
|
|
295
|
+
'paired verdict would attribute its effect to the harness.',
|
|
296
|
+
);
|
|
297
|
+
this.name = 'PathOwnershipError';
|
|
298
|
+
this.path = p;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/** One file a candidate rewrites, and the bytes it writes. */
|
|
303
|
+
export interface Mutation {
|
|
304
|
+
/** Repo-relative, inside {@link CANDIDATE_OWNED_PATHS}. */
|
|
305
|
+
readonly path: string;
|
|
306
|
+
readonly content: string;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* Refuse a mutation set that reaches outside the candidate surface.
|
|
311
|
+
*
|
|
312
|
+
* @throws {PathOwnershipError} on the first unowned path.
|
|
313
|
+
*/
|
|
314
|
+
export function assertMutationPathsOwned(mutations: readonly Mutation[]): void {
|
|
315
|
+
for (const m of mutations) {
|
|
316
|
+
if (!isCandidateOwnedPath(m.path)) {
|
|
317
|
+
throw new PathOwnershipError(m.path);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// --- The records ------------------------------------------------------------
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* A single candidate: exactly one primary dimension, one lifecycle state.
|
|
326
|
+
*
|
|
327
|
+
* `dimension` is a SCALAR and not an array. That is step 3.2's invariant
|
|
328
|
+
* expressed in the type rather than in a validator, so the arity cannot be
|
|
329
|
+
* violated by a caller that constructs the record in TypeScript and skips the
|
|
330
|
+
* parser. The parser exists for the untrusted-input path (a JSON file), where
|
|
331
|
+
* the type buys nothing.
|
|
332
|
+
*/
|
|
333
|
+
export interface CandidateRecord {
|
|
334
|
+
readonly kind: 'candidate';
|
|
335
|
+
readonly version: number;
|
|
336
|
+
readonly id: string;
|
|
337
|
+
readonly dimension: MutationDimension;
|
|
338
|
+
readonly lifecycle: LifecycleState;
|
|
339
|
+
readonly mutations: readonly Mutation[];
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/**
|
|
343
|
+
* A consolidation run: two or more dimensions, deliberately a DIFFERENT type.
|
|
344
|
+
*
|
|
345
|
+
* Step 3.2 requires this to be a distinct record type rather than a candidate
|
|
346
|
+
* with a wider field, and the reason is not tidiness. A metric vector read per
|
|
347
|
+
* candidate assumes single-dimension attribution; a multi-dimension record
|
|
348
|
+
* flowing through the same reader would be read under that assumption and
|
|
349
|
+
* silently mis-attributed. Two types make the mis-read a type error.
|
|
350
|
+
*
|
|
351
|
+
* `sourceCandidates` is required and must name at least two: a consolidation
|
|
352
|
+
* with one source is that candidate, and with none it is a candidate that
|
|
353
|
+
* evaded the arity rule by choosing the other type.
|
|
354
|
+
*/
|
|
355
|
+
export interface ConsolidationRecord {
|
|
356
|
+
readonly kind: 'consolidation';
|
|
357
|
+
readonly version: number;
|
|
358
|
+
readonly id: string;
|
|
359
|
+
readonly dimensions: readonly MutationDimension[];
|
|
360
|
+
readonly lifecycle: LifecycleState;
|
|
361
|
+
readonly sourceCandidates: readonly string[];
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
export class CandidateSchemaError extends Error {
|
|
365
|
+
constructor(msg: string) {
|
|
366
|
+
super(`candidate schema: ${msg}`);
|
|
367
|
+
this.name = 'CandidateSchemaError';
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
function asObject(input: unknown): Record<string, unknown> {
|
|
372
|
+
if (typeof input !== 'object' || input === null || Array.isArray(input)) {
|
|
373
|
+
throw new CandidateSchemaError('record must be a JSON object');
|
|
374
|
+
}
|
|
375
|
+
return input as Record<string, unknown>;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
function requireNonEmptyString(obj: Record<string, unknown>, key: string): string {
|
|
379
|
+
const v = obj[key];
|
|
380
|
+
if (typeof v !== 'string' || v.trim() === '') {
|
|
381
|
+
throw new CandidateSchemaError(`'${key}' must be a non-empty string`);
|
|
382
|
+
}
|
|
383
|
+
return v;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
function requireLifecycle(obj: Record<string, unknown>): LifecycleState {
|
|
387
|
+
// Fail-closed and DELIBERATELY not defaulted. A record with no lifecycle is
|
|
388
|
+
// refused rather than read as `proposed`, because a default is a value the
|
|
389
|
+
// author did not choose, and every downstream reader would treat it as one
|
|
390
|
+
// they did. Step 3.4 exists because `mutated` was once read as `accepted`.
|
|
391
|
+
if (!('lifecycle' in obj)) {
|
|
392
|
+
throw new CandidateSchemaError(
|
|
393
|
+
"'lifecycle' is required and is never defaulted — an absent state is not 'proposed'",
|
|
394
|
+
);
|
|
395
|
+
}
|
|
396
|
+
const v = obj['lifecycle'];
|
|
397
|
+
if (!isLifecycleState(v)) {
|
|
398
|
+
throw new CandidateSchemaError(
|
|
399
|
+
`'lifecycle' must be one of ${LIFECYCLE_STATES.join(', ')} (got ${JSON.stringify(v)})`,
|
|
400
|
+
);
|
|
401
|
+
}
|
|
402
|
+
return v;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
function requireVersion(obj: Record<string, unknown>): number {
|
|
406
|
+
const v = obj['version'];
|
|
407
|
+
if (typeof v !== 'number' || !Number.isInteger(v) || v < 1) {
|
|
408
|
+
throw new CandidateSchemaError("'version' must be a positive integer");
|
|
409
|
+
}
|
|
410
|
+
if (v > CANDIDATE_RECORD_VERSION) {
|
|
411
|
+
throw new CandidateSchemaError(
|
|
412
|
+
`record version ${String(v)} is newer than this build understands ` +
|
|
413
|
+
`(${String(CANDIDATE_RECORD_VERSION)})`,
|
|
414
|
+
);
|
|
415
|
+
}
|
|
416
|
+
return v;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
function parseMutations(obj: Record<string, unknown>): readonly Mutation[] {
|
|
420
|
+
const raw = obj['mutations'];
|
|
421
|
+
if (!Array.isArray(raw)) {
|
|
422
|
+
throw new CandidateSchemaError("'mutations' must be an array (use [] for none)");
|
|
423
|
+
}
|
|
424
|
+
const out: Mutation[] = [];
|
|
425
|
+
for (const item of raw) {
|
|
426
|
+
const m = asObject(item);
|
|
427
|
+
const p = requireNonEmptyString(m, 'path');
|
|
428
|
+
const content = m['content'];
|
|
429
|
+
if (typeof content !== 'string') {
|
|
430
|
+
throw new CandidateSchemaError(`mutation '${p}': 'content' must be a string`);
|
|
431
|
+
}
|
|
432
|
+
out.push({ path: p, content });
|
|
433
|
+
}
|
|
434
|
+
assertMutationPathsOwned(out);
|
|
435
|
+
return out;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
/**
|
|
439
|
+
* Validate an untrusted candidate record — the REFUSING path.
|
|
440
|
+
*
|
|
441
|
+
* Rejects, in this order and each with its own message:
|
|
442
|
+
*
|
|
443
|
+
* - `kind` that is not `'candidate'` (a consolidation record routed here);
|
|
444
|
+
* - a `dimensions` key at all — the plural is how a two-dimension candidate
|
|
445
|
+
* gets written, so it is refused by NAME rather than by counting values;
|
|
446
|
+
* - `dimension` supplied as an array, even a one-element one. An array is a
|
|
447
|
+
* field that can hold two, and step 3.2's invariant is about what the field
|
|
448
|
+
* CAN hold, not about what this instance happens to hold;
|
|
449
|
+
* - a `dimension` outside the three-member alphabet (step 3.3);
|
|
450
|
+
* - an absent or unrecognised `lifecycle` (step 3.4);
|
|
451
|
+
* - a mutation path outside the candidate surface.
|
|
452
|
+
*
|
|
453
|
+
* @throws {CandidateSchemaError} on the first violation.
|
|
454
|
+
*/
|
|
455
|
+
export function parseCandidateRecord(input: unknown): CandidateRecord {
|
|
456
|
+
const obj = asObject(input);
|
|
457
|
+
if (obj['kind'] !== 'candidate') {
|
|
458
|
+
throw new CandidateSchemaError(
|
|
459
|
+
`'kind' must be 'candidate' (got ${JSON.stringify(obj['kind'])}); a multi-dimension ` +
|
|
460
|
+
"consolidation is the distinct 'consolidation' record type",
|
|
461
|
+
);
|
|
462
|
+
}
|
|
463
|
+
const version = requireVersion(obj);
|
|
464
|
+
const id = requireNonEmptyString(obj, 'id');
|
|
465
|
+
if ('dimensions' in obj) {
|
|
466
|
+
throw new CandidateSchemaError(
|
|
467
|
+
"a candidate carries exactly ONE primary dimension, so it has no 'dimensions' field. " +
|
|
468
|
+
"A run changing several dimensions is a 'consolidation' record, not a candidate — " +
|
|
469
|
+
'if routing and content both change and the score moves, the credit is ambiguous',
|
|
470
|
+
);
|
|
471
|
+
}
|
|
472
|
+
const dim = obj['dimension'];
|
|
473
|
+
if (Array.isArray(dim)) {
|
|
474
|
+
throw new CandidateSchemaError(
|
|
475
|
+
"'dimension' must be a single string, not an array — a field that can hold two " +
|
|
476
|
+
'dimensions violates the one-primary-dimension invariant even when it holds one',
|
|
477
|
+
);
|
|
478
|
+
}
|
|
479
|
+
if (!isMutationDimension(dim)) {
|
|
480
|
+
throw new CandidateSchemaError(
|
|
481
|
+
`'dimension' must be one of ${MUTATION_DIMENSIONS.join(', ')} (got ${JSON.stringify(dim)}). ` +
|
|
482
|
+
'Precedence, composition, verification, tool strategy, budget and scope are named ' +
|
|
483
|
+
'and unimplemented until the three carry',
|
|
484
|
+
);
|
|
485
|
+
}
|
|
486
|
+
const lifecycle = requireLifecycle(obj);
|
|
487
|
+
const mutations = parseMutations(obj);
|
|
488
|
+
return { kind: 'candidate', version, id, dimension: dim, lifecycle, mutations };
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
/**
|
|
492
|
+
* Validate an untrusted consolidation record.
|
|
493
|
+
*
|
|
494
|
+
* Requires ≥2 DISTINCT dimensions and ≥2 source candidates. Both floors exist
|
|
495
|
+
* so the second record type cannot be used as an escape hatch from the first:
|
|
496
|
+
* a one-dimension "consolidation" is a candidate that chose the other type.
|
|
497
|
+
*
|
|
498
|
+
* @throws {CandidateSchemaError} on the first violation.
|
|
499
|
+
*/
|
|
500
|
+
export function parseConsolidationRecord(input: unknown): ConsolidationRecord {
|
|
501
|
+
const obj = asObject(input);
|
|
502
|
+
if (obj['kind'] !== 'consolidation') {
|
|
503
|
+
throw new CandidateSchemaError(
|
|
504
|
+
`'kind' must be 'consolidation' (got ${JSON.stringify(obj['kind'])})`,
|
|
505
|
+
);
|
|
506
|
+
}
|
|
507
|
+
const version = requireVersion(obj);
|
|
508
|
+
const id = requireNonEmptyString(obj, 'id');
|
|
509
|
+
const raw = obj['dimensions'];
|
|
510
|
+
if (!Array.isArray(raw)) {
|
|
511
|
+
throw new CandidateSchemaError("'dimensions' must be an array of at least two dimensions");
|
|
512
|
+
}
|
|
513
|
+
for (const d of raw) {
|
|
514
|
+
if (!isMutationDimension(d)) {
|
|
515
|
+
throw new CandidateSchemaError(
|
|
516
|
+
`'dimensions' member ${JSON.stringify(d)} is not one of ${MUTATION_DIMENSIONS.join(', ')}`,
|
|
517
|
+
);
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
const dimensions = [...new Set(raw as MutationDimension[])];
|
|
521
|
+
if (dimensions.length < 2) {
|
|
522
|
+
throw new CandidateSchemaError(
|
|
523
|
+
'a consolidation touches at least two DISTINCT dimensions; with one it is a candidate, ' +
|
|
524
|
+
'and routing it here would evade the one-primary-dimension invariant',
|
|
525
|
+
);
|
|
526
|
+
}
|
|
527
|
+
const sources = obj['sourceCandidates'];
|
|
528
|
+
if (!Array.isArray(sources) || sources.length < 2) {
|
|
529
|
+
throw new CandidateSchemaError(
|
|
530
|
+
"'sourceCandidates' must name at least two candidate ids — a consolidation of one " +
|
|
531
|
+
'candidate is that candidate',
|
|
532
|
+
);
|
|
533
|
+
}
|
|
534
|
+
for (const s of sources) {
|
|
535
|
+
if (typeof s !== 'string' || s.trim() === '') {
|
|
536
|
+
throw new CandidateSchemaError("'sourceCandidates' members must be non-empty strings");
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
const lifecycle = requireLifecycle(obj);
|
|
540
|
+
return { kind: 'consolidation', version, id, dimensions, lifecycle, sourceCandidates: sources as string[] };
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
/** What a structural read produced, and whether this build understood it. */
|
|
544
|
+
export interface CandidateReadResult {
|
|
545
|
+
readonly kind: 'candidate';
|
|
546
|
+
readonly version: number;
|
|
547
|
+
readonly id: string;
|
|
548
|
+
/** The raw value, which may not be in this build's alphabet. */
|
|
549
|
+
readonly dimension: string;
|
|
550
|
+
readonly lifecycle: LifecycleState;
|
|
551
|
+
/** `true` when `dimension` is outside {@link MUTATION_DIMENSIONS}. */
|
|
552
|
+
readonly unknownDimension: boolean;
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
/**
|
|
556
|
+
* Read a historical candidate record WITHOUT refusing an unknown dimension.
|
|
557
|
+
*
|
|
558
|
+
* This is the forward-compatibility half of the E10 split. If the alphabet ever
|
|
559
|
+
* grows to four, every record authored under the three-member alphabet still
|
|
560
|
+
* parses here, and every record authored under a four-member alphabet still
|
|
561
|
+
* READS on a three-member build — flagged, not thrown.
|
|
562
|
+
*
|
|
563
|
+
* The refusal stays in {@link parseCandidateRecord}, which is what a RUN calls.
|
|
564
|
+
* So an unknown dimension cannot enter a run, and cannot make an archive
|
|
565
|
+
* unreadable. Those are different failures and this is where they are kept
|
|
566
|
+
* apart.
|
|
567
|
+
*
|
|
568
|
+
* Everything else is still validated: `kind`, `version`, `id` and `lifecycle`
|
|
569
|
+
* are structural, not alphabet-dependent, and a record missing one of them is
|
|
570
|
+
* malformed under every alphabet. In particular an absent `lifecycle` is still
|
|
571
|
+
* refused here — a reader that invented a state would reintroduce exactly the
|
|
572
|
+
* `mutated`-read-as-`accepted` defect step 3.4 exists for.
|
|
573
|
+
*
|
|
574
|
+
* @throws {CandidateSchemaError} on a structural violation.
|
|
575
|
+
*/
|
|
576
|
+
export function readCandidateRecord(input: unknown): CandidateReadResult {
|
|
577
|
+
const obj = asObject(input);
|
|
578
|
+
if (obj['kind'] !== 'candidate') {
|
|
579
|
+
throw new CandidateSchemaError(`'kind' must be 'candidate' (got ${JSON.stringify(obj['kind'])})`);
|
|
580
|
+
}
|
|
581
|
+
const version = requireVersion(obj);
|
|
582
|
+
const id = requireNonEmptyString(obj, 'id');
|
|
583
|
+
const dim = obj['dimension'];
|
|
584
|
+
if (typeof dim !== 'string' || dim.trim() === '') {
|
|
585
|
+
throw new CandidateSchemaError("'dimension' must be a non-empty string");
|
|
586
|
+
}
|
|
587
|
+
const lifecycle = requireLifecycle(obj);
|
|
588
|
+
return { kind: 'candidate', version, id, dimension: dim, lifecycle, unknownDimension: !isMutationDimension(dim) };
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
/** Serialise a candidate record for the clone manifest. Key order is stable. */
|
|
592
|
+
export function candidateRecordToJson(record: CandidateRecord): Record<string, unknown> {
|
|
593
|
+
return {
|
|
594
|
+
kind: record.kind,
|
|
595
|
+
version: record.version,
|
|
596
|
+
id: record.id,
|
|
597
|
+
dimension: record.dimension,
|
|
598
|
+
lifecycle: record.lifecycle,
|
|
599
|
+
mutations: record.mutations.map((m) => ({ path: m.path, content: m.content })),
|
|
600
|
+
};
|
|
601
|
+
}
|