@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
|
@@ -42,6 +42,7 @@ import * as path from 'node:path';
|
|
|
42
42
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
43
43
|
|
|
44
44
|
import { runGateCli, runSelfTest, type SelfTestCase } from './_lib/gate_self_test.js';
|
|
45
|
+
import { GateLedger } from './_lib/gate_ledger.js';
|
|
45
46
|
import { DeadScopeError, reportScanned } from './_lib/scan_scope.js';
|
|
46
47
|
|
|
47
48
|
const _HERE = fileURLToPath(import.meta.url);
|
|
@@ -292,6 +293,40 @@ function main(argv?: readonly string[]): number {
|
|
|
292
293
|
return 1;
|
|
293
294
|
}
|
|
294
295
|
const { readings, malformed } = parseStore(text);
|
|
296
|
+
|
|
297
|
+
// Per-line accounting. `malformed` is a COUNT, and a count cannot say WHICH
|
|
298
|
+
// lines were dropped — a store whose last 40 appends were malformed reads
|
|
299
|
+
// the same as one with 40 fewer readings, which is the "looked at almost
|
|
300
|
+
// nothing" case. The ledger names each dropped line.
|
|
301
|
+
const ledger = new GateLedger('check_composite_arming');
|
|
302
|
+
const storeLines = text.split('\n').map((l, i) => ({ n: i + 1, t: l.trim() })).filter((l) => l.t !== '');
|
|
303
|
+
ledger.plan(storeLines.map((l) => `line:${String(l.n)}`));
|
|
304
|
+
let parsedSeen = 0;
|
|
305
|
+
for (const l of storeLines) {
|
|
306
|
+
let ok = false;
|
|
307
|
+
try {
|
|
308
|
+
const o = JSON.parse(l.t) as { session?: unknown };
|
|
309
|
+
ok = typeof o.session === 'string' && 'composite_ms' in (o as object);
|
|
310
|
+
} catch {
|
|
311
|
+
ok = false;
|
|
312
|
+
}
|
|
313
|
+
if (ok) {
|
|
314
|
+
parsedSeen += 1;
|
|
315
|
+
ledger.complete(`line:${String(l.n)}`);
|
|
316
|
+
} else {
|
|
317
|
+
ledger.skip(`line:${String(l.n)}`, 'precondition_unmet');
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
ledger.report();
|
|
321
|
+
/* c8 ignore next 6 */
|
|
322
|
+
if (parsedSeen !== readings.length) {
|
|
323
|
+
process.stderr.write(
|
|
324
|
+
`❌ check_composite_arming: ledger counted ${String(parsedSeen)} parseable line(s) but ` +
|
|
325
|
+
`parseStore returned ${String(readings.length)} reading(s) — the accounting and the ` +
|
|
326
|
+
'parser disagree, which means one of them is wrong about what was read.\n',
|
|
327
|
+
);
|
|
328
|
+
return 2;
|
|
329
|
+
}
|
|
295
330
|
if (malformed > 0 && readings.length === 0) {
|
|
296
331
|
process.stderr.write(
|
|
297
332
|
`❌ ${path.relative(REPO_ROOT, store)}: ${String(malformed)} malformed line(s) and no usable ` +
|
|
@@ -0,0 +1,365 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Every hook concern added from here on carries a recorded answer — or fails.
|
|
4
|
+
*
|
|
5
|
+
* The sibling of `check_skill_admissions.ts`, same shape and same reasons, with
|
|
6
|
+
* one structural difference that changes the mechanism rather than the policy.
|
|
7
|
+
*
|
|
8
|
+
* ## Forward-only, and why the scope is computed differently
|
|
9
|
+
*
|
|
10
|
+
* A skill is a FILE, so its gate reads `git diff --diff-filter=A` and the 299
|
|
11
|
+
* already in the tree are grandfathered by construction. A concern is a KEY
|
|
12
|
+
* INSIDE ONE FILE — `src/scripts/hook_manifest.yaml` — so a file-level diff
|
|
13
|
+
* says only "the manifest changed", which is true of every concern edit and
|
|
14
|
+
* discriminates nothing.
|
|
15
|
+
*
|
|
16
|
+
* The scope here is therefore a SET DIFFERENCE over the parsed ids: the
|
|
17
|
+
* concerns at HEAD minus the concerns at the base ref. Both sides go through
|
|
18
|
+
* the same scoped parser (`_lib/concern_estate.ts`), for the reason that module
|
|
19
|
+
* exists — two readings of one corpus is how a gate comes to compare a number
|
|
20
|
+
* against a differently-derived number.
|
|
21
|
+
*
|
|
22
|
+
* The grandfathering is identical in effect: the 55 already in the tree need no
|
|
23
|
+
* ledger row and cannot be forgotten off one.
|
|
24
|
+
*
|
|
25
|
+
* ## The questions are NOT the skill questions
|
|
26
|
+
*
|
|
27
|
+
* They are derived from `docs/contracts/hook-architecture-v1.md`, because what
|
|
28
|
+
* makes a concern risky is different from what makes a skill risky. A concern
|
|
29
|
+
* binds to a slot, on hosts that may or may not honour a deny, inside a
|
|
30
|
+
* per-event budget — and none of that has a skill analogue.
|
|
31
|
+
*
|
|
32
|
+
* ## A rejected row is first-class
|
|
33
|
+
*
|
|
34
|
+
* `decision: rejected` records a visible "no", and a rejected row naming a
|
|
35
|
+
* concern that EXISTS is itself a failure: it means the refusal was recorded
|
|
36
|
+
* and then overridden without the row being updated, which is worse than no
|
|
37
|
+
* record at all.
|
|
38
|
+
*/
|
|
39
|
+
|
|
40
|
+
import * as fs from 'node:fs';
|
|
41
|
+
import * as path from 'node:path';
|
|
42
|
+
import { spawnSync } from 'node:child_process';
|
|
43
|
+
|
|
44
|
+
import { CONCERN_MANIFEST_POSIX, concernIds } from './_lib/concern_estate.js';
|
|
45
|
+
|
|
46
|
+
const REPO_ROOT = process.cwd();
|
|
47
|
+
|
|
48
|
+
export const LEDGER_REL = 'agents/decisions/concern-admissions.jsonl';
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* The five answers, each a non-empty sentence.
|
|
52
|
+
*
|
|
53
|
+
* Every one is a question `hook-architecture-v1.md` makes answerable and a
|
|
54
|
+
* reviewer would otherwise have to reconstruct from the manifest by hand.
|
|
55
|
+
*/
|
|
56
|
+
export const REQUIRED_ANSWERS = [
|
|
57
|
+
/** Which lifecycle slot it binds — `pre_tool_use`, `stop`, and so on. */
|
|
58
|
+
'slot',
|
|
59
|
+
/** Which hosts actually BIND it, as opposed to which declare the slot. */
|
|
60
|
+
'binding_hosts',
|
|
61
|
+
/** What it does where the host ignores a deny — the honest-coverage answer. */
|
|
62
|
+
'behaviour_where_deny_ignored',
|
|
63
|
+
/** Why an existing concern cannot carry this. */
|
|
64
|
+
'why_not_extend',
|
|
65
|
+
/** What it costs against the per-(platform,event) budget. */
|
|
66
|
+
'per_event_budget_impact',
|
|
67
|
+
] as const;
|
|
68
|
+
|
|
69
|
+
export const DECISIONS = ['admitted', 'rejected'] as const;
|
|
70
|
+
export type Decision = (typeof DECISIONS)[number];
|
|
71
|
+
|
|
72
|
+
export interface AdmissionRow {
|
|
73
|
+
concern: string;
|
|
74
|
+
decision: Decision;
|
|
75
|
+
date: string;
|
|
76
|
+
slot?: string;
|
|
77
|
+
binding_hosts?: string;
|
|
78
|
+
behaviour_where_deny_ignored?: string;
|
|
79
|
+
why_not_extend?: string;
|
|
80
|
+
per_event_budget_impact?: string;
|
|
81
|
+
/** Free-form; only meaningful on a `rejected` row. */
|
|
82
|
+
instead?: string;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export interface Finding {
|
|
86
|
+
concern: string;
|
|
87
|
+
kind: 'missing_row' | 'incomplete_row' | 'rejected_but_present' | 'bad_decision' | 'duplicate_row';
|
|
88
|
+
message: string;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function isRow(v: unknown): v is AdmissionRow {
|
|
92
|
+
return typeof v === 'object' && v !== null && typeof (v as AdmissionRow).concern === 'string';
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export function readLedger(root = REPO_ROOT): AdmissionRow[] {
|
|
96
|
+
const p = path.join(root, LEDGER_REL);
|
|
97
|
+
if (!fs.existsSync(p)) {
|
|
98
|
+
// An absent ledger is not an empty corpus: it is a gate with nothing to
|
|
99
|
+
// read, and the forward-only scope means an absent ledger only matters
|
|
100
|
+
// once something has been added.
|
|
101
|
+
return [];
|
|
102
|
+
}
|
|
103
|
+
const out: AdmissionRow[] = [];
|
|
104
|
+
let n = 0;
|
|
105
|
+
for (const line of fs.readFileSync(p, 'utf-8').split('\n')) {
|
|
106
|
+
n += 1;
|
|
107
|
+
const t = line.trim();
|
|
108
|
+
if (t === '') continue;
|
|
109
|
+
let parsed: unknown;
|
|
110
|
+
try {
|
|
111
|
+
parsed = JSON.parse(t);
|
|
112
|
+
} catch {
|
|
113
|
+
throw new Error(
|
|
114
|
+
`${LEDGER_REL}:${String(n)} is not valid JSON. A malformed ledger is a hard ` +
|
|
115
|
+
'failure, never a skipped line — a gate that silently drops rows admits by accident.',
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
if (isRow(parsed)) out.push(parsed);
|
|
119
|
+
}
|
|
120
|
+
return out;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/** Concern ids declared at a git ref, or `null` when the manifest is unreadable there. */
|
|
124
|
+
export function concernsAt(root: string, ref: string): Set<string> | null {
|
|
125
|
+
const res = spawnSync('git', ['show', `${ref}:${CONCERN_MANIFEST_POSIX}`], {
|
|
126
|
+
cwd: root,
|
|
127
|
+
encoding: 'utf-8',
|
|
128
|
+
maxBuffer: 32 * 1024 * 1024,
|
|
129
|
+
});
|
|
130
|
+
if (res.status !== 0) return null;
|
|
131
|
+
return new Set(concernIds(res.stdout ?? ''));
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/** Concern ids in the working tree. */
|
|
135
|
+
export function liveConcerns(root = REPO_ROOT): Set<string> {
|
|
136
|
+
const p = path.join(root, CONCERN_MANIFEST_POSIX);
|
|
137
|
+
if (!fs.existsSync(p)) return new Set();
|
|
138
|
+
return new Set(concernIds(fs.readFileSync(p, 'utf-8')));
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Concerns ADDED since `baseRef` — the forward-only scope.
|
|
143
|
+
*
|
|
144
|
+
* A set difference rather than a file diff, because a concern is a key inside
|
|
145
|
+
* one file. `null` from `concernsAt` means the manifest could not be read at
|
|
146
|
+
* the base, in which case NOTHING is treated as added: a ref that predates the
|
|
147
|
+
* file must not retroactively demand a row for all 55.
|
|
148
|
+
*/
|
|
149
|
+
export function addedConcerns(root: string, baseRef: string): string[] {
|
|
150
|
+
const base = concernsAt(root, baseRef);
|
|
151
|
+
if (base === null) return [];
|
|
152
|
+
return [...liveConcerns(root)].filter((id) => !base.has(id)).sort();
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export function evaluate(root = REPO_ROOT, baseRef: string | null): Finding[] {
|
|
156
|
+
const findings: Finding[] = [];
|
|
157
|
+
const rows = readLedger(root);
|
|
158
|
+
const live = liveConcerns(root);
|
|
159
|
+
|
|
160
|
+
const seen = new Map<string, number>();
|
|
161
|
+
for (const r of rows) {
|
|
162
|
+
seen.set(r.concern, (seen.get(r.concern) ?? 0) + 1);
|
|
163
|
+
if (!(DECISIONS as readonly string[]).includes(r.decision)) {
|
|
164
|
+
findings.push({
|
|
165
|
+
concern: r.concern,
|
|
166
|
+
kind: 'bad_decision',
|
|
167
|
+
message: `decision must be one of ${DECISIONS.join(' | ')}, got ${JSON.stringify(r.decision)}`,
|
|
168
|
+
});
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
171
|
+
if (r.decision === 'rejected' && live.has(r.concern)) {
|
|
172
|
+
findings.push({
|
|
173
|
+
concern: r.concern,
|
|
174
|
+
kind: 'rejected_but_present',
|
|
175
|
+
message:
|
|
176
|
+
'the ledger records a REJECTION and the concern exists in the manifest. A refusal that ' +
|
|
177
|
+
'was later overridden without updating its row is worse than no record at all.',
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
for (const [id, n] of seen) {
|
|
182
|
+
if (n > 1) {
|
|
183
|
+
findings.push({
|
|
184
|
+
concern: id,
|
|
185
|
+
kind: 'duplicate_row',
|
|
186
|
+
message: `${String(n)} rows name this concern; one decision per concern, amended in place`,
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
if (baseRef === null) return findings;
|
|
192
|
+
|
|
193
|
+
const admitted = new Map(rows.filter((r) => r.decision === 'admitted').map((r) => [r.concern, r]));
|
|
194
|
+
for (const id of addedConcerns(root, baseRef)) {
|
|
195
|
+
const row = admitted.get(id);
|
|
196
|
+
if (row === undefined) {
|
|
197
|
+
findings.push({
|
|
198
|
+
concern: id,
|
|
199
|
+
kind: 'missing_row',
|
|
200
|
+
message: `added since ${baseRef} with no \`admitted\` row in ${LEDGER_REL}`,
|
|
201
|
+
});
|
|
202
|
+
continue;
|
|
203
|
+
}
|
|
204
|
+
const blank = REQUIRED_ANSWERS.filter((k) => {
|
|
205
|
+
const v = row[k];
|
|
206
|
+
return typeof v !== 'string' || v.trim() === '';
|
|
207
|
+
});
|
|
208
|
+
if (blank.length > 0) {
|
|
209
|
+
findings.push({
|
|
210
|
+
concern: id,
|
|
211
|
+
kind: 'incomplete_row',
|
|
212
|
+
message: `row is missing a real answer for: ${blank.join(', ')}`,
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
return findings;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// ---------------------------------------------------------------------------
|
|
220
|
+
// CLI
|
|
221
|
+
// ---------------------------------------------------------------------------
|
|
222
|
+
|
|
223
|
+
import { reportScanned } from './_lib/scan_scope.js';
|
|
224
|
+
import { runGateCli, runSelfTest, type SelfTestCase } from './_lib/gate_self_test.js';
|
|
225
|
+
import * as os from 'node:os';
|
|
226
|
+
|
|
227
|
+
function resolveBase(root: string): string | null {
|
|
228
|
+
for (const ref of ['origin/main', 'main']) {
|
|
229
|
+
const r = spawnSync('git', ['rev-parse', '--verify', '--quiet', ref], { cwd: root, encoding: 'utf-8' });
|
|
230
|
+
if (r.status === 0) return ref;
|
|
231
|
+
}
|
|
232
|
+
return null;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
const MANIFEST = (ids: readonly string[]): string =>
|
|
236
|
+
['concerns:', ...ids.map((i) => ` ${i}:\n severity: advisory`), 'roles:', ' dev:', ''].join('\n');
|
|
237
|
+
|
|
238
|
+
const ROW = (concern: string, decision: Decision, blank?: string): string =>
|
|
239
|
+
JSON.stringify({
|
|
240
|
+
concern,
|
|
241
|
+
decision,
|
|
242
|
+
date: '2026-08-30',
|
|
243
|
+
...Object.fromEntries(REQUIRED_ANSWERS.map((k) => [k, k === blank ? '' : `a real answer for ${k}`])),
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
function selfTest(): number {
|
|
247
|
+
const roots: string[] = [];
|
|
248
|
+
const script = path.join('src', 'scripts', 'check_concern_admissions.ts');
|
|
249
|
+
|
|
250
|
+
/** A throwaway repo with a base commit and a working-tree change on top. */
|
|
251
|
+
const fixture = (opts: { base: readonly string[]; head: readonly string[]; ledger?: string }): number => {
|
|
252
|
+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'ca-selftest-'));
|
|
253
|
+
roots.push(dir);
|
|
254
|
+
const g = (...a: string[]): void => {
|
|
255
|
+
spawnSync('git', a, { cwd: dir, encoding: 'utf-8' });
|
|
256
|
+
};
|
|
257
|
+
const put = (rel: string, body: string): void => {
|
|
258
|
+
fs.mkdirSync(path.join(dir, path.dirname(rel)), { recursive: true });
|
|
259
|
+
fs.writeFileSync(path.join(dir, rel), body, 'utf-8');
|
|
260
|
+
};
|
|
261
|
+
g('init', '-q', '-b', 'main');
|
|
262
|
+
g('config', 'user.email', 'selftest@local');
|
|
263
|
+
g('config', 'user.name', 'selftest');
|
|
264
|
+
put(CONCERN_MANIFEST_POSIX, MANIFEST(opts.base));
|
|
265
|
+
put(LEDGER_REL, '');
|
|
266
|
+
g('add', '-A');
|
|
267
|
+
g('commit', '-q', '-m', 'base');
|
|
268
|
+
put(CONCERN_MANIFEST_POSIX, MANIFEST(opts.head));
|
|
269
|
+
if (opts.ledger !== undefined) put(LEDGER_REL, `${opts.ledger}\n`);
|
|
270
|
+
// repoRoot locates tsx AND resolves the relative script path, so it is
|
|
271
|
+
// THIS checkout; cwd and --root are the fixture.
|
|
272
|
+
return runGateCli(REPO_ROOT, script, ['--base', 'main', '--root', dir], dir);
|
|
273
|
+
};
|
|
274
|
+
|
|
275
|
+
const cases: SelfTestCase[] = [
|
|
276
|
+
{
|
|
277
|
+
name: 'no concern added → accept',
|
|
278
|
+
expect: 'accept',
|
|
279
|
+
run: () => fixture({ base: ['one'], head: ['one'] }),
|
|
280
|
+
},
|
|
281
|
+
{
|
|
282
|
+
name: 'a concern added with no ledger row → reject',
|
|
283
|
+
expect: 'reject',
|
|
284
|
+
run: () => fixture({ base: ['one'], head: ['one', 'two'] }),
|
|
285
|
+
},
|
|
286
|
+
{
|
|
287
|
+
name: 'a concern added WITH a complete admitted row → accept',
|
|
288
|
+
expect: 'accept',
|
|
289
|
+
run: () => fixture({ base: ['one'], head: ['one', 'two'], ledger: ROW('two', 'admitted') }),
|
|
290
|
+
},
|
|
291
|
+
{
|
|
292
|
+
// A row that exists but answers nothing is the shape a gate is
|
|
293
|
+
// easiest to satisfy dishonestly, so it is tested explicitly.
|
|
294
|
+
name: 'an admitted row with a blank answer → reject',
|
|
295
|
+
expect: 'reject',
|
|
296
|
+
run: () =>
|
|
297
|
+
fixture({ base: ['one'], head: ['one', 'two'], ledger: ROW('two', 'admitted', 'why_not_extend') }),
|
|
298
|
+
},
|
|
299
|
+
{
|
|
300
|
+
name: 'a rejected row naming a LIVE concern → reject',
|
|
301
|
+
expect: 'reject',
|
|
302
|
+
run: () => fixture({ base: ['one', 'two'], head: ['one', 'two'], ledger: ROW('two', 'rejected') }),
|
|
303
|
+
},
|
|
304
|
+
{
|
|
305
|
+
// The grandfathering, asserted rather than assumed: pre-existing
|
|
306
|
+
// concerns must never demand a row.
|
|
307
|
+
name: 'the pre-existing set needs no rows → accept',
|
|
308
|
+
expect: 'accept',
|
|
309
|
+
run: () => fixture({ base: ['a', 'b', 'c'], head: ['a', 'b', 'c'] }),
|
|
310
|
+
},
|
|
311
|
+
];
|
|
312
|
+
|
|
313
|
+
try {
|
|
314
|
+
return runSelfTest({ gate: 'check_concern_admissions', cases, minCases: 5, minRejectCases: 3 });
|
|
315
|
+
} finally {
|
|
316
|
+
for (const d of roots) fs.rmSync(d, { recursive: true, force: true });
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
export function main(argv: string[] = process.argv.slice(2), rootIn = REPO_ROOT): number {
|
|
321
|
+
if (argv.includes('--self-test')) return selfTest();
|
|
322
|
+
const i = argv.indexOf('--base');
|
|
323
|
+
const j = argv.indexOf('--root');
|
|
324
|
+
const root = j >= 0 && argv[j + 1] !== undefined ? path.resolve(argv[j + 1] as string) : rootIn;
|
|
325
|
+
const baseRef = i >= 0 ? (argv[i + 1] as string) : resolveBase(root);
|
|
326
|
+
|
|
327
|
+
const rows = readLedger(root);
|
|
328
|
+
const added = baseRef === null ? [] : addedConcerns(root, baseRef);
|
|
329
|
+
reportScanned({
|
|
330
|
+
gate: 'check_concern_admissions',
|
|
331
|
+
scanned: rows.length + added.length,
|
|
332
|
+
units: 'admission row(s) + added concern(s)',
|
|
333
|
+
roots: [LEDGER_REL, CONCERN_MANIFEST_POSIX],
|
|
334
|
+
// The state this ships in: 55 concerns are grandfathered by the
|
|
335
|
+
// forward-only set-difference scope, so the first row arrives with the
|
|
336
|
+
// first new concern. `readLedger` throws on a MALFORMED ledger, so
|
|
337
|
+
// "moved" and "empty" stay different verdicts.
|
|
338
|
+
allowEmpty:
|
|
339
|
+
'EMPTY_VALID: no concern has been admitted or refused through this ledger yet — the ' +
|
|
340
|
+
'pre-existing 55 are grandfathered by the forward-only set-difference scope, and a ' +
|
|
341
|
+
'malformed ledger is a separate hard error in readLedger()',
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
const findings = evaluate(root, baseRef);
|
|
345
|
+
if (baseRef === null) {
|
|
346
|
+
process.stdout.write(
|
|
347
|
+
' ⏭️ no base ref resolved — the added-concern scope could not be computed; ' +
|
|
348
|
+
'ledger consistency was still checked\n',
|
|
349
|
+
);
|
|
350
|
+
}
|
|
351
|
+
if (findings.length === 0) {
|
|
352
|
+
process.stdout.write(
|
|
353
|
+
`✅ check_concern_admissions: ${String(added.length)} concern(s) added since ` +
|
|
354
|
+
`${baseRef ?? '<no base>'}, each carrying a recorded answer.\n`,
|
|
355
|
+
);
|
|
356
|
+
return 0;
|
|
357
|
+
}
|
|
358
|
+
process.stderr.write(`❌ check_concern_admissions: ${String(findings.length)} finding(s):\n`);
|
|
359
|
+
for (const f of findings) process.stderr.write(` 🔴 ${f.concern} [${f.kind}] — ${f.message}\n`);
|
|
360
|
+
return 1;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
if (process.argv[1] !== undefined && process.argv[1].includes('check_concern_admissions')) {
|
|
364
|
+
process.exit(main());
|
|
365
|
+
}
|
|
@@ -852,6 +852,34 @@ function main(argv: string[]): number {
|
|
|
852
852
|
}
|
|
853
853
|
const base = JSON.parse(fs.readFileSync(BASELINE, 'utf-8')).summary as Summary;
|
|
854
854
|
const regressions: string[] = [];
|
|
855
|
+
|
|
856
|
+
// `undeclared` is the only counter here that is a TRUE shrink-only
|
|
857
|
+
// ratchet, and the asymmetry is worth stating because the two below it
|
|
858
|
+
// are not.
|
|
859
|
+
//
|
|
860
|
+
// A rule with no declaration has no carrier, so it can contribute to
|
|
861
|
+
// neither `frequency_gap` nor `local_only`. DECLARING one truthfully can
|
|
862
|
+
// therefore only RAISE both — the gap and the taskfile-only reach were
|
|
863
|
+
// always there, and the declaration is what made them computable. Both
|
|
864
|
+
// checks compare a number against a baseline taken when the rule was
|
|
865
|
+
// invisible, so an honest declaring change reds them by construction.
|
|
866
|
+
//
|
|
867
|
+
// Measured 2026-08-27, landing 14 declarations from
|
|
868
|
+
// `road-to-undeclared-obligation-disposition` Phase 1.3:
|
|
869
|
+
// `frequency gaps rose: 9 → 14` and `validators fell back to
|
|
870
|
+
// taskfile-only: 0 → 3`. Nothing fell back and no carrier fired less
|
|
871
|
+
// often. Both readings were new visibility.
|
|
872
|
+
//
|
|
873
|
+
// The sanctioned response is the one the failure message already names —
|
|
874
|
+
// regenerate the baseline — and the obligation this comment adds is that
|
|
875
|
+
// a change doing so SAYS WHY, because a regenerated baseline and a
|
|
876
|
+
// hidden regression look identical in a diff.
|
|
877
|
+
if (summary.undeclared > (base.undeclared ?? Number.POSITIVE_INFINITY)) {
|
|
878
|
+
regressions.push(
|
|
879
|
+
`undeclared rules rose: ${base.undeclared} → ${summary.undeclared} ` +
|
|
880
|
+
`(a rule lost its enforced_by, or a new rule landed without one)`,
|
|
881
|
+
);
|
|
882
|
+
}
|
|
855
883
|
if (summary.blocking < base.blocking) {
|
|
856
884
|
regressions.push(`blocking coverage fell: ${base.blocking} → ${summary.blocking}`);
|
|
857
885
|
}
|
|
@@ -153,6 +153,7 @@ import { GateLedger } from './_lib/gate_ledger.js';
|
|
|
153
153
|
import { resolveBaseRef } from './_lib/ratchet_base_ref.js';
|
|
154
154
|
import { materialiseSubtree } from './_lib/base_tree.js';
|
|
155
155
|
import { SKILLS_POSIX, measureSkillEstate } from './_lib/skill_estate.js';
|
|
156
|
+
import { CONCERN_MANIFEST_POSIX, countConcerns } from './_lib/concern_estate.js';
|
|
156
157
|
import { runGateCli, runSelfTest, type SelfTestCase } from './_lib/gate_self_test.js';
|
|
157
158
|
|
|
158
159
|
const GATE = 'check_estate_count';
|
|
@@ -190,6 +191,23 @@ export interface EstateCounts {
|
|
|
190
191
|
* "no tokens" — see `tokensExact`.
|
|
191
192
|
*/
|
|
192
193
|
skill_description_tokens: number;
|
|
194
|
+
/**
|
|
195
|
+
* Hook concerns declared in `src/scripts/hook_manifest.yaml`.
|
|
196
|
+
*
|
|
197
|
+
* A FIFTH corpus on the same machinery, and the same argument that admitted
|
|
198
|
+
* skills: the floor-from-the-base-ref work is the expensive part and should
|
|
199
|
+
* not be written a third time, and a separate ratchet is a separate place to
|
|
200
|
+
* forget.
|
|
201
|
+
*
|
|
202
|
+
* Read with the SCOPED parser in `_lib/concern_estate.ts`, not the
|
|
203
|
+
* whole-file grep the originating roadmap reproduces its series with. That
|
|
204
|
+
* grep over-counts by exactly 16 at every one of its six pins, because
|
|
205
|
+
* `roles:`, `platforms:` and `native_event_aliases:` sit at the same indent.
|
|
206
|
+
* The growth finding survives the correction — the delta is constant — but
|
|
207
|
+
* the absolute figure does not: the axis stands at 55, not 71, and a floor
|
|
208
|
+
* of 71 is one this gate's own parser could never reproduce.
|
|
209
|
+
*/
|
|
210
|
+
concern_count: number;
|
|
193
211
|
}
|
|
194
212
|
|
|
195
213
|
export type MetricName = keyof EstateCounts;
|
|
@@ -200,6 +218,7 @@ export const METRICS: readonly MetricName[] = [
|
|
|
200
218
|
'open_blockers',
|
|
201
219
|
'skill_count',
|
|
202
220
|
'skill_description_tokens',
|
|
221
|
+
'concern_count',
|
|
203
222
|
];
|
|
204
223
|
|
|
205
224
|
export interface EstateBudget {
|
|
@@ -303,6 +322,29 @@ function repoRootFrom(start: string): string {
|
|
|
303
322
|
}
|
|
304
323
|
}
|
|
305
324
|
|
|
325
|
+
/** File contents, or `null` when it is absent. Absent is a real state here. */
|
|
326
|
+
function readIfPresent(file: string): string | null {
|
|
327
|
+
try {
|
|
328
|
+
return fs.readFileSync(file, 'utf-8');
|
|
329
|
+
} catch {
|
|
330
|
+
return null;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* Concern count at a git ref, or `null` when the manifest is unreadable there.
|
|
336
|
+
*
|
|
337
|
+
* `null` is NOT zero, and the distinction is the whole reason this returns an
|
|
338
|
+
* option. A missing manifest read as 0 makes the floor 0, which makes the
|
|
339
|
+
* ratchet inert on that path while still printing a green line — "a gate that
|
|
340
|
+
* read nothing has not passed", in this repository's own words. The caller
|
|
341
|
+
* turns `null` into a floor-skip, which this gate already treats as a failure.
|
|
342
|
+
*/
|
|
343
|
+
function concernCountAt(repoRoot: string, ref: string): number | null {
|
|
344
|
+
const res = git(['show', `${ref}:${CONCERN_MANIFEST_POSIX}`], repoRoot);
|
|
345
|
+
return res.ok ? countConcerns(res.stdout) : null;
|
|
346
|
+
}
|
|
347
|
+
|
|
306
348
|
function git(args: readonly string[], cwd: string): { ok: boolean; stdout: string } {
|
|
307
349
|
const res = spawnSync('git', [...args], { cwd, encoding: 'utf-8', maxBuffer: 32 * 1024 * 1024 });
|
|
308
350
|
return { ok: res.status === 0, stdout: res.stdout ?? '' };
|
|
@@ -389,6 +431,7 @@ export function countEstate(repoRoot: string): EstateCounts {
|
|
|
389
431
|
// against a proxy one — they differ by more than the growth a ratchet
|
|
390
432
|
// is trying to catch.
|
|
391
433
|
skill_description_tokens: skills.skill_description_tokens ?? 0,
|
|
434
|
+
concern_count: countConcerns(readIfPresent(path.join(repoRoot, CONCERN_MANIFEST_POSIX)) ?? ''),
|
|
392
435
|
};
|
|
393
436
|
}
|
|
394
437
|
|
|
@@ -554,6 +597,15 @@ export function evaluate(
|
|
|
554
597
|
// property, and unlike a number nobody has to remember to update it.
|
|
555
598
|
let floor: EstateCounts | null = null;
|
|
556
599
|
let floorSkipReason: string | null = null;
|
|
600
|
+
/**
|
|
601
|
+
* Does the WORKING TREE carry a manifest at all?
|
|
602
|
+
*
|
|
603
|
+
* The base-ref read only matters when it does. A repository with no manifest
|
|
604
|
+
* on either side has no concern axis to ratchet, and failing there would
|
|
605
|
+
* turn every unrelated fixture red — which is how the first version of this
|
|
606
|
+
* clause was caught.
|
|
607
|
+
*/
|
|
608
|
+
const headHasManifest = fs.existsSync(path.join(repoRoot, CONCERN_MANIFEST_POSIX));
|
|
557
609
|
/** Set when the base ref carries no `src/skills` — the skill metrics drop. */
|
|
558
610
|
let skillFloorSkipReason: string | null = null;
|
|
559
611
|
/** Set when either side's tokeniser did not resolve — that ONE metric drops. */
|
|
@@ -584,7 +636,14 @@ export function evaluate(
|
|
|
584
636
|
if (baseSkills.error !== null || baseSkills.files === 0) {
|
|
585
637
|
// Roadmap floor stands; the skill floor is stated as
|
|
586
638
|
// unavailable and its metrics are dropped from the compare.
|
|
587
|
-
|
|
639
|
+
const cf = concernCountAt(repoRoot, baseRef);
|
|
640
|
+
floor = { ...roadmapFloor, concern_count: cf ?? 0 };
|
|
641
|
+
if (cf === null && headHasManifest) {
|
|
642
|
+
floorSkipReason =
|
|
643
|
+
`could not read ${CONCERN_MANIFEST_POSIX} at ${baseRef} while HEAD ` +
|
|
644
|
+
'carries one. A floor of 0 would make the concern ratchet inert while ' +
|
|
645
|
+
'still printing green, so this fails rather than skips.';
|
|
646
|
+
}
|
|
588
647
|
skillFloorSkipReason =
|
|
589
648
|
`could not read ${SKILLS_POSIX} at ${baseRef} — ` +
|
|
590
649
|
`${baseSkills.error ?? 'no files'}`;
|
|
@@ -594,7 +653,23 @@ export function evaluate(
|
|
|
594
653
|
...roadmapFloor,
|
|
595
654
|
skill_count: s.skill_count,
|
|
596
655
|
skill_description_tokens: s.skill_description_tokens ?? 0,
|
|
656
|
+
// A SINGLE FILE, so `git show` rather than a third
|
|
657
|
+
// `materialiseSubtree` — stated because the roadmap asks
|
|
658
|
+
// which of the two this uses. Materialising a subtree to
|
|
659
|
+
// reach one known path would spend a temp tree and a
|
|
660
|
+
// recursive ls-tree on a `git cat-file` in disguise.
|
|
661
|
+
//
|
|
662
|
+
// `?? 0` never stands for "unreadable": the branch below
|
|
663
|
+
// turns that into a floor-skip, which fails. This
|
|
664
|
+
// coalesce only satisfies the type.
|
|
665
|
+
concern_count: concernCountAt(repoRoot, baseRef) ?? 0,
|
|
597
666
|
};
|
|
667
|
+
if (concernCountAt(repoRoot, baseRef) === null && headHasManifest) {
|
|
668
|
+
floorSkipReason =
|
|
669
|
+
`could not read ${CONCERN_MANIFEST_POSIX} at ${baseRef} while HEAD ` +
|
|
670
|
+
'carries one. A floor of 0 would make the concern ratchet inert while ' +
|
|
671
|
+
'still printing green, so this fails rather than skips.';
|
|
672
|
+
}
|
|
598
673
|
if (s.skill_description_tokens === null) {
|
|
599
674
|
skillTokenSkipReason =
|
|
600
675
|
`the tokeniser did not resolve at ${baseRef}; an exact reading may ` +
|
|
@@ -663,6 +738,14 @@ export function evaluate(
|
|
|
663
738
|
// claim path — a recorded reason in the diff — or it fails.
|
|
664
739
|
skill_count: 0,
|
|
665
740
|
skill_description_tokens: 0,
|
|
741
|
+
// Step 1.2: NO new allowance key. The concern axis joins `skill_count`
|
|
742
|
+
// in taking the claim path or nothing, for the reason the budget file
|
|
743
|
+
// already records for skills — an allowance reopens the gaming path the
|
|
744
|
+
// dimension exists to close. Eight concerns spread across eight events
|
|
745
|
+
// violate the existing per-event cap zero times, which is exactly why a
|
|
746
|
+
// total-growth ratchet was needed and exactly why it must not carry a
|
|
747
|
+
// per-change freebie.
|
|
748
|
+
concern_count: 0,
|
|
666
749
|
};
|
|
667
750
|
|
|
668
751
|
/** Metrics dropped from the compare, with the reason, never silently. */
|
|
@@ -808,6 +891,64 @@ function selfTest(): number {
|
|
|
808
891
|
expect: 'accept',
|
|
809
892
|
run: () => fixture({ roadmaps: 3, base: 'main', after: () => undefined }),
|
|
810
893
|
},
|
|
894
|
+
{
|
|
895
|
+
// The concern axis, both directions. Seeded into the BASE tree so
|
|
896
|
+
// the floor is a real reading of the fixture's own manifest rather
|
|
897
|
+
// than this repository's -- the whole point of measuring the floor
|
|
898
|
+
// from the base ref.
|
|
899
|
+
name: 'concern count grows above the base-tree floor → reject',
|
|
900
|
+
expect: 'reject',
|
|
901
|
+
run: () =>
|
|
902
|
+
fixture({
|
|
903
|
+
roadmaps: 3,
|
|
904
|
+
base: 'main',
|
|
905
|
+
before: (dir) =>
|
|
906
|
+
write(dir, CONCERN_MANIFEST_POSIX, 'concerns:\n one:\n severity: advisory\n'),
|
|
907
|
+
after: (dir) =>
|
|
908
|
+
write(
|
|
909
|
+
dir,
|
|
910
|
+
CONCERN_MANIFEST_POSIX,
|
|
911
|
+
'concerns:\n one:\n severity: advisory\n two:\n severity: advisory\n',
|
|
912
|
+
),
|
|
913
|
+
}),
|
|
914
|
+
},
|
|
915
|
+
{
|
|
916
|
+
// AC-3's explicit clause, and it corrects the first implementation
|
|
917
|
+
// of this metric: an unreadable manifest at the base ref must FAIL,
|
|
918
|
+
// never read as 0. A floor of 0 makes the ratchet inert on that
|
|
919
|
+
// path while still printing a green line, which is the shape this
|
|
920
|
+
// repository calls "a gate that read nothing has not passed".
|
|
921
|
+
name: 'base ref carries no manifest → reject (a 0 floor is not a floor)',
|
|
922
|
+
expect: 'reject',
|
|
923
|
+
run: () =>
|
|
924
|
+
fixture({
|
|
925
|
+
roadmaps: 3,
|
|
926
|
+
base: 'main',
|
|
927
|
+
after: (dir) =>
|
|
928
|
+
write(dir, CONCERN_MANIFEST_POSIX, 'concerns:\n one:\n severity: advisory\n'),
|
|
929
|
+
}),
|
|
930
|
+
},
|
|
931
|
+
{
|
|
932
|
+
// The half that proves the parser is SCOPED rather than a grep: a
|
|
933
|
+
// top-level map gaining members at the same two-space indent is not
|
|
934
|
+
// concern growth, and the roadmap's own reproduce command would
|
|
935
|
+
// have counted it as such.
|
|
936
|
+
name: 'a non-concern top-level map growing is not concern growth → accept',
|
|
937
|
+
expect: 'accept',
|
|
938
|
+
run: () =>
|
|
939
|
+
fixture({
|
|
940
|
+
roadmaps: 3,
|
|
941
|
+
base: 'main',
|
|
942
|
+
before: (dir) =>
|
|
943
|
+
write(dir, CONCERN_MANIFEST_POSIX, 'concerns:\n one:\n severity: advisory\nroles:\n dev:\n'),
|
|
944
|
+
after: (dir) =>
|
|
945
|
+
write(
|
|
946
|
+
dir,
|
|
947
|
+
CONCERN_MANIFEST_POSIX,
|
|
948
|
+
'concerns:\n one:\n severity: advisory\nroles:\n dev:\n ops:\n sre:\n',
|
|
949
|
+
),
|
|
950
|
+
}),
|
|
951
|
+
},
|
|
811
952
|
{
|
|
812
953
|
// The floor is now exact, so this case no longer depends on anyone
|
|
813
954
|
// having typed the right number at the base commit.
|