@event4u/agent-config 16.2.0 → 16.4.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 +337 -234
- package/README.md +15 -13
- package/agents/templates/.ai-council.yml.example +26 -8
- package/dist/agent-src/commands/analyze/inbox.md +11 -8
- package/dist/agent-src/commands/analyze/roadmap-repos.md +1 -1
- package/dist/agent-src/commands/bug/fix.md +17 -2
- package/dist/agent-src/commands/bug/investigate.md +1 -1
- package/dist/agent-src/commands/commit/in-chunks.md +40 -31
- package/dist/agent-src/commands/commit.md +72 -33
- package/dist/agent-src/commands/condense.md +2 -8
- package/dist/agent-src/commands/council/debate.md +11 -7
- package/dist/agent-src/commands/council/default.md +10 -9
- package/dist/agent-src/commands/council.md +1 -1
- package/dist/agent-src/commands/estimate-ticket.md +1 -1
- package/dist/agent-src/commands/feature/explore.md +3 -3
- package/dist/agent-src/commands/feature/plan.md +2 -2
- package/dist/agent-src/commands/feature/roadmap.md +1 -1
- package/dist/agent-src/commands/fix/ci.md +18 -4
- package/dist/agent-src/commands/fix/commit-messages.md +48 -23
- package/dist/agent-src/commands/fix/pr-comments.md +3 -6
- package/dist/agent-src/commands/implement-ticket.md +4 -4
- package/dist/agent-src/commands/jira-ticket.md +1 -1
- package/dist/agent-src/commands/memory/add.md +4 -16
- package/dist/agent-src/commands/memory/load.md +5 -6
- package/dist/agent-src/commands/memory/promote.md +3 -16
- package/dist/agent-src/commands/mission/upgrade.md +3 -2
- package/dist/agent-src/commands/optimize/agents-dir.md +2 -2
- package/dist/agent-src/commands/optimize/deep.md +4 -2
- package/dist/agent-src/commands/optimize/rtk.md +4 -26
- package/dist/agent-src/commands/pr/create/description-only.md +18 -15
- package/dist/agent-src/commands/pr/create.md +42 -34
- package/dist/agent-src/commands/pr/merge.md +136 -9
- package/dist/agent-src/commands/prepare-for-review.md +13 -11
- package/dist/agent-src/commands/project/analyze.md +7 -35
- package/dist/agent-src/commands/refine-ticket.md +1 -1
- package/dist/agent-src/commands/review/changes.md +14 -19
- package/dist/agent-src/commands/roadmap/create.md +1 -1
- package/dist/agent-src/commands/roadmap/materialize.md +1 -1
- package/dist/agent-src/commands/roadmap/next.md +10 -1
- package/dist/agent-src/commands/roadmap/process-full.md +56 -42
- package/dist/agent-src/commands/roadmap/resolve-blockers.md +411 -0
- package/dist/agent-src/commands/roadmap/triage-parked.md +325 -0
- package/dist/agent-src/commands/roadmap.md +18 -4
- package/dist/agent-src/commands/tests/create.md +17 -3
- package/dist/agent-src/commands/tests/execute.md +22 -5
- package/dist/agent-src/commands/worktree/create.md +7 -3
- package/dist/agent-src/commands/worktree/verify.md +2 -1
- package/dist/agent-src/contexts/authority/decision-revisit-gate-enforcement.md +43 -0
- package/dist/agent-src/contexts/communication/rules-auto/design-fidelity-enforcement.md +24 -0
- package/dist/agent-src/contexts/communication/rules-auto/think-before-action-mechanics.md +5 -3
- package/dist/agent-src/contexts/communication/rules-auto/token-efficiency-mechanics.md +7 -9
- package/dist/agent-src/contexts/contracts/command-suggestion-flow.md +8 -6
- package/dist/agent-src/contexts/contracts/frugality-charter.md +8 -5
- package/dist/agent-src/contexts/execution/auto-dispatch-classification.md +7 -7
- package/dist/agent-src/contexts/execution/auto-orchestration-activation.md +0 -1
- package/dist/agent-src/contexts/execution/evidence-discipline.md +2 -2
- package/dist/agent-src/contexts/execution/non-interactive-contract.md +5 -8
- package/dist/agent-src/contexts/execution/rdp-gate.md +4 -4
- package/dist/agent-src/contexts/execution/roadmap-execution-contract.md +9 -8
- package/dist/agent-src/contexts/execution/roadmap-process-loop.md +99 -54
- package/dist/agent-src/contexts/execution/session-canary-enforcement-history.md +67 -0
- package/dist/agent-src/contexts/execution/subagent-routing.md +8 -8
- package/dist/agent-src/contexts/execution/subagent-steering.md +6 -4
- package/dist/agent-src/contexts/execution/terminal-states.md +2 -2
- package/dist/agent-src/contexts/execution/toolchain-resolver.md +95 -7
- package/dist/agent-src/contexts/subagent-configuration.md +1 -1
- package/dist/agent-src/guidelines/agent-infra/active-remediation-mechanics.md +39 -0
- package/dist/agent-src/guidelines/agent-infra/context-hygiene-mechanics.md +1 -1
- package/dist/agent-src/guidelines/agent-infra/cross-source-consistency-mechanics.md +8 -10
- package/dist/agent-src/guidelines/agent-infra/frontier-reasoning-operating-profile.md +3 -2
- package/dist/agent-src/guidelines/agent-infra/layered-settings.md +13 -1
- package/dist/agent-src/guidelines/agent-infra/roadmap-progress-mechanics.md +1 -1
- package/dist/agent-src/guidelines/agent-infra/self-improvement-pipeline.md +10 -3
- package/dist/agent-src/guidelines/agent-infra/size-and-scope.md +36 -0
- package/dist/agent-src/guidelines/php/git.md +15 -2
- package/dist/agent-src/rules/active-remediation.md +1 -2
- package/dist/agent-src/rules/analysis-skill-routing.md +0 -1
- package/dist/agent-src/rules/architecture.md +0 -1
- package/dist/agent-src/rules/artifact-drafting-protocol.md +0 -1
- package/dist/agent-src/rules/artifact-engagement-recording.md +0 -1
- package/dist/agent-src/rules/augment-edit-discipline.md +0 -1
- package/dist/agent-src/rules/autonomous-execution.md +1 -1
- package/dist/agent-src/rules/brand-consistency.md +0 -1
- package/dist/agent-src/rules/brand-source-of-truth.md +0 -1
- package/dist/agent-src/rules/broken-access-control.md +1 -1
- package/dist/agent-src/rules/cli-output-handling.md +0 -1
- package/dist/agent-src/rules/code-comment-discipline.md +0 -1
- package/dist/agent-src/rules/code-provenance.md +0 -1
- package/dist/agent-src/rules/command-suggestion-policy.md +1 -1
- package/dist/agent-src/rules/commit-conventions.md +1 -2
- package/dist/agent-src/rules/communication-through-line.md +1 -2
- package/dist/agent-src/rules/content-quoting-floor.md +1 -2
- package/dist/agent-src/rules/context-hygiene.md +6 -6
- package/dist/agent-src/rules/copilot-routing.md +1 -2
- package/dist/agent-src/rules/council-availability.md +4 -4
- package/dist/agent-src/rules/cross-source-consistency.md +4 -6
- package/dist/agent-src/rules/decision-revisit-gate.md +17 -35
- package/dist/agent-src/rules/delegation-policy.md +2 -3
- package/dist/agent-src/rules/design-fidelity.md +12 -16
- package/dist/agent-src/rules/design-review-after-ui-write.md +3 -3
- package/dist/agent-src/rules/devcontainer-routing.md +1 -2
- package/dist/agent-src/rules/doc-screenshot-hygiene.md +1 -1
- package/dist/agent-src/rules/docker-commands.md +0 -1
- package/dist/agent-src/rules/domain-adoption-policy.md +0 -1
- package/dist/agent-src/rules/domain-safety-disclaimer.md +1 -1
- package/dist/agent-src/rules/domain-safety-pii.md +1 -1
- package/dist/agent-src/rules/domain-safety-retention.md +1 -1
- package/dist/agent-src/rules/downstream-changes.md +0 -1
- package/dist/agent-src/rules/engineering-safety-floor.md +1 -1
- package/dist/agent-src/rules/evaluator-independence.md +3 -2
- package/dist/agent-src/rules/external-code-graph-interop.md +0 -1
- package/dist/agent-src/rules/external-reference-deep-dive.md +0 -1
- package/dist/agent-src/rules/fast-path-marker-visibility.md +2 -2
- package/dist/agent-src/rules/finance-safety-floor.md +1 -1
- package/dist/agent-src/rules/fix-what-you-see.md +6 -32
- package/dist/agent-src/rules/framework-neutrality-in-generic-skills.md +0 -1
- package/dist/agent-src/rules/git-history-discipline.md +2 -2
- package/dist/agent-src/rules/guidelines.md +0 -1
- package/dist/agent-src/rules/history-discipline.md +0 -1
- package/dist/agent-src/rules/icon-consistency.md +0 -1
- package/dist/agent-src/rules/image-likeness-and-rights.md +1 -1
- package/dist/agent-src/rules/improve-before-implement.md +0 -1
- package/dist/agent-src/rules/invite-challenge.md +0 -1
- package/dist/agent-src/rules/laravel-routing.md +1 -2
- package/dist/agent-src/rules/laravel-translations.md +0 -1
- package/dist/agent-src/rules/legal-safety-floor.md +3 -3
- package/dist/agent-src/rules/lethal-trifecta-guard.md +1 -1
- package/dist/agent-src/rules/linked-projects-onboarding-gate.md +0 -1
- package/dist/agent-src/rules/low-impact-corpus-privacy-floor.md +1 -1
- package/dist/agent-src/rules/markdown-safe-codeblocks.md +0 -1
- package/dist/agent-src/rules/media-governance-routing.md +13 -17
- package/dist/agent-src/rules/media-sync-ground-truth.md +0 -1
- package/dist/agent-src/rules/minimal-safe-diff.md +0 -1
- package/dist/agent-src/rules/missing-skill-recovery.md +0 -1
- package/dist/agent-src/rules/missing-tool-handling.md +0 -1
- package/dist/agent-src/rules/model-recommendation.md +0 -1
- package/dist/agent-src/rules/neighbour-precedence.md +64 -0
- package/dist/agent-src/rules/no-attribution-footers.md +0 -1
- package/dist/agent-src/rules/no-decorative-emojis-in-git-surfaces.md +0 -1
- package/dist/agent-src/rules/no-pr-progress-comments.md +0 -1
- package/dist/agent-src/rules/no-roadmap-references.md +0 -1
- package/dist/agent-src/rules/notes-first-reasoning.md +0 -1
- package/dist/agent-src/rules/onboarding-gate.md +0 -1
- package/dist/agent-src/rules/output-discipline.md +0 -1
- package/dist/agent-src/rules/package-ci-checks.md +0 -1
- package/dist/agent-src/rules/persona-governance.md +4 -5
- package/dist/agent-src/rules/php-coding.md +0 -1
- package/dist/agent-src/rules/prefer-enums-over-literals.md +0 -1
- package/dist/agent-src/rules/preservation-guard.md +0 -1
- package/dist/agent-src/rules/provider-lifecycle-discipline.md +3 -4
- package/dist/agent-src/rules/question-not-instruction.md +1 -1
- package/dist/agent-src/rules/reviewer-awareness.md +0 -1
- package/dist/agent-src/rules/roadmap-ci-steps-policy.md +0 -1
- package/dist/agent-src/rules/roadmap-progress-sync.md +4 -7
- package/dist/agent-src/rules/role-mode-adherence.md +0 -1
- package/dist/agent-src/rules/rule-type-governance.md +0 -1
- package/dist/agent-src/rules/runtime-safety.md +1 -1
- package/dist/agent-src/rules/scale-discipline.md +0 -1
- package/dist/agent-src/rules/secret-vcs-guard.md +1 -1
- package/dist/agent-src/rules/security-sensitive-stop.md +3 -3
- package/dist/agent-src/rules/self-repair-loop.md +0 -1
- package/dist/agent-src/rules/senior-engineering-discipline.md +0 -1
- package/dist/agent-src/rules/session-canary.md +7 -62
- package/dist/agent-src/rules/settings-ask-protocol.md +6 -6
- package/dist/agent-src/rules/size-enforcement.md +0 -1
- package/dist/agent-src/rules/skill-improvement-trigger.md +2 -3
- package/dist/agent-src/rules/skill-quality.md +0 -1
- package/dist/agent-src/rules/slash-command-routing-policy.md +0 -1
- package/dist/agent-src/rules/source-confidentiality.md +1 -1
- package/dist/agent-src/rules/source-discovery-gate.md +0 -1
- package/dist/agent-src/rules/source-of-truth.md +0 -1
- package/dist/agent-src/rules/spreadsheet-source-quality.md +1 -2
- package/dist/agent-src/rules/strategy-safety-floor.md +1 -1
- package/dist/agent-src/rules/symfony-routing.md +1 -2
- package/dist/agent-src/rules/think-before-action.md +0 -1
- package/dist/agent-src/rules/token-budget-discipline.md +12 -46
- package/dist/agent-src/rules/token-efficiency.md +0 -1
- package/dist/agent-src/rules/token-optimizer-maintenance.md +0 -1
- package/dist/agent-src/rules/tool-safety.md +1 -1
- package/dist/agent-src/rules/ui-audit-gate.md +1 -2
- package/dist/agent-src/rules/untrusted-input-defense.md +1 -1
- package/dist/agent-src/rules/upstream-proposal.md +0 -1
- package/dist/agent-src/rules/user-interaction.md +0 -1
- package/dist/agent-src/rules/user-interrupt-priority.md +1 -1
- package/dist/agent-src/scripts/archive_completed_roadmaps.ts +115 -20
- package/dist/agent-src/scripts/deferral_carry.ts +339 -0
- package/dist/agent-src/scripts/gate_budget.ts +47 -23
- package/dist/agent-src/scripts/gate_execute.ts +12 -4
- package/dist/agent-src/scripts/update_roadmap_progress.ts +4 -1
- package/dist/agent-src/skills/ai-council/SKILL.md +7 -4
- package/dist/agent-src/skills/ai-council/references/cost-and-redaction.md +21 -11
- package/dist/agent-src/skills/ai-council/references/procedure.md +12 -6
- package/dist/agent-src/skills/analysis-autonomous-mode/SKILL.md +1 -1
- package/dist/agent-src/skills/api-design/data/manifest.json +2 -2
- package/dist/agent-src/skills/api-testing/SKILL.md +17 -0
- package/dist/agent-src/skills/bug-analyzer/SKILL.md +1 -1
- package/dist/agent-src/skills/code-intelligence/SKILL.md +14 -2
- package/dist/agent-src/skills/command-routing/SKILL.md +3 -4
- package/dist/agent-src/skills/conventional-commits-writing/SKILL.md +127 -139
- package/dist/agent-src/skills/corpus-grounding/ATTRIBUTION.md +39 -0
- package/dist/agent-src/skills/corpus-grounding/SKILL.md +8 -2
- package/dist/agent-src/skills/database/data/manifest.json +2 -2
- package/dist/agent-src/skills/database/data/query-tuning.csv +13 -13
- package/dist/agent-src/skills/design-system-capture/SKILL.md +0 -3
- package/dist/agent-src/skills/design-system-capture/references/import-procedure.md +1 -1
- package/dist/agent-src/skills/estimate-ticket/SKILL.md +1 -1
- package/dist/agent-src/skills/existing-ui-audit/SKILL.md +2 -2
- package/dist/agent-src/skills/fe-design/SKILL.md +1 -1
- package/dist/agent-src/skills/feature-planning/SKILL.md +1 -1
- package/dist/agent-src/skills/git-workflow/SKILL.md +44 -44
- package/dist/agent-src/skills/git-workflow/references/branch-update.md +312 -0
- package/dist/agent-src/skills/git-workflow/references/commit-subject.md +205 -0
- package/dist/agent-src/skills/iconography/SKILL.md +1 -1
- package/dist/agent-src/skills/jira-integration/SKILL.md +3 -3
- package/dist/agent-src/skills/js-library-packaging/SKILL.md +2 -2
- package/dist/agent-src/skills/merge-conflicts/SKILL.md +10 -4
- package/dist/agent-src/skills/monorepo-workspace/SKILL.md +1 -1
- package/dist/agent-src/skills/po-discovery/SKILL.md +1 -1
- package/dist/agent-src/skills/quality-tools/SKILL.md +11 -1
- package/dist/agent-src/skills/quality-tools/references/go-tools.md +76 -0
- package/dist/agent-src/skills/quality-tools/references/python-tools.md +104 -0
- package/dist/agent-src/skills/refine-ticket/SKILL.md +4 -4
- package/dist/agent-src/skills/review-routing/SKILL.md +2 -2
- package/dist/agent-src/skills/roadmap-writing/SKILL.md +2 -2
- package/dist/agent-src/skills/skill-improvement-pipeline/SKILL.md +5 -3
- package/dist/agent-src/skills/source-discovery/SKILL.md +1 -1
- package/dist/agent-src/skills/storybook-workshop/SKILL.md +3 -3
- package/dist/agent-src/skills/tailwind-engineer/SKILL.md +4 -2
- package/dist/agent-src/skills/test-case-discovery/SKILL.md +7 -0
- package/dist/agent-src/skills/test-driven-development/SKILL.md +17 -0
- package/dist/agent-src/skills/test-performance/SKILL.md +19 -1
- package/dist/agent-src/skills/testing-anti-patterns/SKILL.md +1 -1
- package/dist/agent-src/skills/ui-component-architect/SKILL.md +2 -2
- package/dist/agent-src/skills/using-git-worktrees/SKILL.md +1 -1
- package/dist/agent-src/skills/verify-repair-loop/SKILL.md +2 -2
- package/dist/agent-src/templates/AGENTS.md +1 -1
- package/dist/agent-src/templates/agent-settings.md +46 -129
- package/dist/agent-src/templates/agents/agent-project-settings.example.yml +3 -3
- package/dist/agent-src/templates/command.md +2 -2
- package/dist/agent-src/templates/roadmaps.md +6 -1
- package/dist/agent-src/templates/scripts/work_engine/_lib/agent_settings.ts +0 -6
- package/dist/agent-src/templates/scripts/work_engine/directives/backend/_self_fix.ts +11 -7
- package/dist/agent-src/templates/scripts/work_engine/directives/ui/apply.ts +48 -6
- package/dist/agent-src/templates/scripts/work_engine/hooks/builtin/decision_trace.ts +3 -2
- package/dist/agent-src/templates/scripts/work_engine/hooks/builtin/memory_visibility.ts +2 -1
- package/dist/agent-src/templates/scripts/work_engine/hooks/settings.ts +0 -6
- package/dist/agent-src/templates/scripts/work_engine/scoring/decision_engine.ts +9 -12
- package/dist/agent-src/templates/scripts/work_engine/scoring/memory_visibility.ts +2 -1
- package/dist/agent-src/templates/scripts/work_engine/stack/runner.ts +774 -109
- package/dist/agent-src/templates/skill.md +1 -1
- package/dist/cli/registry.js +2 -1
- package/dist/cli-delegate/archive_completed_roadmaps.js +92 -12
- package/dist/cli-delegate/check_update_banner.js +207 -120
- package/dist/cli-delegate/chunk-523HCFYO.js +90 -0
- package/dist/cli-delegate/{chunk-FP36SWJD.js → chunk-5GPULQBE.js} +2797 -338
- package/dist/cli-delegate/{chunk-IGD6ERX6.js → chunk-6SRJTYJS.js} +42 -176
- package/dist/cli-delegate/chunk-6V5FMA3P.js +186 -0
- package/dist/cli-delegate/{chunk-M2DPGFZ4.js → chunk-6V6TA25H.js} +22 -152
- package/dist/cli-delegate/chunk-6YQC2HLF.js +415 -0
- package/dist/cli-delegate/{chunk-E4XIZDIB.js → chunk-7HKRE54K.js} +1 -64
- package/dist/cli-delegate/chunk-7IQE3D5S.js +191 -0
- package/dist/cli-delegate/{chunk-YJ72UIAK.js → chunk-AED2VQGM.js} +1245 -249
- package/dist/cli-delegate/{chunk-7HQ52DHU.js → chunk-AL3RZ22D.js} +1 -1
- package/dist/cli-delegate/chunk-DEIV7XP7.js +108 -0
- package/dist/cli-delegate/{chunk-DBH4AHDK.js → chunk-ED7KCMLB.js} +9 -11
- package/dist/cli-delegate/chunk-F3QDG2GT.js +2569 -0
- package/dist/cli-delegate/{chunk-6KFEC3G7.js → chunk-G5VVIZRC.js} +3 -3
- package/dist/cli-delegate/{chunk-FC75PGJE.js → chunk-H76PHZBI.js} +149 -61
- package/dist/cli-delegate/chunk-IAYTIIQW.js +67 -0
- package/dist/cli-delegate/chunk-JQOM7TTM.js +1201 -0
- package/dist/cli-delegate/chunk-KB5FZ655.js +208 -0
- package/dist/cli-delegate/{chunk-7RQP4UDH.js → chunk-KR6EM23A.js} +235 -1455
- package/dist/cli-delegate/{chunk-LTYVIY76.js → chunk-KXXWJTDO.js} +6 -11
- package/dist/cli-delegate/{chunk-T7L2QK3K.js → chunk-LQOCRFWT.js} +1 -1
- package/dist/cli-delegate/{chunk-6KM6ORRY.js → chunk-MDPPKQCQ.js} +58 -9
- package/dist/cli-delegate/{chunk-4XFP2Y3E.js → chunk-MSUL5NYP.js} +5 -5
- package/dist/cli-delegate/chunk-OAFSJBHH.js +272 -0
- package/dist/cli-delegate/{chunk-4E2W63DA.js → chunk-OKJUIAPS.js} +62 -1
- package/dist/cli-delegate/{chunk-QAJS5YWY.js → chunk-PHD7OOLE.js} +2 -1
- package/dist/cli-delegate/{chunk-P6ZHP5W2.js → chunk-PWX7CKMD.js} +4 -4
- package/dist/cli-delegate/{chunk-NDJLNC6Q.js → chunk-T6BP34WI.js} +1 -0
- package/dist/cli-delegate/{chunk-7XGBX6ZH.js → chunk-T7XSJSUQ.js} +1144 -589
- package/dist/cli-delegate/chunk-ULQRUR23.js +208 -0
- package/dist/cli-delegate/chunk-WQWXGIWB.js +1779 -0
- package/dist/cli-delegate/{chunk-RGNIWTEA.js → chunk-XQTPXKFA.js} +1 -1
- package/dist/cli-delegate/{chunk-NUHLI2RX.js → chunk-YCU3TIXH.js} +2 -0
- package/dist/cli-delegate/chunk-ZBZV2G5R.js +210 -0
- package/dist/cli-delegate/{chunk-WGXTVKCQ.js → chunk-ZUZKZYCN.js} +5 -3
- package/dist/cli-delegate/cmd_analyze_session.js +223 -43
- package/dist/cli-delegate/cmd_brand_status.js +3 -2
- package/dist/cli-delegate/cmd_conformance.js +1252 -249
- package/dist/cli-delegate/cmd_converge.js +3 -3
- package/dist/cli-delegate/cmd_doctor.js +19 -9
- package/dist/cli-delegate/cmd_doctor_neighbours.js +30 -0
- package/dist/cli-delegate/cmd_explain.js +45 -21
- package/dist/cli-delegate/cmd_export.js +13 -10
- package/dist/cli-delegate/cmd_fleet.js +3 -3
- package/dist/cli-delegate/cmd_git_convention.js +1851 -0
- package/dist/cli-delegate/cmd_handoff.js +44 -22
- package/dist/cli-delegate/cmd_mcp_available.js +3 -2
- package/dist/cli-delegate/cmd_memory_get.js +3 -1
- package/dist/cli-delegate/cmd_migrate.js +3 -3
- package/dist/cli-delegate/cmd_packs_active.js +5 -4
- package/dist/cli-delegate/cmd_preflight.js +6 -6
- package/dist/cli-delegate/cmd_prune.js +14 -11
- package/dist/cli-delegate/cmd_refresh.js +2 -1
- package/dist/cli-delegate/cmd_route_audit.js +10 -8
- package/dist/cli-delegate/cmd_route_explain.js +4 -4
- package/dist/cli-delegate/cmd_session_recycle.js +10 -7
- package/dist/cli-delegate/cmd_settings_check.js +54 -7
- package/dist/cli-delegate/cmd_settings_get.js +18 -8
- package/dist/cli-delegate/cmd_settings_migrate.js +13 -10
- package/dist/cli-delegate/cmd_settings_set.js +4 -4
- package/dist/cli-delegate/cmd_sync.js +14 -11
- package/dist/cli-delegate/cmd_uninstall.js +15 -12
- package/dist/cli-delegate/cmd_update.js +7 -7
- package/dist/cli-delegate/cmd_upgrade.js +9 -8
- package/dist/cli-delegate/cmd_validate.js +15 -12
- package/dist/cli-delegate/cmd_versions.js +5 -5
- package/dist/cli-delegate/deferral_carry.js +16 -0
- package/dist/cli-delegate/gate_budget.js +1 -1
- package/dist/cli-delegate/gate_execute.js +3 -3
- package/dist/cli-delegate/roadmap_gates.js +6 -6
- package/dist/cli-delegate/update_roadmap_progress.js +1 -1
- package/dist/discovery/deprecation-report.md +1 -1
- package/dist/discovery/discovery-manifest.json +232 -141
- package/dist/discovery/discovery-manifest.json.sha256 +1 -1
- package/dist/discovery/discovery-manifest.summary.md +3 -3
- package/dist/discovery/dormancy-report.md +1 -1
- package/dist/discovery/orphan-report.md +1 -1
- package/dist/discovery/packs.json +26 -9
- package/dist/discovery/trust-report.md +3 -3
- package/dist/discovery/workspaces.json +7 -4
- package/dist/hooks/dispatch.js +5655 -6623
- package/dist/hooks/dispatch.sha256 +1 -1
- package/dist/install/claudePathsPlan.js +47 -6
- package/dist/install/claudeRuleRewrite.js +7 -0
- package/dist/install/emit_host_rules_cli.js +39 -36
- package/dist/install/globalRuleScope.js +87 -0
- package/dist/install/host_rule_ownership.js +160 -0
- package/dist/install/install.mjs +2336 -1249
- package/dist/install/installThinLayer.js +289 -0
- package/dist/install/installedRuleLinks.js +262 -0
- package/dist/install/wizard-plan.js +24 -0
- package/dist/mcp/awesome-mcp-servers.row.md +1 -1
- package/dist/mcp/mcp-cloudflare-catalogue.json +2 -2
- package/dist/mcp/registry-manifest.json +5 -5
- package/dist/mcp/server.json +2 -2
- package/dist/mcp/server.mjs +36504 -30986
- package/dist/router.json +1 -1
- package/dist/scripts/_lib/agent_settings.js +26 -120
- package/dist/scripts/_lib/claude_settings_hooks.js +10 -8
- package/dist/scripts/_lib/compile_time_toggles.js +12 -21
- package/dist/scripts/_lib/git_convention.js +232 -0
- package/dist/scripts/_lib/global_deploy_inventory.js +730 -0
- package/dist/scripts/_lib/installed_layer.js +704 -0
- package/dist/scripts/_lib/lean_projection_mode.js +218 -15
- package/dist/scripts/_lib/package_root.js +68 -0
- package/dist/scripts/_lib/rule_consequence_class.js +45 -0
- package/dist/scripts/_lib/rule_injection.js +67 -10
- package/dist/scripts/_lib/rule_law_section.js +122 -0
- package/dist/scripts/_lib/settings_removed_keys.js +168 -0
- package/dist/scripts/_lib/thin_rules.js +535 -0
- package/dist/scripts/_lib/windsurf_render.js +60 -0
- package/dist/scripts/condense.js +14 -26
- package/dist/scripts/project_thin_rules.js +14 -294
- package/dist/server/app.js +2 -1
- package/dist/server/gitKeysGate.js +105 -0
- package/dist/server/routes/settings.js +67 -8
- package/dist/server/routes/wizard.js +4 -0
- package/dist/server/schemas/settings.js +29 -81
- package/dist/shared/skillRanking.js +103 -16
- package/dist/ui/assets/{index-Cwq4d0hX.js → index-C3djEJah.js} +2 -2
- package/dist/ui/index.html +1 -1
- package/docs/MIGRATION.md +72 -1
- package/docs/contracts/settings-classes.md +39 -67
- package/docs/guidelines/agent-infra/active-remediation-mechanics.md +39 -0
- package/docs/guidelines/agent-infra/context-hygiene-mechanics.md +1 -1
- package/docs/guidelines/agent-infra/cross-source-consistency-mechanics.md +8 -10
- package/docs/guidelines/agent-infra/frontier-reasoning-operating-profile.md +3 -2
- package/docs/guidelines/agent-infra/layered-settings.md +13 -1
- package/docs/guidelines/agent-infra/roadmap-progress-mechanics.md +1 -1
- package/docs/guidelines/agent-infra/self-improvement-pipeline.md +10 -3
- package/docs/guidelines/agent-infra/size-and-scope.md +36 -0
- package/docs/guidelines/php/git.md +15 -2
- package/package.json +1 -1
- package/src/agent-src/scripts/archive_completed_roadmaps.ts +115 -20
- package/src/agent-src/scripts/deferral_carry.ts +339 -0
- package/src/agent-src/scripts/gate_budget.ts +47 -23
- package/src/agent-src/scripts/gate_execute.ts +12 -4
- package/src/agent-src/scripts/update_roadmap_progress.ts +4 -1
- package/src/agent-src/templates/AGENTS.md +1 -1
- package/src/agent-src/templates/scripts/work_engine/_lib/agent_settings.ts +0 -6
- package/src/agent-src/templates/scripts/work_engine/directives/backend/_self_fix.ts +11 -7
- package/src/agent-src/templates/scripts/work_engine/directives/ui/apply.ts +48 -6
- package/src/agent-src/templates/scripts/work_engine/hooks/builtin/decision_trace.ts +3 -2
- package/src/agent-src/templates/scripts/work_engine/hooks/builtin/memory_visibility.ts +2 -1
- package/src/agent-src/templates/scripts/work_engine/hooks/settings.ts +0 -6
- package/src/agent-src/templates/scripts/work_engine/scoring/decision_engine.ts +9 -12
- package/src/agent-src/templates/scripts/work_engine/scoring/memory_visibility.ts +2 -1
- package/src/agent-src/templates/scripts/work_engine/stack/runner.ts +774 -109
- package/src/config/agent-settings.template.yml +141 -334
- package/src/config/agents-paths.yml +16 -0
- package/src/config/assurance-capability-registry.json +7 -3
- package/src/config/ci-local-parity.yml +3 -0
- package/src/config/continuity-surface.json +10 -3
- package/src/config/discovery/packs.yml +17 -1
- package/src/config/evaluator-budgets.json +23 -10
- package/src/config/gate-coverage.yml +138 -3
- package/src/config/gate-violation-baselines.json +64 -16
- package/src/config/gitignore-block.txt +19 -0
- package/src/config/hook-bundle-budget.json +25 -0
- package/src/config/hook-token-budget.json +6 -1
- package/src/config/host-instruction-limits.json +33 -0
- package/src/config/release-gate-locality.yml +2 -1
- package/src/config/routing-coverage-seed.json +67 -4
- package/src/config/rule-activation-census.json +7 -3
- package/src/config/rule-consequence-class.json +161 -0
- package/src/config/rule-law-ceilings.json +105 -0
- package/src/config/rule-obligations.json +833 -0
- package/src/install/claudePathsPlan.ts +46 -4
- package/src/install/claudeRuleRewrite.ts +10 -1
- package/src/install/emit_host_rules_cli.ts +43 -34
- package/src/install/globalRuleScope.ts +109 -0
- package/src/install/host_rule_ownership.ts +170 -0
- package/src/install/installThinLayer.ts +378 -0
- package/src/install/installedRuleLinks.ts +308 -0
- package/src/install/wizard-plan.ts +24 -0
- package/src/scripts/_cli/cmd_analyze_session.ts +9 -15
- package/src/scripts/_cli/cmd_conformance.ts +13 -2
- package/src/scripts/_cli/cmd_doctor.ts +33 -81
- package/src/scripts/_cli/cmd_doctor_neighbours.ts +426 -0
- package/src/scripts/_cli/cmd_explain.ts +72 -14
- package/src/scripts/_cli/cmd_git_convention.ts +637 -0
- package/src/scripts/_cli/cmd_settings_check.ts +66 -8
- package/src/scripts/_cli/cmd_settings_get.ts +18 -3
- package/src/scripts/_cli/doctor_execution.ts +72 -1
- package/src/scripts/_dispatch.bash +53 -4
- package/src/scripts/_lib/agent_settings.ts +26 -124
- package/src/scripts/_lib/blocked_by_marker.ts +1 -1
- package/src/scripts/_lib/claude_settings_hooks.ts +10 -8
- package/src/scripts/_lib/compile_time_toggles.ts +12 -21
- package/src/scripts/_lib/dispatch_import_closure.ts +163 -0
- package/src/scripts/_lib/estate_offsets.ts +241 -0
- package/src/scripts/_lib/exemption_shape.ts +143 -0
- package/src/scripts/_lib/fnv.ts +23 -0
- package/src/scripts/_lib/foreign_scan.ts +106 -0
- package/src/scripts/_lib/git_base_ref.ts +92 -0
- package/src/scripts/_lib/git_convention.ts +291 -0
- package/src/scripts/_lib/git_convention_carrier.ts +389 -0
- package/src/scripts/_lib/git_convention_grammar.ts +246 -0
- package/src/scripts/_lib/git_convention_measure.ts +344 -0
- package/src/scripts/_lib/gitignore_block.ts +314 -0
- package/src/scripts/_lib/global_deploy_inventory.ts +106 -0
- package/src/scripts/_lib/graph_feeder_record.ts +437 -0
- package/src/scripts/_lib/hook_settings.ts +73 -0
- package/src/scripts/_lib/host_capability.ts +40 -19
- package/src/scripts/_lib/host_hook_merge.ts +165 -0
- package/src/scripts/_lib/installed_layer.ts +963 -0
- package/src/scripts/_lib/json_merge.ts +82 -0
- package/src/scripts/_lib/json_pointers.ts +93 -11
- package/src/scripts/_lib/knowledge_global.ts +14 -1
- package/src/scripts/_lib/lean_projection_mode.ts +275 -15
- package/src/scripts/_lib/machine_wake.ts +16 -3
- package/src/scripts/_lib/module_reach.ts +456 -0
- package/src/scripts/_lib/neighbour_census.ts +760 -0
- package/src/scripts/_lib/neighbour_contradictions.ts +236 -0
- package/src/scripts/_lib/neighbour_scan.ts +122 -0
- package/src/scripts/_lib/neighbour_tool_use.ts +187 -0
- package/src/scripts/_lib/prompt_shape.ts +46 -0
- package/src/scripts/_lib/ratification_artifact.ts +220 -1
- package/src/scripts/_lib/ratification_subject.ts +92 -0
- package/src/scripts/_lib/release_findings_ingest.ts +15 -7
- package/src/scripts/_lib/release_highlights.ts +5 -0
- package/src/scripts/_lib/release_migration_gate.ts +19 -3
- package/src/scripts/_lib/reserved_name_sweep.ts +73 -0
- package/src/scripts/_lib/review_coverage.ts +134 -0
- package/src/scripts/_lib/rule_consequence_class.ts +71 -0
- package/src/scripts/_lib/rule_injection.ts +130 -10
- package/src/scripts/_lib/rule_law_section.ts +147 -0
- package/src/scripts/_lib/rule_layer_overlap.ts +58 -0
- package/src/scripts/_lib/rule_obligations.ts +205 -0
- package/src/scripts/_lib/self_repair.ts +1 -1
- package/src/scripts/_lib/settings_removed_keys.ts +173 -0
- package/src/scripts/_lib/skill_origin.ts +254 -0
- package/src/scripts/_lib/structured_ask.ts +64 -21
- package/src/scripts/_lib/test_red_state.ts +5 -0
- package/src/scripts/_lib/thin_rules.ts +620 -0
- package/src/scripts/_lib/touched_file_quality.ts +570 -0
- package/src/scripts/_lib/turn_end_refusals.ts +77 -2
- package/src/scripts/_lib/windsurf_render.ts +63 -0
- package/src/scripts/_lib/worker_budget.ts +1 -1
- package/src/scripts/adr/regenerate_index.ts +16 -1
- package/src/scripts/ai_council/cli_call_budget.ts +13 -5
- package/src/scripts/ai_council/clients.ts +26 -57
- package/src/scripts/ai_council/config.ts +8 -8
- package/src/scripts/ai_council/modification_review.ts +100 -0
- package/src/scripts/ai_council/modification_review_questions.ts +90 -0
- package/src/scripts/ai_council/orchestrator.ts +5 -5
- package/src/scripts/ai_council/spend_gate.ts +18 -7
- package/src/scripts/ai_council/vendor_defaults.ts +45 -0
- package/src/scripts/ai_team/config.ts +11 -16
- package/src/scripts/audit_skill_overlap.ts +261 -0
- package/src/scripts/audit_user_type_axis.ts +20 -8
- package/src/scripts/before_complete_hook.ts +243 -12
- package/src/scripts/bench_ab_clone.ts +1 -2
- package/src/scripts/bench_concern_spawn_path.ts +129 -0
- package/src/scripts/bench_graph_feeder_latency.ts +584 -0
- package/src/scripts/bench_touched_file_quality.ts +238 -0
- package/src/scripts/check_agent_artifact_location.ts +36 -0
- package/src/scripts/check_branch_freshness.ts +147 -24
- package/src/scripts/check_enforcement_coverage.ts +130 -0
- package/src/scripts/check_enforcement_matrix.ts +377 -20
- package/src/scripts/check_estate_count.ts +130 -153
- package/src/scripts/check_evaluator_budgets.ts +4 -0
- package/src/scripts/check_finding_dispositions.ts +196 -21
- package/src/scripts/check_gate_coverage.ts +4 -3
- package/src/scripts/check_gate_reachability.ts +48 -1
- package/src/scripts/check_generator_sync.ts +48 -0
- package/src/scripts/check_hook_bundle_composition.ts +477 -0
- package/src/scripts/check_kernel_edit_ratified.ts +132 -14
- package/src/scripts/check_memory.ts +7 -1
- package/src/scripts/check_pack_size.ts +1 -1
- package/src/scripts/check_release_highlights.ts +10 -0
- package/src/scripts/check_release_manifest_content.ts +189 -0
- package/src/scripts/check_release_pr_shape.ts +38 -15
- package/src/scripts/check_standing_rule_delivery.ts +129 -13
- package/src/scripts/check_suppression_hygiene.ts +2 -5
- package/src/scripts/check_verify_expectation_delta.ts +521 -0
- package/src/scripts/ci_settle.ts +42 -5
- package/src/scripts/classify_merge_risk.ts +320 -0
- package/src/scripts/closure_scan.ts +91 -1
- package/src/scripts/code_graph/detect.ts +127 -7
- package/src/scripts/code_graph/foreign.ts +194 -0
- package/src/scripts/code_graph/query.ts +217 -4
- package/src/scripts/code_graph/types.ts +26 -2
- package/src/scripts/code_graph/validate.ts +3 -1
- package/src/scripts/code_graph/verbs.ts +103 -0
- package/src/scripts/command_suggester/settings.ts +8 -108
- package/src/scripts/command_suggester/types.ts +4 -5
- package/src/scripts/compile_hook_manifest.ts +33 -1
- package/src/scripts/condense.ts +14 -27
- package/src/scripts/consumer_matrix.ts +102 -0
- package/src/scripts/context_hygiene_hook.ts +13 -23
- package/src/scripts/cost/preflight.mjs +12 -1
- package/src/scripts/council_cli.ts +7 -7
- package/src/scripts/dispatch_r2_reviewer.ts +24 -11
- package/src/scripts/estate_exemption_shape.ts +202 -0
- package/src/scripts/explain_run.ts +8 -13
- package/src/scripts/generate_git_convention_grammar.ts +127 -0
- package/src/scripts/generate_host_cost_table.ts +21 -4
- package/src/scripts/handoff_context_hook.ts +86 -37
- package/src/scripts/hook_manifest.json +1 -1
- package/src/scripts/hook_manifest.yaml +175 -15
- package/src/scripts/hooks/block_config_weakening.ts +115 -7
- package/src/scripts/hooks/block_plumbing_writes.ts +37 -5
- package/src/scripts/hooks/code_graph_context_hook.ts +252 -23
- package/src/scripts/hooks/concern_failure_policy.ts +280 -0
- package/src/scripts/hooks/concern_registry.ts +2 -0
- package/src/scripts/hooks/delegation_nudge_hook.ts +2 -3
- package/src/scripts/hooks/dispatch_hook.ts +52 -52
- package/src/scripts/hooks/evidence_independence.ts +12 -4
- package/src/scripts/hooks/exit_codes.ts +2 -1
- package/src/scripts/hooks/host_lowering.json +1 -1
- package/src/scripts/hooks/host_lowering.ts +19 -1
- package/src/scripts/hooks/mcp_usage_observation_hook.ts +121 -0
- package/src/scripts/hooks/one_question_per_ask_hook.ts +41 -25
- package/src/scripts/hooks/payload_stub.ts +10 -1
- package/src/scripts/hooks/push_settle_hook.ts +4 -3
- package/src/scripts/hooks/rule_inject_hook.ts +795 -60
- package/src/scripts/hooks/state_io.ts +8 -4
- package/src/scripts/hooks/table_fingerprint.ts +21 -0
- package/src/scripts/hooks/telemetry_usage_hook.ts +24 -7
- package/src/scripts/hooks/turn_end_gate_hook.ts +85 -0
- package/src/scripts/hooks_status.ts +112 -0
- package/src/scripts/injection_scan_hook.ts +7 -0
- package/src/scripts/install-hooks.sh +42 -14
- package/src/scripts/install.ts +78 -215
- package/src/scripts/installed_layer_report.ts +103 -0
- package/src/scripts/language_mirror_hook.ts +15 -6
- package/src/scripts/learning_sidecar.ts +119 -8
- package/src/scripts/lint_blocker_citations.ts +198 -0
- package/src/scripts/lint_evidence_artifacts.ts +9 -0
- package/src/scripts/lint_hook_concern_budget.ts +10 -21
- package/src/scripts/lint_hook_manifest.ts +53 -0
- package/src/scripts/lint_major_migration_sections.ts +156 -26
- package/src/scripts/lint_pack_boundaries.ts +465 -29
- package/src/scripts/lint_roadmap_blockers.ts +282 -29
- package/src/scripts/lint_roadmap_complexity.ts +4 -2
- package/src/scripts/lint_rule_law_section.ts +647 -0
- package/src/scripts/lint_rule_skill_pack_reach.ts +17 -0
- package/src/scripts/lint_settings_classes.ts +1 -1
- package/src/scripts/lint_ticked_unmet_criteria.ts +165 -0
- package/src/scripts/lint_workflow_security.ts +173 -6
- package/src/scripts/mcp_server/consumer_tool_catalog.json +53 -0
- package/src/scripts/mcp_server/graph_tools.ts +72 -3
- package/src/scripts/measure_routing_signal.ts +54 -16
- package/src/scripts/measure_skill_ranker_baseline.ts +221 -8
- package/src/scripts/memory_learn_hook.ts +91 -6
- package/src/scripts/model_rule_injection.ts +55 -8
- package/src/scripts/onboarding_gate_hook.ts +7 -6
- package/src/scripts/print_required_checks.ts +15 -3
- package/src/scripts/probe_host_compliance.ts +33 -5
- package/src/scripts/probe_turnaround.ts +183 -3
- package/src/scripts/project_thin_rules.ts +34 -327
- package/src/scripts/ratification_header.ts +77 -0
- package/src/scripts/refusal_envelope.ts +138 -0
- package/src/scripts/release_publication.ts +29 -3
- package/src/scripts/report_dispatch_import_closure.ts +76 -0
- package/src/scripts/report_evidence_temperature.ts +657 -0
- package/src/scripts/report_installed_rule_links.ts +274 -0
- package/src/scripts/report_label_agreement.ts +233 -0
- package/src/scripts/report_module_reach.ts +212 -0
- package/src/scripts/report_obligation_mechanism.ts +228 -0
- package/src/scripts/report_parked_blockers.ts +222 -0
- package/src/scripts/report_required_checks_drift.ts +196 -0
- package/src/scripts/report_single_token_triggers.ts +277 -0
- package/src/scripts/report_skill_ranker_confusion.ts +543 -0
- package/src/scripts/report_standing_payload_by_host.ts +188 -15
- package/src/scripts/report_verification_record_coverage.ts +306 -0
- package/src/scripts/routing_doctor.ts +2 -2
- package/src/scripts/rule_activation_census.ts +5 -2
- package/src/scripts/rule_trigger_eval.ts +9 -9
- package/src/scripts/schemas/agent-settings.schema.json +17 -31
- package/src/scripts/schemas/command.schema.json +1 -1
- package/src/scripts/schemas/review-findings.schema.json +3 -2
- package/src/scripts/schemas/rule.schema.json +5 -0
- package/src/scripts/schemas/skill.schema.json +1 -1
- package/src/scripts/self_review_gate.ts +101 -9
- package/src/scripts/skill_tools/score_skill_relevance.ts +314 -40
- package/src/scripts/sweep_skill_ranker_signals.ts +129 -0
- package/src/scripts/sync_gitignore.ts +29 -235
- package/src/scripts/sync_pr_branch.ts +403 -132
- package/src/scripts/trigger_eval_grandfather.json +0 -21
- package/src/server/schemas/settings.ts +42 -176
- package/src/shared/skillRanking.ts +169 -14
- package/dist/cli-delegate/chunk-5R7HOLAF.js +0 -99
- package/dist/cli-delegate/chunk-JNMVLWLR.js +0 -4457
- package/dist/cli-delegate/chunk-YDPILCOL.js +0 -152
- package/src/scripts/_lib/config_chain.ts +0 -477
- package/src/scripts/_lib/conformance_report.ts +0 -403
- package/src/scripts/_lib/eval_discrimination.ts +0 -95
- package/src/scripts/_lib/evaluator_promotion.ts +0 -225
- package/src/scripts/_lib/file_slicer.ts +0 -117
- package/src/scripts/_lib/legacy_boundary_map.ts +0 -228
- package/src/scripts/_lib/minimality_tiebreak.ts +0 -168
- package/src/scripts/_lib/overbuild_lens_contract.ts +0 -196
- package/src/scripts/_lib/role_split.ts +0 -338
|
@@ -0,0 +1,704 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a host actually loads, measured in the host's own unit (step 0.1 of
|
|
3
|
+
* `road-to-a-rule-carrier-that-works-outside-the-repo`).
|
|
4
|
+
*
|
|
5
|
+
* THE QUESTION NOTHING IN THIS TREE COULD ANSWER. Every existing figure for the
|
|
6
|
+
* installed layer counts raw bytes of whole files: `censusRuleDir` sums
|
|
7
|
+
* `statSync().size`, `charsAtRoot` does the same for a directory, and
|
|
8
|
+
* `check_preamble_payload_budget` reads both. A host does not load frontmatter
|
|
9
|
+
* as instruction — it is the routing surface, which the router has already
|
|
10
|
+
* read — and it does not render HTML comments, which are authoring scaffolding.
|
|
11
|
+
* So the number a reader wants when they ask "how much instruction is this
|
|
12
|
+
* consumer under" has never been computed here.
|
|
13
|
+
*
|
|
14
|
+
* THE NUMBER IS THEREFORE NOT COMPARABLE WITH ANY PUBLISHED BASELINE, and that
|
|
15
|
+
* is stated here rather than discovered by someone holding the two side by
|
|
16
|
+
* side. `chars` below is strictly smaller than the byte figures in
|
|
17
|
+
* `budgets.yml`, `rule-activation-census.json` or the payload ratchet, and the
|
|
18
|
+
* gap is not a constant — it is whatever that particular corpus spends on
|
|
19
|
+
* frontmatter and comments.
|
|
20
|
+
*
|
|
21
|
+
* ONE LIBRARY, FOUR CONSUMERS. The report CLI is the only one wired in this
|
|
22
|
+
* change (Phase 0 makes no behavior change), and the shape is the reason the
|
|
23
|
+
* other three named in step 0.1 — CI, `doctor`, the upgrade receipt — can use
|
|
24
|
+
* it without a second reader: every input is a PARAMETER. `home`, `projectRoot`
|
|
25
|
+
* and `manifestPath` are supplied by the caller, never read off the process, so
|
|
26
|
+
* a test, a temporary `HOME` and a real machine are the same call with
|
|
27
|
+
* different arguments. That is also what makes the step's "install into a
|
|
28
|
+
* temporary HOME" reachable at all, and it is the fact that refutes
|
|
29
|
+
* `check_standing_rule_delivery`'s header: a user-scope install IS observable
|
|
30
|
+
* from CI, because the installer takes `HOME` from the environment.
|
|
31
|
+
*
|
|
32
|
+
* WHAT `package_owned` AND `foreign` MEAN, because the honest answer is three
|
|
33
|
+
* valued and the report has two columns. `recordedOwnership` distinguishes
|
|
34
|
+
* `recorded-unchanged`, `recorded-modified` and `unknown`, where `unknown` is
|
|
35
|
+
* explicitly "this tree cannot say" rather than "foreign". The report folds the
|
|
36
|
+
* first two into `package_owned` — both are files the manifest claims — and
|
|
37
|
+
* reports the third as `foreign`, which therefore reads "not claimed by this
|
|
38
|
+
* install", never "written by someone else". A machine with no manifest reports
|
|
39
|
+
* every file foreign, which is the correct reading of no evidence and is why
|
|
40
|
+
* {@link InstalledLayerReport.manifest_present} is in the output.
|
|
41
|
+
*/
|
|
42
|
+
import * as fs from 'node:fs';
|
|
43
|
+
import * as os from 'node:os';
|
|
44
|
+
import * as path from 'node:path';
|
|
45
|
+
import { fileURLToPath } from 'node:url';
|
|
46
|
+
import { GLOBAL_RULE_DIRS, PROJECT_RULE_DIRS, globalRuleLayerPath, } from '../../install/globalRuleLayers.js';
|
|
47
|
+
import { NO_RECORDED_HASHES, readRecordedHashes } from '../../install/recordedOwnership.js';
|
|
48
|
+
import { INVENTORY_BASENAME, inventory_path, load_inventory, recorded_absolute_files, } from './global_deploy_inventory.js';
|
|
49
|
+
import { DEFAULT_EVENT4U_ROOT_RELATIVE } from './user_global_paths.js';
|
|
50
|
+
import { ruleBody, splitFrontmatter } from './rule_law_section.js';
|
|
51
|
+
/** How many files the per-directory ranking carries. Step 0.1 fixes it at 20. */
|
|
52
|
+
export const TOP_FILES = 20;
|
|
53
|
+
/**
|
|
54
|
+
* The fraction of a published limit at which the report warns.
|
|
55
|
+
*
|
|
56
|
+
* 0.8 is step 1.4's number, not a tuned one. It is a WARNING threshold and
|
|
57
|
+
* never a gate: crossing it changes what the report says and nothing else, so
|
|
58
|
+
* no install fails here.
|
|
59
|
+
*/
|
|
60
|
+
export const LIMIT_WARN_FRACTION = 0.8;
|
|
61
|
+
/**
|
|
62
|
+
* Whether the host loads this file on every session rather than on a path match.
|
|
63
|
+
*
|
|
64
|
+
* `paths:` at the start of a line IN THE FRONTMATTER is the whole test, and it
|
|
65
|
+
* is the host's own convention rather than this package's: `claudeRuleRewrite`
|
|
66
|
+
* states it from the emitting side — "with it a rule loads on a path match,
|
|
67
|
+
* without it the rule loads unconditionally".
|
|
68
|
+
*
|
|
69
|
+
* SCOPED TO THE FRONTMATTER, which it was not until 2026-10-06. The predicate
|
|
70
|
+
* tested the whole unstripped file while the characters it partitions come
|
|
71
|
+
* from `ruleBody` — predicate and measure read different strings. That was
|
|
72
|
+
* survivable while it only moved a file COUNT; since the when-loaded character
|
|
73
|
+
* split it decides which bucket a whole file's characters land in, and
|
|
74
|
+
* `unconditional_chars` is the figure the ceiling argument is read in. One body
|
|
75
|
+
* line beginning `paths:` — a YAML trigger example quoted inside a kernel or
|
|
76
|
+
* `no_stub` rule, which is ordinary prose — would have moved that rule's entire
|
|
77
|
+
* character count from standing to path-scoped, silently.
|
|
78
|
+
*
|
|
79
|
+
* Measured before the change: of the 105 files on a real opted-in install, two
|
|
80
|
+
* are path-scoped by either reading and none carries a body-only mention, so
|
|
81
|
+
* the fix moves no published figure. It is hardening against a case that does
|
|
82
|
+
* not exist yet rather than a correction of one that does, and it is worth
|
|
83
|
+
* making precisely because the failure would be silent.
|
|
84
|
+
*
|
|
85
|
+
* `check_single_delivery` already slices the frontmatter before this test, so
|
|
86
|
+
* this now AGREES with it. `rule_activation_census.projection_reading` still
|
|
87
|
+
* scans the whole file; that reader counts files rather than characters, so the
|
|
88
|
+
* divergence costs it nothing today, and changing a census's reading belongs to
|
|
89
|
+
* that census rather than here. Named so the next reader finds a known
|
|
90
|
+
* difference instead of discovering an unexplained one.
|
|
91
|
+
*
|
|
92
|
+
* SOURCE-SIDE CLASSIFICATION IS A DIFFERENT QUESTION and deliberately not used.
|
|
93
|
+
* `classify_rule` reads `alwaysApply` and `type: always` to tell `always` from
|
|
94
|
+
* `unconditional`, which matters when you are deciding what to emit. This
|
|
95
|
+
* reads what WAS emitted, where the distinction has already been spent: an
|
|
96
|
+
* Iron-Law rule and an untriggered tier-2 rule both arrive without `paths:` and
|
|
97
|
+
* both load every session, which is the only property the host acts on.
|
|
98
|
+
*/
|
|
99
|
+
export function isUnconditional(text) {
|
|
100
|
+
// THE SAME FUNCTION that produces the characters being partitioned, not the
|
|
101
|
+
// same arithmetic re-implemented. An earlier version scanned for
|
|
102
|
+
// `'\n---'` while `ruleBody` -> `splitFrontmatter` scans for `'\n---\n'`,
|
|
103
|
+
// so the two disagreed about where the frontmatter stops, and the
|
|
104
|
+
// predicate's early stop could hide a later `paths:` key.
|
|
105
|
+
const [frontmatter] = splitFrontmatter(text);
|
|
106
|
+
// AND THE BLIND SPOT THAT SCOPING OPENS, CLOSED HERE. `splitFrontmatter`
|
|
107
|
+
// returns `''` for anything that does not begin exactly `---\n` — a BOM, a
|
|
108
|
+
// leading blank line, CRLF, a block that never closes — and scoping to `''`
|
|
109
|
+
// reads every such file as unconditional even when its frontmatter carries
|
|
110
|
+
// `paths:`. `readLayer` measures every `.md` in a host directory, foreign
|
|
111
|
+
// user-authored rules included, so that would inflate `unconditional_chars`
|
|
112
|
+
// silently, which is the figure the whole ceiling argument is read in.
|
|
113
|
+
//
|
|
114
|
+
// A file that CLAIMS a block `splitFrontmatter` could not parse falls back
|
|
115
|
+
// to the whole-text test. The claim test is the one `installThinLayer`
|
|
116
|
+
// already uses for the same blind spot, and is a regex rather than
|
|
117
|
+
// `startsWith('---')` for the reason stated there.
|
|
118
|
+
//
|
|
119
|
+
// WHICH WAY THE FALLBACK ERRS, SAID PLAINLY RATHER THAN CALLED
|
|
120
|
+
// "CONSERVATIVE". It can only move characters OUT of the standing bucket,
|
|
121
|
+
// never into it. That is conservative about the claim "this rule stands
|
|
122
|
+
// every session" — it never asserts one it cannot support. It is the
|
|
123
|
+
// FLATTERING direction for the ceiling argument, which reads the standing
|
|
124
|
+
// figure against a limit: a misfire here makes the layer look smaller than
|
|
125
|
+
// it is. Both halves are true and the second is the one a reader checking
|
|
126
|
+
// the overage needs, so it is stated rather than left to the word.
|
|
127
|
+
//
|
|
128
|
+
// AND THE STATE THAT IS NOT AN UNPARSEABLE BLOCK. `splitFrontmatter`
|
|
129
|
+
// returns '' for a WELL-FORMED EMPTY block too — `---\n---\n` has no
|
|
130
|
+
// `\n---\n` after index 4, so its own parse fails — and such a file has no
|
|
131
|
+
// `paths:` key by construction. Without the second test it fell to the
|
|
132
|
+
// whole-text scan and a body line beginning `paths:` (a quoted trigger
|
|
133
|
+
// example, which is exactly what a rule about triggers contains) moved the
|
|
134
|
+
// file's entire character count out of the standing figure. Measured
|
|
135
|
+
// before the exclusion: `---\n---\npaths: not a key, just prose\n` read
|
|
136
|
+
// path-scoped.
|
|
137
|
+
const emptyBlock = /^---\n---(\n|$)/.test(text);
|
|
138
|
+
if (frontmatter === '' && !emptyBlock && /^\uFEFF?[\s]*---/.test(text)) {
|
|
139
|
+
return !/^paths:/m.test(text);
|
|
140
|
+
}
|
|
141
|
+
return !/^paths:/m.test(frontmatter);
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Measure one directory.
|
|
145
|
+
*
|
|
146
|
+
* `.md` only, matching `censusRuleDir` and `topRulesByCost`, so the FILE count
|
|
147
|
+
* is comparable with the existing byte censuses even though the character count
|
|
148
|
+
* is not. Cursor's `.mdc` tree is therefore read as zero files, which is the
|
|
149
|
+
* same blind spot every other census in this tree has and is not fixed here —
|
|
150
|
+
* fixing it in one reader and not the others would make two counts that
|
|
151
|
+
* disagree, which is worse than one that is openly partial.
|
|
152
|
+
*/
|
|
153
|
+
export function readLayer(host, scope, dir, recorded) {
|
|
154
|
+
const empty = {
|
|
155
|
+
files: 0,
|
|
156
|
+
unconditional: 0,
|
|
157
|
+
chars: 0,
|
|
158
|
+
package_owned: 0,
|
|
159
|
+
foreign: 0,
|
|
160
|
+
package_owned_chars: 0,
|
|
161
|
+
foreign_chars: 0,
|
|
162
|
+
unconditional_chars: 0,
|
|
163
|
+
scoped_chars: 0,
|
|
164
|
+
top: [],
|
|
165
|
+
};
|
|
166
|
+
let names;
|
|
167
|
+
try {
|
|
168
|
+
names = fs.readdirSync(dir);
|
|
169
|
+
}
|
|
170
|
+
catch {
|
|
171
|
+
return { host, scope, dir, present: false, ...empty };
|
|
172
|
+
}
|
|
173
|
+
const readings = [];
|
|
174
|
+
for (const name of names.sort()) {
|
|
175
|
+
if (!name.endsWith('.md'))
|
|
176
|
+
continue;
|
|
177
|
+
const abs = path.join(dir, name);
|
|
178
|
+
let text;
|
|
179
|
+
try {
|
|
180
|
+
const st = fs.statSync(abs);
|
|
181
|
+
if (!st.isFile())
|
|
182
|
+
continue;
|
|
183
|
+
text = fs.readFileSync(abs, 'utf-8');
|
|
184
|
+
}
|
|
185
|
+
catch {
|
|
186
|
+
continue; // a file that cannot be read is not a file the host loads
|
|
187
|
+
}
|
|
188
|
+
// RECORDED-BUT-MODIFIED IS STILL RECORDED. The report's two columns
|
|
189
|
+
// answer "does this install claim the file"; `classifyOwnership` answers
|
|
190
|
+
// the finer question the upgrade path needs — whether the bytes still
|
|
191
|
+
// match — and folding that in here would give a column three meanings.
|
|
192
|
+
const owned = recorded.has(abs);
|
|
193
|
+
readings.push({
|
|
194
|
+
file: name,
|
|
195
|
+
// THE STRIP IS THE PROJECTOR'S OWN. `ruleBody` is what
|
|
196
|
+
// `project_thin_rules` and the delivery carrier both call, so "what
|
|
197
|
+
// a host loads" and "what a delivery carries" cannot drift into two
|
|
198
|
+
// spellings of the same measurement.
|
|
199
|
+
chars: ruleBody(text).length,
|
|
200
|
+
unconditional: isUnconditional(text),
|
|
201
|
+
package_owned: owned,
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
const top = [...readings]
|
|
205
|
+
// RANKED BY THE SAME MEASURE AS THE TOTAL, which is why
|
|
206
|
+
// `preamble_byte_census.topRulesByCost` is not reused here: it ranks by
|
|
207
|
+
// `statSync().size`, so its list and this total would be in different
|
|
208
|
+
// units and the largest file by one could be absent from the other.
|
|
209
|
+
.sort((a, b) => b.chars - a.chars || a.file.localeCompare(b.file))
|
|
210
|
+
.slice(0, TOP_FILES);
|
|
211
|
+
return {
|
|
212
|
+
host,
|
|
213
|
+
scope,
|
|
214
|
+
dir,
|
|
215
|
+
present: true,
|
|
216
|
+
files: readings.length,
|
|
217
|
+
unconditional: readings.filter((r) => r.unconditional).length,
|
|
218
|
+
chars: readings.reduce((n, r) => n + r.chars, 0),
|
|
219
|
+
package_owned: readings.filter((r) => r.package_owned).length,
|
|
220
|
+
foreign: readings.filter((r) => !r.package_owned).length,
|
|
221
|
+
package_owned_chars: readings.reduce((n, r) => n + (r.package_owned ? r.chars : 0), 0),
|
|
222
|
+
foreign_chars: readings.reduce((n, r) => n + (r.package_owned ? 0 : r.chars), 0),
|
|
223
|
+
unconditional_chars: readings.reduce((n, r) => n + (r.unconditional ? r.chars : 0), 0),
|
|
224
|
+
scoped_chars: readings.reduce((n, r) => n + (r.unconditional ? 0 : r.chars), 0),
|
|
225
|
+
top,
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Where the limits live, derived from this module rather than from `cwd`.
|
|
230
|
+
*
|
|
231
|
+
* The two `..` hops climb from `src/scripts/_lib` to `src/`, which is where
|
|
232
|
+
* `config/` lives — NOT to the package root, though the comment said so until
|
|
233
|
+
* 2026-10-05. The result is correct and the arithmetic stated for it was not,
|
|
234
|
+
* which is the half a future reader would trust when changing the hops.
|
|
235
|
+
*
|
|
236
|
+
* `fileURLToPath`, NOT `new URL(...).pathname` — the claimed parity with
|
|
237
|
+
* `default_template_path()` was only half true until 2026-10-05. A `pathname`
|
|
238
|
+
* percent-encodes, so a package under a directory with a space resolved to
|
|
239
|
+
* `/Users/x/My%20Projects/...`, and on Windows it yields a leading-slash drive
|
|
240
|
+
* path. Either way the read fails, `loadHostInstructionLimits` swallows it and
|
|
241
|
+
* returns an empty map, and EVERY host then reads `unpublished` with `warn`
|
|
242
|
+
* false — the budget measurement degrading to "not measured" with no signal,
|
|
243
|
+
* which is the one failure mode this file's own limit table was written to
|
|
244
|
+
* avoid.
|
|
245
|
+
*/
|
|
246
|
+
export function defaultHostLimitsPath() {
|
|
247
|
+
return path.join(path.dirname(fileURLToPath(import.meta.url)), '..', '..', 'config', 'host-instruction-limits.json');
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* Read the published-limit table.
|
|
251
|
+
*
|
|
252
|
+
* TOLERANT IN ONE DIRECTION ONLY. An unreadable or malformed file yields an
|
|
253
|
+
* EMPTY table, so every host reads `unpublished` and nothing warns — a reading
|
|
254
|
+
* this tree cannot stand behind must never become a warning it can. A present
|
|
255
|
+
* entry whose `limit` is not a positive finite number is treated the same way,
|
|
256
|
+
* because a `0` or a string would otherwise make every install look saturated.
|
|
257
|
+
*/
|
|
258
|
+
export function loadHostInstructionLimits(file = defaultHostLimitsPath()) {
|
|
259
|
+
const out = new Map();
|
|
260
|
+
let raw;
|
|
261
|
+
try {
|
|
262
|
+
raw = JSON.parse(fs.readFileSync(file, 'utf-8'));
|
|
263
|
+
}
|
|
264
|
+
catch {
|
|
265
|
+
return out; // no table ⇒ no limits ⇒ no warnings
|
|
266
|
+
}
|
|
267
|
+
if (typeof raw !== 'object' || raw === null || Array.isArray(raw))
|
|
268
|
+
return out;
|
|
269
|
+
for (const [host, v] of Object.entries(raw)) {
|
|
270
|
+
if (host.startsWith('_'))
|
|
271
|
+
continue; // `_comment` and friends are not hosts
|
|
272
|
+
if (typeof v !== 'object' || v === null || Array.isArray(v))
|
|
273
|
+
continue;
|
|
274
|
+
const e = v;
|
|
275
|
+
const n = e.limit;
|
|
276
|
+
const limit = typeof n === 'number' && Number.isFinite(n) && n > 0 ? n : null;
|
|
277
|
+
const k = e.kind;
|
|
278
|
+
const kind = limit === null
|
|
279
|
+
? 'unpublished'
|
|
280
|
+
: k === 'vendor-published' || k === 'reported'
|
|
281
|
+
? k
|
|
282
|
+
: 'reported'; // a cited figure with no stated kind is the weaker one
|
|
283
|
+
out.set(host, { limit, kind });
|
|
284
|
+
}
|
|
285
|
+
return out;
|
|
286
|
+
}
|
|
287
|
+
/**
|
|
288
|
+
* Fold the per-directory readings into one row per host, against its limit.
|
|
289
|
+
*
|
|
290
|
+
* Exported so a caller holding layers it built itself — the install receipt,
|
|
291
|
+
* which measures before and after — can produce the same rows without
|
|
292
|
+
* re-reading the tree.
|
|
293
|
+
*/
|
|
294
|
+
export function buildHostLimitRows(layers, limits) {
|
|
295
|
+
const byHost = new Map();
|
|
296
|
+
for (const l of layers) {
|
|
297
|
+
if (!l.present)
|
|
298
|
+
continue;
|
|
299
|
+
const acc = byHost.get(l.host) ?? { chars: 0, owned: 0, foreign: 0 };
|
|
300
|
+
acc.chars += l.chars;
|
|
301
|
+
acc.owned += l.package_owned_chars;
|
|
302
|
+
acc.foreign += l.foreign_chars;
|
|
303
|
+
byHost.set(l.host, acc);
|
|
304
|
+
}
|
|
305
|
+
// EVERY host that has a directory gets a row, present or not — a host whose
|
|
306
|
+
// layer is absent reads 0 against its limit, which is a real answer and the
|
|
307
|
+
// one step 3.5 asks for ("one row per host with a published limit").
|
|
308
|
+
for (const l of layers)
|
|
309
|
+
if (!byHost.has(l.host))
|
|
310
|
+
byHost.set(l.host, { chars: 0, owned: 0, foreign: 0 });
|
|
311
|
+
// AND every host the LIMITS table knows, whether or not a layer reached it.
|
|
312
|
+
// Rows were layer-driven while the requirement is limit-driven, so a limit
|
|
313
|
+
// recorded for a host the layer map does not list disappeared silently —
|
|
314
|
+
// the two sources agree today, which means they can only diverge in the
|
|
315
|
+
// direction the code did not cover.
|
|
316
|
+
for (const [host, e] of limits)
|
|
317
|
+
if (e.limit !== null && !byHost.has(host)) {
|
|
318
|
+
byHost.set(host, { chars: 0, owned: 0, foreign: 0 });
|
|
319
|
+
}
|
|
320
|
+
const rows = [];
|
|
321
|
+
for (const host of [...byHost.keys()].sort()) {
|
|
322
|
+
const a = byHost.get(host);
|
|
323
|
+
const e = limits.get(host) ?? { limit: null, kind: 'unpublished' };
|
|
324
|
+
const fraction = e.limit === null ? null : a.chars / e.limit;
|
|
325
|
+
rows.push({
|
|
326
|
+
host,
|
|
327
|
+
limit: e.limit,
|
|
328
|
+
kind: e.kind,
|
|
329
|
+
chars: a.chars,
|
|
330
|
+
package_owned_chars: a.owned,
|
|
331
|
+
foreign_chars: a.foreign,
|
|
332
|
+
fraction,
|
|
333
|
+
warn: fraction !== null && fraction >= LIMIT_WARN_FRACTION,
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
return rows;
|
|
337
|
+
}
|
|
338
|
+
/**
|
|
339
|
+
* Where `deployed-files.json` lives FOR A STATED HOME.
|
|
340
|
+
*
|
|
341
|
+
* `global_deploy_inventory.inventory_path()` answers for the process asking,
|
|
342
|
+
* honouring `AGENT_CONFIG_DEPLOY_INVENTORY` and `EVENT4U_CONFIG_HOME`. That is
|
|
343
|
+
* right for the installer, which is running the install it is recording, and
|
|
344
|
+
* WRONG for a reader measuring some other home: a report about a staged install
|
|
345
|
+
* under a temporary HOME would read the maintainer's own inventory and claim
|
|
346
|
+
* ownership of files that install never wrote.
|
|
347
|
+
*
|
|
348
|
+
* SO AN EXPLICITLY SUPPLIED HOME WINS, AND THE ENV OVERRIDES DECIDE ONLY THE
|
|
349
|
+
* HOME-LESS CASE. A caller that says which home it is measuring has already
|
|
350
|
+
* answered the question those variables would answer for it, and letting the
|
|
351
|
+
* asking process's environment override that is exactly the hazard named above.
|
|
352
|
+
* An intermediate draft of this function had the precedence the other way round
|
|
353
|
+
* while this paragraph said what it says now; the code is what changed.
|
|
354
|
+
*
|
|
355
|
+
* THE CONSEQUENCE, STATED RATHER THAN IMPLIED: `ReportOptions.home` is
|
|
356
|
+
* REQUIRED, so every caller of {@link buildInstalledLayerReport} supplies one
|
|
357
|
+
* and neither env override reaches this reader through that path. That is
|
|
358
|
+
* correct — those variables describe the asking process's own install, which is
|
|
359
|
+
* not what a report about another home is measuring — but it means "honours the
|
|
360
|
+
* env override" is not a true sentence about the report, and saying so here is
|
|
361
|
+
* cheaper than leaving the next reader to discover it.
|
|
362
|
+
*
|
|
363
|
+
* The layout is COMPOSED from the modules that own it rather than re-spelled,
|
|
364
|
+
* so a layout change moves this reader with it instead of leaving it looking in
|
|
365
|
+
* a directory nobody writes.
|
|
366
|
+
*/
|
|
367
|
+
export function defaultInventoryPath(home) {
|
|
368
|
+
// NO HOME STATED — the asking process is the subject, so the env-aware
|
|
369
|
+
// resolver is the right answer.
|
|
370
|
+
if (home === undefined || home === null || home === '')
|
|
371
|
+
return inventory_path();
|
|
372
|
+
// THE STATED HOME IS THE ASKING PROCESS'S OWN. `ReportOptions.home` is
|
|
373
|
+
// required, so `buildInstalledLayerReport` ALWAYS states one — including
|
|
374
|
+
// when it is simply measuring the machine it is running on. Treating that
|
|
375
|
+
// as "a different home" sent it to the hardcoded layout, found nothing
|
|
376
|
+
// under `EVENT4U_CONFIG_HOME` or `AGENT_CONFIG_DEPLOY_INVENTORY`, and
|
|
377
|
+
// printed "NO manifest and NO deploy inventory resolved" — the
|
|
378
|
+
// `0 package-owned` reading step 1.2 exists to remove, and it put the
|
|
379
|
+
// report and the receipt in disagreement about one install.
|
|
380
|
+
//
|
|
381
|
+
// The precedence argument only ever covered a home that is NOT this
|
|
382
|
+
// process's, so that is the case the branch now tests for.
|
|
383
|
+
try {
|
|
384
|
+
// BOTH SPELLINGS, for the reason `recorded_absolute_files` states at
|
|
385
|
+
// length: a lexical compare alone calls a symlinked or alternate
|
|
386
|
+
// spelling of the SAME home "a different home", takes the hardcoded
|
|
387
|
+
// layout, and reproduces the 0 package-owned reading this branch
|
|
388
|
+
// exists to prevent. macOS `/var` -> `/private/var` is the standing
|
|
389
|
+
// example.
|
|
390
|
+
const real = (q) => {
|
|
391
|
+
try {
|
|
392
|
+
return fs.realpathSync(q);
|
|
393
|
+
}
|
|
394
|
+
catch {
|
|
395
|
+
return path.resolve(q);
|
|
396
|
+
}
|
|
397
|
+
};
|
|
398
|
+
const mine = os.homedir();
|
|
399
|
+
if (path.resolve(home) === path.resolve(mine) || real(home) === real(mine)) {
|
|
400
|
+
return inventory_path();
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
catch {
|
|
404
|
+
// `homedir()` can throw on an exotic environment; fall through to the
|
|
405
|
+
// layout, which is the answer for a home that is not ours anyway.
|
|
406
|
+
}
|
|
407
|
+
return path.join(home, DEFAULT_EVENT4U_ROOT_RELATIVE, INVENTORY_BASENAME);
|
|
408
|
+
}
|
|
409
|
+
/**
|
|
410
|
+
* The ONE place that answers "which of these installed files are ours".
|
|
411
|
+
*
|
|
412
|
+
* Two readers ask it — the installed-layer report and the install receipt's
|
|
413
|
+
* budget lines — and until step 1.2 they asked different things. Both called
|
|
414
|
+
* `manifest_path(...)` and read a project-scoped `agents/installed-tools.lock`.
|
|
415
|
+
* A global-only install writes nothing into a project, so on exactly the
|
|
416
|
+
* install shape the thinned-layer roadmap's AC-1 is about, both reported
|
|
417
|
+
* `0 package-owned / N foreign` and AC-1's figure could not be produced at all.
|
|
418
|
+
*
|
|
419
|
+
* The global install DOES record what it wrote: `deployed-files.json` lists,
|
|
420
|
+
* per tool, the anchor and every file the installer maintains there —
|
|
421
|
+
* "the ownership proof that makes reaping safe". This reads it.
|
|
422
|
+
*
|
|
423
|
+
* IT IS A UNION, AND THE PATH SPACES ARE WHY. An earlier shape let the project
|
|
424
|
+
* manifest win OUTRIGHT whenever its file existed, on the reasoning that it
|
|
425
|
+
* carries hashes an inventory does not. That reasoning does not survive the
|
|
426
|
+
* observation that the two sources describe DISJOINT sets of paths: the
|
|
427
|
+
* manifest records project-relative entries resolved against `projectRoot`,
|
|
428
|
+
* the inventory records paths under the user's home. Exclusivity therefore
|
|
429
|
+
* bought nothing and cost everything — any `agents/installed-tools.lock` on
|
|
430
|
+
* disk, a gitignored file a single project-scoped install creates, restored
|
|
431
|
+
* the exact `0 package-owned` reading for the global layer that step 1.2
|
|
432
|
+
* exists to fix, while the report printed "read from the installed-tools
|
|
433
|
+
* manifest" with no evidence behind it for those paths.
|
|
434
|
+
*
|
|
435
|
+
* So every source contributes, and the MANIFEST IS MERGED LAST: on the paths
|
|
436
|
+
* it names its hash overwrites a hashless entry, which preserves the
|
|
437
|
+
* `recorded-unchanged` / `recorded-modified` distinction that motivated the
|
|
438
|
+
* precedence in the first place, without discarding evidence about paths it
|
|
439
|
+
* says nothing about.
|
|
440
|
+
*
|
|
441
|
+
* {@link OwnershipResolution.sources} lists every source that contributed at
|
|
442
|
+
* least one path, because one word cannot describe two path spaces — a report
|
|
443
|
+
* reading global and project layers together may have manifest evidence for
|
|
444
|
+
* one and inventory evidence for the other, and saying only one of them is
|
|
445
|
+
* wrong about half the files it is describing.
|
|
446
|
+
*
|
|
447
|
+
* A FILE THE USER EDITED AND THE INSTALLER PRESERVED STILL COUNTS AS
|
|
448
|
+
* MAINTAINED. The inventory records what the installer MAINTAINS at a path, not
|
|
449
|
+
* what the bytes currently are, so a preserved rule stays package-owned. That
|
|
450
|
+
* is the correct reading — the package is still responsible for the path — and
|
|
451
|
+
* it is why this function returns `null` hashes rather than fabricating one.
|
|
452
|
+
*/
|
|
453
|
+
export function resolveLayerOwnership(opts) {
|
|
454
|
+
const recorded = new Map();
|
|
455
|
+
const sources = [];
|
|
456
|
+
// HASHLESS SOURCES FIRST, so the manifest's hashes overwrite a `null` on
|
|
457
|
+
// any path both happen to name rather than the other way round.
|
|
458
|
+
if (opts.inventoryPath !== null) {
|
|
459
|
+
let n = 0;
|
|
460
|
+
try {
|
|
461
|
+
const inv = load_inventory(opts.inventoryPath ?? defaultInventoryPath(opts.home));
|
|
462
|
+
for (const abs of recorded_absolute_files(inv, opts.home ?? null)) {
|
|
463
|
+
// COUNT PATHS NEWLY CONTRIBUTED, not entries read, so a
|
|
464
|
+
// source that added nothing is not named as evidence.
|
|
465
|
+
//
|
|
466
|
+
// WHAT THIS GUARD DOES NOT DO, since an earlier comment here
|
|
467
|
+
// claimed it did: it does not collapse the two spellings
|
|
468
|
+
// `recorded_absolute_files` emits per file. The lexical and the
|
|
469
|
+
// realpath spelling are distinct Map keys, so wherever they
|
|
470
|
+
// differ — every path under macOS `/var` — each is newly
|
|
471
|
+
// contributed and `n` still rises twice for one file. Harmless
|
|
472
|
+
// only because `n` is read as `> 0` and never as a count; a
|
|
473
|
+
// future reader wanting a count must not take it from here.
|
|
474
|
+
if (!recorded.has(abs))
|
|
475
|
+
n += 1;
|
|
476
|
+
recorded.set(abs, null);
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
catch {
|
|
480
|
+
// Defence in depth, and NOT where the documented behaviour lives.
|
|
481
|
+
// "A corrupt inventory is no evidence, never a failure" is real but
|
|
482
|
+
// is carried one module down: `load_inventory` swallows its own
|
|
483
|
+
// read and parse failures and returns an empty shell, and
|
|
484
|
+
// `recorded_absolute_files` type-guards every field. So this catch
|
|
485
|
+
// is unreachable for that reason and the fixture case named for it
|
|
486
|
+
// exercises `load_inventory`, not this guard. Kept because a future
|
|
487
|
+
// change to either could make it reachable; labelled so it is not
|
|
488
|
+
// mistaken for tested coverage.
|
|
489
|
+
}
|
|
490
|
+
if (n > 0)
|
|
491
|
+
sources.push('global-inventory');
|
|
492
|
+
}
|
|
493
|
+
let fromDeploy = 0;
|
|
494
|
+
for (const [anchor, rels] of opts.thisDeploy ?? new Map()) {
|
|
495
|
+
for (const rel of rels) {
|
|
496
|
+
const abs = path.resolve(anchor, rel);
|
|
497
|
+
// Same rule as the inventory branch: a source is named only when it
|
|
498
|
+
// contributes a path nothing else had. On every install after the
|
|
499
|
+
// first, `thisDeploy`'s paths are already in the inventory, and
|
|
500
|
+
// naming it there printed "the file set this install just wrote"
|
|
501
|
+
// for a source that added nothing.
|
|
502
|
+
if (!recorded.has(abs))
|
|
503
|
+
fromDeploy += 1;
|
|
504
|
+
recorded.set(abs, null);
|
|
505
|
+
// Both spellings, for the reason `recorded_absolute_files` states:
|
|
506
|
+
// the reader compares against a lexically joined path, and a HOME
|
|
507
|
+
// behind a symlink makes the two disagree.
|
|
508
|
+
try {
|
|
509
|
+
recorded.set(fs.realpathSync(abs), null);
|
|
510
|
+
}
|
|
511
|
+
catch {
|
|
512
|
+
// Not on disk — the lexical spelling is all there is.
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
if (fromDeploy > 0)
|
|
517
|
+
sources.push('this-deploy');
|
|
518
|
+
const manifestPath = opts.manifestPath ?? null;
|
|
519
|
+
if (manifestPath !== null && fs.existsSync(manifestPath)) {
|
|
520
|
+
let n = 0;
|
|
521
|
+
// DEGRADE LIKE THE INVENTORY BRANCH, which this function documents
|
|
522
|
+
// twice. The code this replaced wrapped exactly this call, so a corrupt
|
|
523
|
+
// `installed-tools.lock` used to read as "no manifest evidence" and the
|
|
524
|
+
// budget block still printed. Unguarded it threw into the receipt's
|
|
525
|
+
// outer catch and suppressed the WHOLE block — ownership line, per-host
|
|
526
|
+
// rows and the 80 % warning — and out of `buildInstalledLayerReport`,
|
|
527
|
+
// which has no outer catch at all. An unreadable manifest is no
|
|
528
|
+
// evidence, never a failure; the asymmetry was undeclared.
|
|
529
|
+
let recordedHashes;
|
|
530
|
+
try {
|
|
531
|
+
recordedHashes = readRecordedHashes(manifestPath, opts.projectRoot);
|
|
532
|
+
}
|
|
533
|
+
catch {
|
|
534
|
+
recordedHashes = NO_RECORDED_HASHES;
|
|
535
|
+
}
|
|
536
|
+
for (const [abs, hash] of recordedHashes) {
|
|
537
|
+
// The manifest contributes whenever it names a path at all: it
|
|
538
|
+
// supplies the HASH even where another source already had the path,
|
|
539
|
+
// which is evidence the others cannot give.
|
|
540
|
+
n += 1;
|
|
541
|
+
recorded.set(abs, hash);
|
|
542
|
+
}
|
|
543
|
+
if (n > 0)
|
|
544
|
+
sources.push('manifest');
|
|
545
|
+
}
|
|
546
|
+
if (recorded.size === 0) {
|
|
547
|
+
return { recorded: NO_RECORDED_HASHES, sources: [], source: 'none' };
|
|
548
|
+
}
|
|
549
|
+
// `source` keeps a single word for readers that want one. The manifest is
|
|
550
|
+
// the most specific evidence, so it leads when it contributed; otherwise
|
|
551
|
+
// the inventory, otherwise this deploy's own set.
|
|
552
|
+
const primary = sources.includes('manifest')
|
|
553
|
+
? 'manifest'
|
|
554
|
+
: sources.includes('global-inventory')
|
|
555
|
+
? 'global-inventory'
|
|
556
|
+
: 'this-deploy';
|
|
557
|
+
return { recorded, sources, source: primary };
|
|
558
|
+
}
|
|
559
|
+
/**
|
|
560
|
+
* Read every host rule directory in both scopes.
|
|
561
|
+
*
|
|
562
|
+
* BOTH SCOPES, BECAUSE THE HOST UNIONS THEM. A reading of the global layer
|
|
563
|
+
* alone would understate a consumer whose project carries its own rules, and
|
|
564
|
+
* the carrier's own scope filter reads the union for the same reason
|
|
565
|
+
* (`rule_layer_overlap.hostRuleLayerIds`).
|
|
566
|
+
*/
|
|
567
|
+
export function buildInstalledLayerReport(opts) {
|
|
568
|
+
const manifestPath = opts.manifestPath ?? null;
|
|
569
|
+
const { recorded, source: ownership_source, sources: ownership_sources } = resolveLayerOwnership({
|
|
570
|
+
manifestPath,
|
|
571
|
+
projectRoot: opts.projectRoot,
|
|
572
|
+
home: opts.home,
|
|
573
|
+
inventoryPath: opts.inventoryPath,
|
|
574
|
+
});
|
|
575
|
+
const layers = [];
|
|
576
|
+
for (const host of Object.keys(GLOBAL_RULE_DIRS).sort()) {
|
|
577
|
+
const dir = globalRuleLayerPath(host, opts.home);
|
|
578
|
+
if (dir === null)
|
|
579
|
+
continue;
|
|
580
|
+
layers.push(readLayer(host, 'global', dir, recorded));
|
|
581
|
+
}
|
|
582
|
+
for (const rel of Object.keys(PROJECT_RULE_DIRS).sort()) {
|
|
583
|
+
const host = PROJECT_RULE_DIRS[rel];
|
|
584
|
+
layers.push(readLayer(host, 'project', path.join(opts.projectRoot, rel), recorded));
|
|
585
|
+
}
|
|
586
|
+
const totals = layers.reduce((acc, l) => ({
|
|
587
|
+
files: acc.files + l.files,
|
|
588
|
+
unconditional: acc.unconditional + l.unconditional,
|
|
589
|
+
chars: acc.chars + l.chars,
|
|
590
|
+
package_owned: acc.package_owned + l.package_owned,
|
|
591
|
+
foreign: acc.foreign + l.foreign,
|
|
592
|
+
package_owned_chars: acc.package_owned_chars + l.package_owned_chars,
|
|
593
|
+
foreign_chars: acc.foreign_chars + l.foreign_chars,
|
|
594
|
+
unconditional_chars: acc.unconditional_chars + l.unconditional_chars,
|
|
595
|
+
scoped_chars: acc.scoped_chars + l.scoped_chars,
|
|
596
|
+
}), {
|
|
597
|
+
files: 0,
|
|
598
|
+
unconditional: 0,
|
|
599
|
+
chars: 0,
|
|
600
|
+
package_owned: 0,
|
|
601
|
+
foreign: 0,
|
|
602
|
+
package_owned_chars: 0,
|
|
603
|
+
foreign_chars: 0,
|
|
604
|
+
unconditional_chars: 0,
|
|
605
|
+
scoped_chars: 0,
|
|
606
|
+
});
|
|
607
|
+
const limits = buildHostLimitRows(layers, loadHostInstructionLimits(opts.hostLimitsPath ?? defaultHostLimitsPath()));
|
|
608
|
+
return {
|
|
609
|
+
home: opts.home,
|
|
610
|
+
project_root: opts.projectRoot,
|
|
611
|
+
host_version: opts.hostVersion ?? 'unknown',
|
|
612
|
+
manifest_present: manifestPath !== null && fs.existsSync(manifestPath),
|
|
613
|
+
ownership_source,
|
|
614
|
+
ownership_sources,
|
|
615
|
+
layers,
|
|
616
|
+
limits,
|
|
617
|
+
totals,
|
|
618
|
+
};
|
|
619
|
+
}
|
|
620
|
+
/**
|
|
621
|
+
* One line naming WHICH evidence answered ownership.
|
|
622
|
+
*
|
|
623
|
+
* Exported so the report and the install receipt say the same sentence about
|
|
624
|
+
* the same source — a consumer comparing a receipt against a later report must
|
|
625
|
+
* not have to decide whether a wording difference means a different reading.
|
|
626
|
+
*/
|
|
627
|
+
export function ownershipLine(sources) {
|
|
628
|
+
const list = (Array.isArray(sources) ? sources : [sources]).filter((s) => s !== 'none');
|
|
629
|
+
if (list.length === 0) {
|
|
630
|
+
return 'ownership: NO manifest and NO deploy inventory resolved — every file reads foreign, which is no evidence rather than a finding';
|
|
631
|
+
}
|
|
632
|
+
// EVERY contributor is named, never just one. The sources cover disjoint
|
|
633
|
+
// path spaces, so a line naming one of two is a true sentence about half
|
|
634
|
+
// the files and a false one about the rest.
|
|
635
|
+
const phrases = {
|
|
636
|
+
manifest: 'the installed-tools manifest',
|
|
637
|
+
'global-inventory': 'the global deploy inventory (deployed-files.json)',
|
|
638
|
+
'this-deploy': 'the file set this install just wrote',
|
|
639
|
+
};
|
|
640
|
+
const named = list.map((s) => phrases[s]);
|
|
641
|
+
const joined = named.length === 1
|
|
642
|
+
? named[0]
|
|
643
|
+
: `${named.slice(0, -1).join(', ')} and ${named[named.length - 1]}`;
|
|
644
|
+
return `ownership: read from ${joined}`;
|
|
645
|
+
}
|
|
646
|
+
/** The report as lines, for a terminal. One directory per block. */
|
|
647
|
+
export function renderInstalledLayerReport(report) {
|
|
648
|
+
const out = [];
|
|
649
|
+
out.push(`installed layer — host ${report.host_version}, HOME ${report.home}`);
|
|
650
|
+
out.push(`project ${report.project_root}`);
|
|
651
|
+
out.push(ownershipLine(report.ownership_sources));
|
|
652
|
+
for (const l of report.layers) {
|
|
653
|
+
if (!l.present) {
|
|
654
|
+
out.push(` ${l.host} (${l.scope}) — absent: ${l.dir}`);
|
|
655
|
+
continue;
|
|
656
|
+
}
|
|
657
|
+
out.push(` ${l.host} (${l.scope}) — ${String(l.files)} files ` +
|
|
658
|
+
`(${String(l.unconditional)} unconditional), ${String(l.chars)} chars ` +
|
|
659
|
+
`(${String(l.unconditional_chars)} standing + ${String(l.scoped_chars)} path-scoped), ` +
|
|
660
|
+
`${String(l.package_owned)} package-owned / ${String(l.foreign)} foreign`);
|
|
661
|
+
for (const f of l.top) {
|
|
662
|
+
out.push(` ${String(f.chars).padStart(7)} ${f.unconditional ? 'uncond' : 'scoped'} ${f.file}`);
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
const t = report.totals;
|
|
666
|
+
out.push(` TOTAL — ${String(t.files)} files (${String(t.unconditional)} unconditional), ` +
|
|
667
|
+
`${String(t.chars)} chars ` +
|
|
668
|
+
`(${String(t.unconditional_chars)} standing + ${String(t.scoped_chars)} path-scoped), ` +
|
|
669
|
+
`${String(t.package_owned)} package-owned / ${String(t.foreign)} foreign`);
|
|
670
|
+
out.push(...renderHostLimitRows(report.limits));
|
|
671
|
+
return out;
|
|
672
|
+
}
|
|
673
|
+
/**
|
|
674
|
+
* The budget block — one row per host, against its published limit.
|
|
675
|
+
*
|
|
676
|
+
* Extracted so the REPORT and the INSTALL RECEIPT print the same bytes. They
|
|
677
|
+
* answer the same question at two moments, and a consumer comparing a receipt
|
|
678
|
+
* against a later report must not have to decide whether a wording difference
|
|
679
|
+
* means a measurement difference.
|
|
680
|
+
*/
|
|
681
|
+
export function renderHostLimitRows(rows) {
|
|
682
|
+
// ONE ROW PER HOST, against its published limit — steps 1.4 and 3.5. The
|
|
683
|
+
// combined figure is what the row is about: a consumer's own instruction
|
|
684
|
+
// files sit in the same budget as this package's, so a package-owned number
|
|
685
|
+
// alone would understate what the host is actually holding.
|
|
686
|
+
const out = [
|
|
687
|
+
'host instruction budgets — package-owned + foreign against the published limit',
|
|
688
|
+
];
|
|
689
|
+
for (const r of rows) {
|
|
690
|
+
const split = `${String(r.package_owned_chars)} package-owned + ` +
|
|
691
|
+
`${String(r.foreign_chars)} foreign = ${String(r.chars)} chars`;
|
|
692
|
+
if (r.limit === null) {
|
|
693
|
+
out.push(` ${r.host} — ${split}; no published limit recorded — not measured`);
|
|
694
|
+
continue;
|
|
695
|
+
}
|
|
696
|
+
const pct = (r.fraction * 100).toFixed(1);
|
|
697
|
+
out.push(` ${r.host} — ${split}; ${pct}% of ${String(r.limit)} (${r.kind})` +
|
|
698
|
+
(r.warn
|
|
699
|
+
? ` — WARNING: at or past ${String(Math.round(LIMIT_WARN_FRACTION * 100))}% of the published limit`
|
|
700
|
+
: ''));
|
|
701
|
+
}
|
|
702
|
+
return out;
|
|
703
|
+
}
|
|
704
|
+
//# sourceMappingURL=installed_layer.js.map
|