@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,430 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime-wiring checks for `agent-config doctor`.
|
|
3
|
+
*
|
|
4
|
+
* `road-to-skill-ecosystem-runtime-enforcement` Phase 2, Steps 2-5. The gate
|
|
5
|
+
* estate checks ARTIFACTS — is the rule written, is the projection byte-exact,
|
|
6
|
+
* does the manifest parse. Nothing checked whether the wiring is LIVE: whether
|
|
7
|
+
* the settings resolver returns anything, whether the router artifact parses,
|
|
8
|
+
* whether each registered hook resolves to something executable, and whether an
|
|
9
|
+
* inherited git-discovery variable is silently re-pointing every gate at another
|
|
10
|
+
* repository.
|
|
11
|
+
*
|
|
12
|
+
* All four are REPORTS. `doctor` writes nothing and always exits zero, so a
|
|
13
|
+
* check here returns a verdict and never a refusal — the point is that a
|
|
14
|
+
* disabled or mis-wired estate becomes VISIBLE, not that a diagnostic starts
|
|
15
|
+
* failing builds. A gate that wanted to refuse would belong in the gate estate.
|
|
16
|
+
*
|
|
17
|
+
* Extracted into `_lib/` rather than added to `cmd_doctor.ts` for a stated
|
|
18
|
+
* reason: that file is 3,700 lines and `check_source_size_budget` counts every
|
|
19
|
+
* line above 1,500, so four checks written inline would be charged against a
|
|
20
|
+
* budget while the same code in a new module is free. The seam is also what
|
|
21
|
+
* makes them unit-testable without booting the CLI.
|
|
22
|
+
*/
|
|
23
|
+
import { spawnSync } from 'node:child_process';
|
|
24
|
+
import * as fs from 'node:fs';
|
|
25
|
+
import * as path from 'node:path';
|
|
26
|
+
import process from 'node:process';
|
|
27
|
+
|
|
28
|
+
import { parse as parseYaml } from 'yaml';
|
|
29
|
+
|
|
30
|
+
import { inheritedGitOverrides } from './repo_root.js';
|
|
31
|
+
|
|
32
|
+
/** The verdict shape `cmd_doctor` renders. Mirrors its internal `Dict` rows. */
|
|
33
|
+
export interface WiringCheck {
|
|
34
|
+
id: string;
|
|
35
|
+
/**
|
|
36
|
+
* The four the doctor's own report contract publishes. `info` was a fifth
|
|
37
|
+
* here for one commit and is gone: the MCP `doctor_report` tool asserts the
|
|
38
|
+
* published union, so a fifth value that never reached that contract failed
|
|
39
|
+
* the tool's shape test rather than appearing in a report. A vocabulary a
|
|
40
|
+
* consumer cannot receive is not a vocabulary.
|
|
41
|
+
*/
|
|
42
|
+
status: 'ok' | 'warn' | 'fail' | 'skipped';
|
|
43
|
+
message: string;
|
|
44
|
+
remedy: string;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Step 2 — the settings resolver returns something, and WHICH FILE won.
|
|
49
|
+
*
|
|
50
|
+
* The cascade is user-global then project, last write winning. Nearly twenty
|
|
51
|
+
* rules in this suite tell an agent to read `.agent-settings.yml` to learn
|
|
52
|
+
* whether a feature is on, and every one of them is wrong about a key set on
|
|
53
|
+
* another layer. Reporting the winning FILE per key is the part that makes the
|
|
54
|
+
* check worth running — a value alone cannot be argued with.
|
|
55
|
+
*/
|
|
56
|
+
export function checkSettingsResolution(
|
|
57
|
+
iterOverrides: () => Iterable<[string, unknown, string]>,
|
|
58
|
+
): WiringCheck {
|
|
59
|
+
let pairs: [string, unknown, string][];
|
|
60
|
+
try {
|
|
61
|
+
pairs = [...iterOverrides()];
|
|
62
|
+
} catch (e) {
|
|
63
|
+
return {
|
|
64
|
+
id: 'settings-resolution',
|
|
65
|
+
status: 'fail',
|
|
66
|
+
message: `settings cascade did not resolve: ${e instanceof Error ? e.message : String(e)}`,
|
|
67
|
+
remedy: 'check the YAML in every layer `agent-config settings:get <key>` reports',
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
if (pairs.length === 0) {
|
|
71
|
+
return {
|
|
72
|
+
id: 'settings-resolution',
|
|
73
|
+
// `skipped`, not `ok`: the cascade RESOLVED and there was nothing
|
|
74
|
+
// in it, which is a check that had nothing to inspect rather than a
|
|
75
|
+
// check that passed. Reporting `ok` here would read as "settings
|
|
76
|
+
// resolved fine" on a tree where no setting exists at all — the
|
|
77
|
+
// same conflation the reach check exists to remove elsewhere.
|
|
78
|
+
status: 'skipped',
|
|
79
|
+
message:
|
|
80
|
+
'settings cascade resolves, and NO layer sets any key — every read falls to the ' +
|
|
81
|
+
'template default. That is a valid state, not a failure: a fresh project has no settings.',
|
|
82
|
+
remedy: '',
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
// Last writer wins, which is the resolver's own rule — so the winning file
|
|
86
|
+
// is the LAST occurrence of a key, not the first.
|
|
87
|
+
const winner = new Map<string, string>();
|
|
88
|
+
for (const [key, , file] of pairs) winner.set(key, file);
|
|
89
|
+
const byFile = new Map<string, number>();
|
|
90
|
+
for (const file of winner.values()) byFile.set(file, (byFile.get(file) ?? 0) + 1);
|
|
91
|
+
const summary = [...byFile.entries()]
|
|
92
|
+
.sort((a, b) => b[1] - a[1])
|
|
93
|
+
.map(([f, n]) => `${path.basename(f)}=${String(n)}`)
|
|
94
|
+
.join(' · ');
|
|
95
|
+
return {
|
|
96
|
+
id: 'settings-resolution',
|
|
97
|
+
status: 'ok',
|
|
98
|
+
message: `${String(winner.size)} key(s) resolved across ${String(byFile.size)} layer(s) — ${summary}`,
|
|
99
|
+
remedy: '',
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Step 3 — the router artifact exists, PARSES, and reports its rule count.
|
|
105
|
+
*
|
|
106
|
+
* Parsing is the half that matters. A router file that exists but does not parse
|
|
107
|
+
* fails silently at load: the host reads no rules and behaves like a tree with
|
|
108
|
+
* none, which is indistinguishable from a correctly minimal configuration.
|
|
109
|
+
*/
|
|
110
|
+
export function checkRouterArtifact(repoRoot: string, rel = 'dist/router.json'): WiringCheck {
|
|
111
|
+
const abs = path.join(repoRoot, rel);
|
|
112
|
+
if (!fs.existsSync(abs)) {
|
|
113
|
+
return {
|
|
114
|
+
id: 'router-artifact',
|
|
115
|
+
status: 'warn',
|
|
116
|
+
message: `${rel} is absent — no rule router is deployed`,
|
|
117
|
+
remedy: 'task generate-tools',
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
let doc: unknown;
|
|
121
|
+
try {
|
|
122
|
+
doc = JSON.parse(fs.readFileSync(abs, 'utf8'));
|
|
123
|
+
} catch (e) {
|
|
124
|
+
return {
|
|
125
|
+
id: 'router-artifact',
|
|
126
|
+
status: 'fail',
|
|
127
|
+
message: `${rel} exists but does NOT parse: ${e instanceof Error ? e.message : String(e)}`,
|
|
128
|
+
remedy: 'task generate-tools (a present-but-unparseable router loads as zero rules)',
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
const rules = _routerRuleCount(doc);
|
|
132
|
+
if (rules === 0) {
|
|
133
|
+
return {
|
|
134
|
+
id: 'router-artifact',
|
|
135
|
+
status: 'warn',
|
|
136
|
+
message: `${rel} parses and carries ZERO rules — a router with no rules routes nothing`,
|
|
137
|
+
remedy: 'task generate-tools',
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
return { id: 'router-artifact', status: 'ok', message: `${rel} parses · ${String(rules)} rule(s)`, remedy: '' };
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/** Count routed rules across whichever shape the artifact uses. */
|
|
144
|
+
export function _routerRuleCount(doc: unknown): number {
|
|
145
|
+
if (doc === null || typeof doc !== 'object') return 0;
|
|
146
|
+
const d = doc as Record<string, unknown>;
|
|
147
|
+
if (Array.isArray(d['rules'])) return d['rules'].length;
|
|
148
|
+
let n = 0;
|
|
149
|
+
for (const v of Object.values(d)) {
|
|
150
|
+
if (Array.isArray(v)) n += v.length;
|
|
151
|
+
else if (v !== null && typeof v === 'object') n += _routerRuleCount(v);
|
|
152
|
+
}
|
|
153
|
+
return n;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export interface HookProbe {
|
|
157
|
+
id: string;
|
|
158
|
+
script: string;
|
|
159
|
+
resolves: boolean;
|
|
160
|
+
ms: number | null;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Step 4 — every registered hook resolves to a real file, with its cost.
|
|
165
|
+
*
|
|
166
|
+
* Cost is reported PER HOOK rather than as a total, because a latency
|
|
167
|
+
* regression is only actionable where it is incurred: a 300 ms total tells you
|
|
168
|
+
* nothing, while one 280 ms concern names itself.
|
|
169
|
+
*
|
|
170
|
+
* `probe` defaults to a real invocation with `--help`, which every dispatcher
|
|
171
|
+
* entry point accepts and which does no work. Injectable so the unit tests do
|
|
172
|
+
* not spawn 53 processes.
|
|
173
|
+
*/
|
|
174
|
+
export function checkHookResolution(
|
|
175
|
+
repoRoot: string,
|
|
176
|
+
manifestRel = 'src/scripts/hook_manifest.yaml',
|
|
177
|
+
probe: ((script: string) => number | null) | null = null,
|
|
178
|
+
): { check: WiringCheck; probes: HookProbe[] } {
|
|
179
|
+
const abs = path.join(repoRoot, manifestRel);
|
|
180
|
+
if (!fs.existsSync(abs)) {
|
|
181
|
+
return {
|
|
182
|
+
check: {
|
|
183
|
+
id: 'hook-resolution',
|
|
184
|
+
status: 'fail',
|
|
185
|
+
message: `${manifestRel} is absent — the hook estate cannot be enumerated`,
|
|
186
|
+
remedy: 'restore the manifest; every hook is registered from it',
|
|
187
|
+
},
|
|
188
|
+
probes: [],
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
let concerns: Record<string, { script?: unknown }>;
|
|
192
|
+
try {
|
|
193
|
+
const doc = parseYaml(fs.readFileSync(abs, 'utf8')) as { concerns?: unknown };
|
|
194
|
+
concerns = (doc.concerns ?? {}) as Record<string, { script?: unknown }>;
|
|
195
|
+
} catch (e) {
|
|
196
|
+
return {
|
|
197
|
+
check: {
|
|
198
|
+
id: 'hook-resolution',
|
|
199
|
+
status: 'fail',
|
|
200
|
+
message: `${manifestRel} does not parse: ${e instanceof Error ? e.message : String(e)}`,
|
|
201
|
+
remedy: 'fix the manifest YAML',
|
|
202
|
+
},
|
|
203
|
+
probes: [],
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
const probes: HookProbe[] = [];
|
|
207
|
+
for (const [id, c] of Object.entries(concerns)) {
|
|
208
|
+
const script = typeof c.script === 'string' ? c.script : '';
|
|
209
|
+
const resolves = script !== '' && fs.existsSync(path.join(repoRoot, script));
|
|
210
|
+
probes.push({ id, script, resolves, ms: resolves && probe !== null ? probe(script) : null });
|
|
211
|
+
}
|
|
212
|
+
const broken = probes.filter((p) => !p.resolves);
|
|
213
|
+
if (broken.length > 0) {
|
|
214
|
+
return {
|
|
215
|
+
check: {
|
|
216
|
+
id: 'hook-resolution',
|
|
217
|
+
status: 'fail',
|
|
218
|
+
message:
|
|
219
|
+
`${String(broken.length)} of ${String(probes.length)} registered hook(s) do NOT resolve to a file: ` +
|
|
220
|
+
broken.map((b) => b.id).join(', '),
|
|
221
|
+
remedy: 'a registered hook with no script silently no-ops every session — fix the path or drop the entry',
|
|
222
|
+
},
|
|
223
|
+
probes,
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
const timed = probes.filter((p) => p.ms !== null).sort((a, b) => (b.ms ?? 0) - (a.ms ?? 0));
|
|
227
|
+
const slowest = timed.length > 0 ? ` · slowest ${timed[0]!.id} ${String(timed[0]!.ms)}ms` : '';
|
|
228
|
+
return {
|
|
229
|
+
check: {
|
|
230
|
+
id: 'hook-resolution',
|
|
231
|
+
status: 'ok',
|
|
232
|
+
message: `${String(probes.length)} registered hook(s), all resolving${slowest}`,
|
|
233
|
+
remedy: '',
|
|
234
|
+
},
|
|
235
|
+
probes,
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/** A real `--help` invocation, timed. `null` when it cannot be measured. */
|
|
240
|
+
export function probeHookCost(repoRoot: string, script: string): number | null {
|
|
241
|
+
const started = Date.now();
|
|
242
|
+
const r = spawnSync(process.execPath, [path.join(repoRoot, 'scripts-run'), script, '--help'], {
|
|
243
|
+
cwd: repoRoot,
|
|
244
|
+
encoding: 'utf8',
|
|
245
|
+
timeout: 10_000,
|
|
246
|
+
});
|
|
247
|
+
if (r.error) return null;
|
|
248
|
+
return Date.now() - started;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Step 5 — an inherited git-discovery variable.
|
|
253
|
+
*
|
|
254
|
+
* The recorded failure: a git hook exports `GIT_DIR` into every child, so a gate
|
|
255
|
+
* run inside one resolves against whatever that points at — inside a worktree,
|
|
256
|
+
* the parent checkout. The gate runs, reads the wrong tree, and PASSES. Nothing
|
|
257
|
+
* about the output says which repository answered.
|
|
258
|
+
*
|
|
259
|
+
* Reported as a warning rather than a failure because the variable is legitimate
|
|
260
|
+
* where git itself set it; what it is not is invisible.
|
|
261
|
+
*/
|
|
262
|
+
export function checkInheritedGitEnv(env: NodeJS.ProcessEnv = process.env): WiringCheck {
|
|
263
|
+
const found = inheritedGitOverrides(env);
|
|
264
|
+
if (found.length === 0) {
|
|
265
|
+
return {
|
|
266
|
+
id: 'inherited-git-env',
|
|
267
|
+
status: 'ok',
|
|
268
|
+
message: 'no git discovery override in the environment — git resolves from the working directory',
|
|
269
|
+
remedy: '',
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
return {
|
|
273
|
+
id: 'inherited-git-env',
|
|
274
|
+
status: 'warn',
|
|
275
|
+
message:
|
|
276
|
+
`${String(found.length)} git discovery override(s) inherited: ` +
|
|
277
|
+
found.map((f) => `${f.name}=${f.value}`).join(' · ') +
|
|
278
|
+
' — every git call in this process answers for THAT repository, not the working directory',
|
|
279
|
+
remedy:
|
|
280
|
+
'unset them before running a gate from inside a git hook (`env -u GIT_DIR -u GIT_WORK_TREE …`), ' +
|
|
281
|
+
'or resolve the root with _lib/repo_root.ts, which refuses instead of guessing',
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* The four runtime-wiring runners, as one table.
|
|
287
|
+
*
|
|
288
|
+
* Returned as a map rather than registered inline at each of `cmd_doctor`'s TWO
|
|
289
|
+
* runner tables: written there it is ~20 lines duplicated, and every line of
|
|
290
|
+
* that file above 1,500 is charged by `check_source_size_budget`. One factory,
|
|
291
|
+
* two spreads.
|
|
292
|
+
*/
|
|
293
|
+
/** The four ids, in render order. Spread into `cmd_doctor`'s two id registries. */
|
|
294
|
+
export const WIRING_CHECK_IDS = ['settings-resolution', 'router-artifact', 'hook-resolution', 'inherited-git-env', 'hooks-kill-switch'] as const;
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* The estate kill switch, reported so a DISABLED estate is visible.
|
|
299
|
+
*
|
|
300
|
+
* `road-to-skill-ecosystem-runtime-enforcement` Phase 1 Step 7's second clause,
|
|
301
|
+
* which could not be satisfied until the Phase 2 diagnostic existed. The flag
|
|
302
|
+
* itself has been implemented since Phase 1; what was missing is that a session
|
|
303
|
+
* running with every guard switched off looked exactly like one running with
|
|
304
|
+
* them all armed.
|
|
305
|
+
*
|
|
306
|
+
* ONLY the exact value `1` disarms, and the check mirrors that rather than
|
|
307
|
+
* guessing: a truthy-ish test would report `=0` as disabled, which is the
|
|
308
|
+
* opposite of what it means.
|
|
309
|
+
*/
|
|
310
|
+
export function checkHooksDisabled(env: NodeJS.ProcessEnv = process.env): WiringCheck {
|
|
311
|
+
const raw = env['AGENT_CONFIG_DISABLE_HOOKS'];
|
|
312
|
+
if (raw !== '1') {
|
|
313
|
+
return {
|
|
314
|
+
id: 'hooks-kill-switch',
|
|
315
|
+
status: 'ok',
|
|
316
|
+
message:
|
|
317
|
+
raw === undefined || raw === ''
|
|
318
|
+
? 'AGENT_CONFIG_DISABLE_HOOKS is not set — the hook estate is armed'
|
|
319
|
+
: `AGENT_CONFIG_DISABLE_HOOKS=${raw} — only the exact value "1" disarms, so the estate is armed`,
|
|
320
|
+
remedy: '',
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
return {
|
|
324
|
+
id: 'hooks-kill-switch',
|
|
325
|
+
status: 'warn',
|
|
326
|
+
message:
|
|
327
|
+
'AGENT_CONFIG_DISABLE_HOOKS=1 — EVERY hook and shim in this environment is disabled. ' +
|
|
328
|
+
'A run under this flag enforces nothing, and without this line it would look identical ' +
|
|
329
|
+
'to one with the whole estate armed.',
|
|
330
|
+
remedy: 'unset AGENT_CONFIG_DISABLE_HOOKS (it is intended for one command, not a session)',
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
export function wiringRunners(opts: {
|
|
335
|
+
packageRoot: string;
|
|
336
|
+
iterOverrides: () => Iterable<[string, unknown, string]>;
|
|
337
|
+
}): Record<string, () => WiringCheck> {
|
|
338
|
+
return {
|
|
339
|
+
'settings-resolution': () => checkSettingsResolution(opts.iterOverrides),
|
|
340
|
+
'router-artifact': () => checkRouterArtifact(opts.packageRoot),
|
|
341
|
+
// Per-hook COST is not probed on the default path: it spawns one process
|
|
342
|
+
// per registered concern (53 today), turning a read-only diagnostic into
|
|
343
|
+
// a multi-second one. `probeHookCost` is exported for a caller that wants it.
|
|
344
|
+
'hook-resolution': () => checkHookResolution(opts.packageRoot).check,
|
|
345
|
+
'inherited-git-env': () => checkInheritedGitEnv(),
|
|
346
|
+
'hooks-kill-switch': () => checkHooksDisabled(),
|
|
347
|
+
};
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
/**
|
|
351
|
+
* Managed hook wiring (single-surface model): the deterministic hook matrix
|
|
352
|
+
* must be registered in ~/.claude/settings.json AND the dispatch target
|
|
353
|
+
* (`agent-config` binary) must resolve on PATH. Both halves are required —
|
|
354
|
+
* hooks without a binary silently no-op every session.
|
|
355
|
+
*/
|
|
356
|
+
export interface HookWiringDeps {
|
|
357
|
+
which: (bin: string) => string | null;
|
|
358
|
+
packageRoot: string;
|
|
359
|
+
homeDir: string;
|
|
360
|
+
buildMatrix: (manifestPath: string) => Record<string, unknown>;
|
|
361
|
+
managedSignature: string;
|
|
362
|
+
pluginInstalled: () => boolean;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
export function checkHookWiring(deps: HookWiringDeps): WiringCheck {
|
|
366
|
+
const claude = deps.which('claude');
|
|
367
|
+
if (claude === null) {
|
|
368
|
+
return {
|
|
369
|
+
id: 'hook-wiring',
|
|
370
|
+
status: 'skipped',
|
|
371
|
+
message: 'Claude Code CLI not on PATH — hook-wiring check not applicable',
|
|
372
|
+
remedy: '',
|
|
373
|
+
};
|
|
374
|
+
}
|
|
375
|
+
const settings_path = path.join(deps.homeDir, '.claude', 'settings.json');
|
|
376
|
+
let missing: string[];
|
|
377
|
+
try {
|
|
378
|
+
const matrix = deps.buildMatrix(path.join(deps.packageRoot, 'src', 'scripts', 'hook_manifest.yaml'));
|
|
379
|
+
const settings = fs.existsSync(settings_path)
|
|
380
|
+
? (JSON.parse(fs.readFileSync(settings_path, 'utf8')) as Record<string, unknown>)
|
|
381
|
+
: {};
|
|
382
|
+
const hooks = ((settings['hooks'] ?? {}) as Record<string, unknown>) || {};
|
|
383
|
+
missing = Object.keys(matrix).filter((ev) => {
|
|
384
|
+
const groups = hooks[ev];
|
|
385
|
+
if (!Array.isArray(groups)) return true;
|
|
386
|
+
return !JSON.stringify(groups).includes(deps.managedSignature);
|
|
387
|
+
});
|
|
388
|
+
} catch (e) {
|
|
389
|
+
return {
|
|
390
|
+
id: 'hook-wiring',
|
|
391
|
+
status: 'fail',
|
|
392
|
+
message: `cannot evaluate managed hooks: ${String(e)}`,
|
|
393
|
+
remedy: 'fix ~/.claude/settings.json (invalid JSON?), then agent-config refresh --global',
|
|
394
|
+
};
|
|
395
|
+
}
|
|
396
|
+
const binary_ok = deps.which('agent-config') !== null;
|
|
397
|
+
if (missing.length === 0 && binary_ok) {
|
|
398
|
+
return {
|
|
399
|
+
id: 'hook-wiring',
|
|
400
|
+
status: 'ok',
|
|
401
|
+
message: 'managed hooks registered in ~/.claude/settings.json; dispatch binary on PATH',
|
|
402
|
+
remedy: '',
|
|
403
|
+
};
|
|
404
|
+
}
|
|
405
|
+
if (missing.length > 0 && deps.pluginInstalled()) {
|
|
406
|
+
return {
|
|
407
|
+
id: 'hook-wiring',
|
|
408
|
+
status: 'warn',
|
|
409
|
+
message:
|
|
410
|
+
`managed hooks missing for: ${missing.join(', ')} — currently carried by the ` +
|
|
411
|
+
'installed plugin (deprecated surface)',
|
|
412
|
+
remedy: 'agent-config refresh --global (writes the managed settings.json block)',
|
|
413
|
+
};
|
|
414
|
+
}
|
|
415
|
+
const parts: string[] = [];
|
|
416
|
+
if (missing.length > 0) parts.push(`managed hooks missing for: ${missing.join(', ')}`);
|
|
417
|
+
if (!binary_ok) parts.push('`agent-config` not on PATH (hooks would silently no-op)');
|
|
418
|
+
if (parts.length === 0) {
|
|
419
|
+
// hooks complete, binary missing was the only issue → covered above.
|
|
420
|
+
parts.push('`agent-config` not on PATH');
|
|
421
|
+
}
|
|
422
|
+
return {
|
|
423
|
+
id: 'hook-wiring',
|
|
424
|
+
status: 'fail',
|
|
425
|
+
message: parts.join('; '),
|
|
426
|
+
remedy: binary_ok
|
|
427
|
+
? 'agent-config refresh --global'
|
|
428
|
+
: 'npm install -g @event4u/agent-config, then agent-config refresh --global',
|
|
429
|
+
};
|
|
430
|
+
}
|
|
@@ -93,7 +93,17 @@ export function resolveSkillsRoot(workspaceRoot: string): string | null {
|
|
|
93
93
|
for (const candidate of DEFAULT_CATALOGUE_ROOTS) {
|
|
94
94
|
const abs = path.join(workspaceRoot, candidate);
|
|
95
95
|
try {
|
|
96
|
-
if (fs.existsSync(abs)
|
|
96
|
+
if (!fs.existsSync(abs) || !fs.statSync(abs).isDirectory()) continue;
|
|
97
|
+
// NON-EMPTY, and that qualifier is the whole fix. `.claude/skills`
|
|
98
|
+
// is a gitignored projection, so in a fresh worktree it EXISTS and
|
|
99
|
+
// holds nothing — and an empty root resolved as a match makes the
|
|
100
|
+
// ranker report an empty catalogue as an empty RESULT, which is the
|
|
101
|
+
// silent failure `road-to-inbox-harvest-2026-08-f-skill-selection-evidence`
|
|
102
|
+
// is about, arriving through a second door. Measured in that run: a
|
|
103
|
+
// worktree that had never run `generate-tools` ranked zero skills
|
|
104
|
+
// and exited 0 on a task that scores 47 against `src/skills`.
|
|
105
|
+
if (fs.readdirSync(abs).length === 0) continue;
|
|
106
|
+
return abs;
|
|
97
107
|
} catch {
|
|
98
108
|
// Unreadable candidate is not a match; try the next.
|
|
99
109
|
}
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
#!/usr/bin/env tsx
|
|
2
|
+
/**
|
|
3
|
+
* source_digest — keyed digests for the source deny set
|
|
4
|
+
* (`road-to-source-silence` Phase 1.1).
|
|
5
|
+
*
|
|
6
|
+
* ## The problem
|
|
7
|
+
*
|
|
8
|
+
* `external_sources_denylist.json` publishes, in plaintext in a public
|
|
9
|
+
* repository, 65 names of the harvest sources it exists to hide. The list IS
|
|
10
|
+
* the disclosure. Phase 1's answer is to stop shipping the names: the tracked
|
|
11
|
+
* config carries **HMAC-SHA256 digests**, the readable master lives in a
|
|
12
|
+
* gitignored private file, and a build step derives one from the other.
|
|
13
|
+
*
|
|
14
|
+
* ## Why keyed and not a plain hash
|
|
15
|
+
*
|
|
16
|
+
* The candidate space is public repository slugs — a few million entries,
|
|
17
|
+
* enumerable in minutes. An unsalted SHA-256 of a slug is a lookup, not a
|
|
18
|
+
* secret. The construction is therefore keyed: without the key a digest is not
|
|
19
|
+
* reversible by dictionary attack, and with the key the gate matches exactly as
|
|
20
|
+
* a name list would.
|
|
21
|
+
*
|
|
22
|
+
* ## STATUS: DORMANT. This ships ALONGSIDE the plaintext gate, not instead of it.
|
|
23
|
+
*
|
|
24
|
+
* AI council 2026-08-28 (blocker `where-the-key-lives`), 2/2 convergent:
|
|
25
|
+
* shipping this as a REPLACEMENT is net-negative, because the half an agent can
|
|
26
|
+
* build is precisely the half that enforces nothing. Provisioning the CI
|
|
27
|
+
* secret, generating production digests, deleting the tracked plaintext deny
|
|
28
|
+
* array, and switching CI to strict mode are **one atomic maintainer change**;
|
|
29
|
+
* splitting them leaves the gate either failing every run for want of a key or
|
|
30
|
+
* silently degraded to warn mode, which is the exact failure the roadmap exists
|
|
31
|
+
* to remove.
|
|
32
|
+
*
|
|
33
|
+
* So: the plaintext `deny` array stays in force and is not deleted, and
|
|
34
|
+
* `deny_digests` ships EMPTY. When it is empty this module does no work at all.
|
|
35
|
+
* The cutover recipe is in `docs/maintainers/source-deny-digests.md`.
|
|
36
|
+
*/
|
|
37
|
+
|
|
38
|
+
import * as crypto from 'node:crypto';
|
|
39
|
+
|
|
40
|
+
/** Environment variable carrying the HMAC key (CI secret / local `.env`). */
|
|
41
|
+
export const KEY_ENV = 'SOURCE_DENY_KEY';
|
|
42
|
+
|
|
43
|
+
/** Environment variable that makes a missing key fatal instead of dormant. */
|
|
44
|
+
export const STRICT_ENV = 'SOURCE_DENY_STRICT';
|
|
45
|
+
|
|
46
|
+
/** Exit code for "strict mode asked for, key absent". Never 0 — see Phase 1.2. */
|
|
47
|
+
export const EXIT_NO_KEY = 3;
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Fold a candidate to its comparison form.
|
|
51
|
+
*
|
|
52
|
+
* Lowercase, every separator run (`-`, `_`, `.`, `/`, whitespace) collapsed to a
|
|
53
|
+
* single `-`, edges trimmed. `Foo_Bar`, `foo.bar`, `foo/bar` and `FOO - BAR` all
|
|
54
|
+
* reach the same digest, so a source cannot be smuggled past the set by
|
|
55
|
+
* re-punctuating its name.
|
|
56
|
+
*/
|
|
57
|
+
export function normalise(token: string): string {
|
|
58
|
+
return token
|
|
59
|
+
.toLowerCase()
|
|
60
|
+
.replace(/[\s._/\\-]+/g, '-')
|
|
61
|
+
.replace(/^-+|-+$/g, '');
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** HMAC-SHA256 of the normalised token, lowercase hex. */
|
|
65
|
+
export function digest(token: string, key: string): string {
|
|
66
|
+
if (!key) throw new Error('empty digest key');
|
|
67
|
+
return crypto.createHmac('sha256', key).update(normalise(token), 'utf-8').digest('hex');
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** A digest as it must appear in the config. */
|
|
71
|
+
export const DIGEST_RE = /^[0-9a-f]{64}$/;
|
|
72
|
+
|
|
73
|
+
/** Word-ish runs a source name could occupy. */
|
|
74
|
+
const WORD_RE = /[A-Za-z0-9][A-Za-z0-9._-]{2,80}/g;
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* The candidate tokens on one line.
|
|
78
|
+
*
|
|
79
|
+
* A digest cannot be regex-matched, so the line is tokenised and each candidate
|
|
80
|
+
* hashed. Three shapes are produced per word run: the run itself, the run
|
|
81
|
+
* joined to its right-hand neighbour across a `/` (the `owner/repo` form), and
|
|
82
|
+
* the same across a `-`. That is a deliberate ceiling: an attribution spread
|
|
83
|
+
* over more than two adjacent tokens is not caught, and the module docstring of
|
|
84
|
+
* `source_shape.ts` says the same thing about its own classes — a floor, not a
|
|
85
|
+
* proof.
|
|
86
|
+
*/
|
|
87
|
+
export function candidateTokens(line: string): string[] {
|
|
88
|
+
const words: string[] = [];
|
|
89
|
+
WORD_RE.lastIndex = 0;
|
|
90
|
+
let m: RegExpExecArray | null;
|
|
91
|
+
const spans: Array<{ text: string; end: number; start: number }> = [];
|
|
92
|
+
while ((m = WORD_RE.exec(line)) !== null) {
|
|
93
|
+
spans.push({ text: m[0], start: m.index, end: m.index + m[0].length });
|
|
94
|
+
}
|
|
95
|
+
for (let i = 0; i < spans.length; i += 1) {
|
|
96
|
+
const cur = spans[i] as { text: string; end: number; start: number };
|
|
97
|
+
words.push(cur.text);
|
|
98
|
+
const next = spans[i + 1];
|
|
99
|
+
if (!next) continue;
|
|
100
|
+
const between = line.slice(cur.end, next.start);
|
|
101
|
+
if (between === '/' || between === '-') words.push(`${cur.text}${between}${next.text}`);
|
|
102
|
+
}
|
|
103
|
+
return words;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* A digest matcher over one deny set. Memoises per normalised token, so the
|
|
108
|
+
* per-line cost is a Map lookup once a token has been seen anywhere in the run.
|
|
109
|
+
*/
|
|
110
|
+
export class DigestMatcher {
|
|
111
|
+
private readonly set: ReadonlySet<string>;
|
|
112
|
+
private readonly key: string;
|
|
113
|
+
private readonly memo = new Map<string, boolean>();
|
|
114
|
+
|
|
115
|
+
constructor(digests: readonly string[], key: string) {
|
|
116
|
+
this.set = new Set(digests.map((d) => d.toLowerCase()));
|
|
117
|
+
this.key = key;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/** Is this raw token in the deny set? */
|
|
121
|
+
has(token: string): boolean {
|
|
122
|
+
const norm = normalise(token);
|
|
123
|
+
if (norm.length < 3) return false;
|
|
124
|
+
const cached = this.memo.get(norm);
|
|
125
|
+
if (cached !== undefined) return cached;
|
|
126
|
+
const hit = this.set.has(digest(norm, this.key));
|
|
127
|
+
this.memo.set(norm, hit);
|
|
128
|
+
return hit;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/** The denied tokens on one line, deduplicated, normalised form. */
|
|
132
|
+
hits(line: string): string[] {
|
|
133
|
+
const out = new Set<string>();
|
|
134
|
+
for (const t of candidateTokens(line)) if (this.has(t)) out.add(normalise(t));
|
|
135
|
+
return [...out];
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
get size(): number {
|
|
139
|
+
return this.set.size;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export interface DigestModeVerdict {
|
|
144
|
+
/** Digest matching is active this run. */
|
|
145
|
+
active: boolean;
|
|
146
|
+
/** Digests present in the config. */
|
|
147
|
+
digests: number;
|
|
148
|
+
/** Set, but the key is missing. */
|
|
149
|
+
keyMissing: boolean;
|
|
150
|
+
/** Strict mode was requested. */
|
|
151
|
+
strict: boolean;
|
|
152
|
+
/** One line for stdout/stderr. Empty when there is nothing to say. */
|
|
153
|
+
message: string;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Decide what digest matching does this run — the loud-when-keyless contract
|
|
158
|
+
* from Phase 1.2.
|
|
159
|
+
*
|
|
160
|
+
* | digests | key | strict | verdict |
|
|
161
|
+
* |---|---|---|---|
|
|
162
|
+
* | none | — | no | dormant, silent (today's shipped state) |
|
|
163
|
+
* | none | — | yes | **fatal**: strict asked for and there is nothing to enforce |
|
|
164
|
+
* | some | absent | no | **loud warning**, matching skipped — never silently green |
|
|
165
|
+
* | some | absent | yes | **fatal** (`EXIT_NO_KEY`) |
|
|
166
|
+
* | some | present | — | active |
|
|
167
|
+
*/
|
|
168
|
+
export function digestMode(opts: {
|
|
169
|
+
digests: readonly string[];
|
|
170
|
+
key: string | undefined;
|
|
171
|
+
strict: boolean;
|
|
172
|
+
}): DigestModeVerdict {
|
|
173
|
+
const n = opts.digests.length;
|
|
174
|
+
const key = opts.key ?? '';
|
|
175
|
+
if (n === 0) {
|
|
176
|
+
return {
|
|
177
|
+
active: false,
|
|
178
|
+
digests: 0,
|
|
179
|
+
keyMissing: false,
|
|
180
|
+
strict: opts.strict,
|
|
181
|
+
message: opts.strict
|
|
182
|
+
? `${STRICT_ENV} is set but the config carries no deny_digests — strict mode has ` +
|
|
183
|
+
'nothing to enforce. Run the digest build step before enabling strict mode.'
|
|
184
|
+
: '',
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
if (key === '') {
|
|
188
|
+
return {
|
|
189
|
+
active: false,
|
|
190
|
+
digests: n,
|
|
191
|
+
keyMissing: true,
|
|
192
|
+
strict: opts.strict,
|
|
193
|
+
message:
|
|
194
|
+
`⚠️ ${String(n)} deny digest(s) present but ${KEY_ENV} is unset — digest matching ` +
|
|
195
|
+
'is SKIPPED this run. This run did NOT check the hashed deny set. Set ' +
|
|
196
|
+
`${KEY_ENV} (CI secret / local .env) to enforce it.`,
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
return { active: true, digests: n, keyMissing: false, strict: opts.strict, message: '' };
|
|
200
|
+
}
|