@event4u/agent-config 16.3.0 → 16.5.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 +178 -78
- package/README.md +2 -2
- package/agents/templates/.ai-council.yml.example +26 -8
- package/dist/agent-src/commands/analyze/inbox.md +10 -7
- 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/design.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 +1 -1
- package/dist/agent-src/commands/fix/ci.md +17 -3
- 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 +3 -3
- 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/next.md +10 -1
- package/dist/agent-src/commands/roadmap/process-full.md +52 -38
- package/dist/agent-src/commands/roadmap/resolve-blockers.md +64 -14
- 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/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 +1 -1
- 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 +67 -52
- 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 +4 -10
- 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/design-antipatterns.md +1 -1
- package/dist/agent-src/guidelines/php/git.md +15 -2
- package/dist/agent-src/rules/active-remediation.md +0 -1
- 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 +0 -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 +0 -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 +0 -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 +5 -5
- 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 +2 -3
- package/dist/agent-src/rules/delegation-policy.md +2 -3
- package/dist/agent-src/rules/design-fidelity.md +0 -1
- 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 +0 -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 +0 -1
- package/dist/agent-src/rules/domain-safety-pii.md +0 -1
- package/dist/agent-src/rules/domain-safety-retention.md +0 -1
- package/dist/agent-src/rules/downstream-changes.md +0 -1
- package/dist/agent-src/rules/engineering-safety-floor.md +0 -1
- 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 +1 -2
- package/dist/agent-src/rules/finance-safety-floor.md +0 -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 +1 -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 +0 -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 +0 -1
- package/dist/agent-src/rules/lethal-trifecta-guard.md +0 -1
- package/dist/agent-src/rules/linked-projects-onboarding-gate.md +0 -1
- package/dist/agent-src/rules/low-impact-corpus-privacy-floor.md +0 -1
- package/dist/agent-src/rules/markdown-safe-codeblocks.md +0 -1
- package/dist/agent-src/rules/media-governance-routing.md +1 -2
- 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 +2 -3
- 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 +2 -3
- package/dist/agent-src/rules/question-not-instruction.md +0 -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 +3 -6
- 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 +0 -1
- package/dist/agent-src/rules/scale-discipline.md +0 -1
- package/dist/agent-src/rules/secret-vcs-guard.md +0 -1
- package/dist/agent-src/rules/security-sensitive-stop.md +0 -1
- 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 +0 -1
- package/dist/agent-src/rules/settings-ask-protocol.md +5 -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 +0 -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 +0 -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 +14 -48
- 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 +0 -1
- package/dist/agent-src/rules/ui-audit-gate.md +1 -2
- package/dist/agent-src/rules/untrusted-input-defense.md +0 -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 +0 -1
- package/dist/agent-src/scripts/gate_budget.ts +47 -23
- package/dist/agent-src/scripts/gate_execute.ts +12 -4
- package/dist/agent-src/skills/accessibility-auditor/data/aria-patterns.csv +2 -0
- package/dist/agent-src/skills/accessibility-auditor/data/manifest.json +3 -3
- 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/bug-analyzer/SKILL.md +1 -1
- package/dist/agent-src/skills/canvas-design/SKILL.md +4 -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-intelligence/references/integration-mapping.md +13 -10
- package/dist/agent-src/skills/design-system-capture/SKILL.md +0 -3
- package/dist/agent-src/skills/estimate-ticket/SKILL.md +1 -1
- package/dist/agent-src/skills/fe-design/references/design-patterns.md +49 -2
- package/dist/agent-src/skills/feature-planning/SKILL.md +1 -1
- package/dist/agent-src/skills/frontend-render-security/SKILL.md +9 -0
- 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 +4 -2
- package/dist/agent-src/skills/image-provider-routing/SKILL.md +16 -5
- package/dist/agent-src/skills/jira-integration/SKILL.md +3 -3
- package/dist/agent-src/skills/logo-generation/SKILL.md +12 -8
- package/dist/agent-src/skills/merge-conflicts/SKILL.md +10 -4
- package/dist/agent-src/skills/po-discovery/SKILL.md +1 -1
- package/dist/agent-src/skills/refine-ticket/SKILL.md +4 -4
- package/dist/agent-src/skills/roadmap-writing/SKILL.md +2 -2
- package/dist/agent-src/skills/skill-improvement-pipeline/SKILL.md +4 -2
- package/dist/agent-src/skills/source-discovery/SKILL.md +1 -1
- package/dist/agent-src/skills/svg-authoring/SKILL.md +318 -0
- package/dist/agent-src/skills/tailwind-engineer/SKILL.md +3 -1
- package/dist/agent-src/skills/testing-anti-patterns/SKILL.md +1 -1
- 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/skills/wireframe/SKILL.md +5 -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/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/skill.md +1 -1
- package/dist/cli/commands/uiRender.js +201 -4
- package/dist/cli/commands/uiRenderAsset.js +1308 -0
- package/dist/cli/commands/uiRenderAssetCompare.js +752 -0
- package/dist/cli/commands/uiRenderAssetGrid.js +265 -0
- package/dist/cli/commands/uiRenderAssetGrounds.js +323 -0
- package/dist/cli/commands/uiRenderAssetIsolation.js +85 -0
- package/dist/cli/commands/uiRenderAssetTypes.js +145 -0
- package/dist/cli/main.js +47 -1
- package/dist/cli/registry.js +2 -1
- package/dist/cli-delegate/check_update_banner.js +195 -119
- package/dist/cli-delegate/{chunk-S4UMZNES.js → chunk-4DBG5K7M.js} +5 -5
- package/dist/cli-delegate/{chunk-TRI6DQZ6.js → chunk-5GPULQBE.js} +60 -42
- package/dist/cli-delegate/{chunk-JQVYKIZA.js → chunk-6SRJTYJS.js} +33 -177
- package/dist/cli-delegate/chunk-6V5FMA3P.js +186 -0
- 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-7HQ52DHU.js → chunk-AL3RZ22D.js} +1 -1
- package/dist/cli-delegate/{chunk-DBH4AHDK.js → chunk-ED7KCMLB.js} +9 -11
- package/dist/cli-delegate/{chunk-NXEILF2K.js → chunk-F3QDG2GT.js} +481 -72
- package/dist/cli-delegate/{chunk-CQWX336L.js → chunk-G5VVIZRC.js} +3 -3
- package/dist/cli-delegate/{chunk-JY2I2DXV.js → chunk-H76PHZBI.js} +137 -60
- package/dist/cli-delegate/chunk-IAYTIIQW.js +67 -0
- package/dist/cli-delegate/{chunk-LIYTLH6O.js → chunk-KXXWJTDO.js} +5 -10
- package/dist/cli-delegate/{chunk-T7L2QK3K.js → chunk-LQOCRFWT.js} +1 -1
- package/dist/cli-delegate/{chunk-S4YWNRKO.js → chunk-MDPPKQCQ.js} +34 -1
- package/dist/cli-delegate/chunk-OAFSJBHH.js +272 -0
- package/dist/cli-delegate/{chunk-GKE7ZVFA.js → chunk-PWX7CKMD.js} +1 -1
- package/dist/cli-delegate/{chunk-NDJLNC6Q.js → chunk-T6BP34WI.js} +1 -0
- package/dist/cli-delegate/{chunk-RQNNW7IF.js → chunk-T7XSJSUQ.js} +433 -632
- package/dist/cli-delegate/{chunk-NQ7WT43P.js → chunk-TMWYUOK4.js} +75 -12
- package/dist/cli-delegate/{chunk-EEHDXSYD.js → chunk-WQWXGIWB.js} +12 -273
- package/dist/cli-delegate/{chunk-QLDHIPWR.js → chunk-XQTPXKFA.js} +1 -1
- 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 +489 -238
- package/dist/cli-delegate/cmd_converge.js +3 -3
- package/dist/cli-delegate/cmd_doctor.js +15 -12
- package/dist/cli-delegate/cmd_doctor_neighbours.js +11 -4
- package/dist/cli-delegate/cmd_explain.js +43 -19
- package/dist/cli-delegate/cmd_export.js +10 -10
- package/dist/cli-delegate/cmd_git_convention.js +1851 -0
- package/dist/cli-delegate/cmd_handoff.js +40 -21
- package/dist/cli-delegate/cmd_mcp_available.js +3 -2
- package/dist/cli-delegate/cmd_migrate.js +3 -3
- package/dist/cli-delegate/cmd_packs_active.js +4 -3
- package/dist/cli-delegate/cmd_prune.js +10 -10
- package/dist/cli-delegate/cmd_route_audit.js +7 -7
- package/dist/cli-delegate/cmd_route_explain.js +5 -5
- package/dist/cli-delegate/cmd_session_recycle.js +9 -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 +10 -10
- package/dist/cli-delegate/cmd_settings_set.js +1 -1
- package/dist/cli-delegate/cmd_sync.js +10 -10
- package/dist/cli-delegate/cmd_uninstall.js +10 -10
- package/dist/cli-delegate/cmd_update.js +7 -7
- package/dist/cli-delegate/cmd_upgrade.js +7 -7
- package/dist/cli-delegate/cmd_validate.js +10 -10
- package/dist/cli-delegate/cmd_versions.js +5 -5
- package/dist/cli-delegate/gate_budget.js +1 -1
- package/dist/cli-delegate/gate_execute.js +2 -2
- package/dist/cli-delegate/roadmap_gates.js +2 -2
- package/dist/discovery/deprecation-report.md +1 -1
- package/dist/discovery/discovery-manifest.json +171 -107
- package/dist/discovery/discovery-manifest.json.sha256 +1 -1
- package/dist/discovery/discovery-manifest.summary.md +7 -7
- package/dist/discovery/dormancy-report.md +1 -1
- package/dist/discovery/orphan-report.md +1 -1
- package/dist/discovery/packs.json +41 -22
- package/dist/discovery/trust-report.md +3 -3
- package/dist/discovery/workspaces.json +16 -11
- package/dist/hooks/dispatch.js +2520 -1924
- package/dist/hooks/dispatch.sha256 +1 -1
- package/dist/hooks/svg_rules.chunk.cjs +1626 -0
- package/dist/hooks/svg_rules.chunk.cjs.sha256 +1 -0
- package/dist/install/install.mjs +462 -431
- package/dist/install/installThinLayer.js +40 -12
- package/dist/install/installedRuleLinks.js +105 -19
- 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 +3063 -2160
- package/dist/router.json +1 -1
- package/dist/scripts/_lib/agent_settings.js +15 -120
- 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 +344 -18
- package/dist/scripts/_lib/package_root.js +68 -0
- package/dist/scripts/_lib/rule_law_section.js +1 -1
- package/dist/scripts/_lib/scan_scope.js +105 -0
- package/dist/scripts/_lib/settings_removed_keys.js +168 -0
- package/dist/scripts/_lib/svg_dom.js +284 -0
- package/dist/scripts/_lib/svg_geometry.js +477 -0
- package/dist/scripts/_lib/svg_motion.js +179 -0
- package/dist/scripts/_lib/svg_rules.js +1337 -0
- package/dist/scripts/_lib/svg_urls.js +67 -0
- package/dist/scripts/_lib/thin_rules.js +80 -3
- package/dist/scripts/lint_svg_asset.js +324 -0
- 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 +23 -81
- package/dist/shared/skillRanking.js +30 -4
- 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 +35 -65
- 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/design-antipatterns.md +1 -1
- package/docs/guidelines/php/git.md +15 -2
- package/package.json +4 -3
- package/src/agent-src/scripts/gate_budget.ts +47 -23
- package/src/agent-src/scripts/gate_execute.ts +12 -4
- 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/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/config/agent-settings.template.yml +79 -312
- 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 +3 -3
- package/src/config/discovery/packs.yml +17 -1
- package/src/config/evaluator-budgets.json +19 -6
- package/src/config/gate-coverage.yml +65 -23
- package/src/config/gate-violation-baselines.json +56 -16
- package/src/config/gitignore-block.txt +19 -0
- package/src/config/hook-bundle-budget.json +1 -1
- package/src/config/release-gate-locality.yml +2 -1
- package/src/config/routing-coverage-seed.json +55 -6
- package/src/config/rule-law-ceilings.json +12 -6
- package/src/config/rule-obligations.json +833 -0
- package/src/install/installThinLayer.ts +76 -10
- package/src/install/installedRuleLinks.ts +105 -17
- 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 +10 -10
- package/src/scripts/_cli/cmd_doctor_neighbours.ts +133 -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 +37 -2
- package/src/scripts/_lib/agent_settings.ts +15 -124
- package/src/scripts/_lib/blocked_by_marker.ts +1 -1
- package/src/scripts/_lib/compile_time_toggles.ts +12 -21
- package/src/scripts/_lib/dispatch_import_closure.ts +163 -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/global_deploy_inventory.ts +74 -0
- package/src/scripts/_lib/graph_feeder_record.ts +236 -13
- package/src/scripts/_lib/installed_layer.ts +448 -23
- package/src/scripts/_lib/knowledge_global.ts +14 -1
- package/src/scripts/_lib/module_reach.ts +456 -0
- package/src/scripts/_lib/neighbour_contradictions.ts +236 -0
- package/src/scripts/_lib/neighbour_tool_use.ts +8 -10
- 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/rule_law_section.ts +1 -1
- 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/svg_dom.ts +318 -0
- package/src/scripts/_lib/svg_geometry.ts +480 -0
- package/src/scripts/_lib/svg_motion.ts +208 -0
- package/src/scripts/_lib/svg_rules.ts +1453 -0
- package/src/scripts/_lib/svg_urls.ts +72 -0
- package/src/scripts/_lib/test_red_state.ts +5 -0
- package/src/scripts/_lib/thin_rules.ts +83 -3
- package/src/scripts/_lib/touched_file_quality.ts +142 -6
- package/src/scripts/_lib/verification_command.ts +59 -0
- package/src/scripts/_lib/verify_clause.ts +27 -17
- package/src/scripts/_lib/worker_budget.ts +1 -1
- package/src/scripts/adr/regenerate_index.ts +16 -1
- package/src/scripts/ai_council/config.ts +7 -7
- 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_team/config.ts +11 -16
- package/src/scripts/audit_user_type_axis.ts +20 -8
- 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/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_evaluator_budgets.ts +4 -0
- package/src/scripts/check_finding_dispositions.ts +88 -25
- 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 +107 -0
- package/src/scripts/check_kernel_edit_ratified.ts +86 -6
- package/src/scripts/check_pack_size.ts +1 -1
- package/src/scripts/check_release_manifest_content.ts +189 -0
- package/src/scripts/check_standing_rule_delivery.ts +96 -0
- package/src/scripts/check_suppression_hygiene.ts +2 -5
- 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/command_suggester/settings.ts +8 -108
- package/src/scripts/command_suggester/types.ts +4 -5
- 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/explain_run.ts +8 -13
- package/src/scripts/generate_git_convention_grammar.ts +127 -0
- package/src/scripts/generate_host_cost_table.ts +20 -3
- package/src/scripts/handoff_context_hook.ts +86 -37
- package/src/scripts/hook_manifest.json +1 -1
- package/src/scripts/hook_manifest.yaml +25 -6
- package/src/scripts/hooks/block_config_weakening.ts +48 -5
- package/src/scripts/hooks/block_plumbing_writes.ts +8 -4
- package/src/scripts/hooks/block_speaking_inbox_dir.ts +16 -1
- package/src/scripts/hooks/concern_registry.ts +2 -0
- package/src/scripts/hooks/delegation_nudge_hook.ts +2 -3
- package/src/scripts/hooks/design_pass_hook.ts +157 -3
- package/src/scripts/hooks/evidence_independence.ts +12 -4
- package/src/scripts/hooks/mcp_usage_observation_hook.ts +121 -0
- package/src/scripts/hooks/push_settle_hook.ts +4 -3
- package/src/scripts/hooks/rule_inject_hook.ts +44 -3
- package/src/scripts/hooks/state_io.ts +8 -4
- package/src/scripts/hooks/svg_obligation.ts +497 -0
- package/src/scripts/hooks/svg_rules_loader.ts +133 -0
- package/src/scripts/hooks/telemetry_usage_hook.ts +7 -46
- package/src/scripts/hooks/turn_end_gate_hook.ts +6 -3
- package/src/scripts/install-hooks.sh +42 -14
- package/src/scripts/install.ts +7 -31
- package/src/scripts/lint_blocker_citations.ts +198 -0
- package/src/scripts/lint_consolidation_lineage.ts +6 -3
- package/src/scripts/lint_design_quality.ts +60 -12
- package/src/scripts/lint_evidence_artifacts.ts +9 -0
- package/src/scripts/lint_hook_concern_budget.ts +10 -21
- package/src/scripts/lint_motion_authority_drift.ts +27 -0
- package/src/scripts/lint_pack_boundaries.ts +406 -28
- 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 +151 -21
- package/src/scripts/lint_rule_skill_pack_reach.ts +17 -0
- package/src/scripts/lint_settings_classes.ts +1 -1
- package/src/scripts/lint_svg_asset.ts +365 -0
- package/src/scripts/lint_ticked_unmet_criteria.ts +165 -0
- package/src/scripts/lint_workflow_security.ts +12 -1
- package/src/scripts/measure_skill_ranker_baseline.ts +5 -0
- package/src/scripts/model_rule_injection.ts +3 -3
- 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/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_installed_rule_links.ts +118 -1
- package/src/scripts/report_module_reach.ts +212 -0
- package/src/scripts/report_obligation_mechanism.ts +16 -18
- package/src/scripts/report_parked_blockers.ts +222 -0
- package/src/scripts/report_required_checks_drift.ts +196 -0
- package/src/scripts/report_skill_ranker_confusion.ts +103 -2
- package/src/scripts/report_standing_payload_by_host.ts +1 -1
- package/src/scripts/routing_doctor.ts +2 -2
- package/src/scripts/schemas/agent-settings.schema.json +17 -31
- package/src/scripts/schemas/command.schema.json +1 -1
- package/src/scripts/schemas/skill.schema.json +1 -1
- package/src/scripts/self_review_gate.ts +39 -2
- package/src/scripts/skill_tools/score_skill_relevance.ts +28 -19
- package/src/scripts/sync_pr_branch.ts +403 -132
- package/src/scripts/trigger_eval_grandfather.json +0 -16
- package/src/server/schemas/settings.ts +33 -177
- package/src/shared/skillRanking.ts +53 -4
- package/dist/cli-delegate/chunk-NYX3L2PZ.js +0 -99
- 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
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* reader in the neighbour census.
|
|
4
4
|
*
|
|
5
5
|
* WHY A MODULE OF ITS OWN, AND NOT A CONSTANT IN EITHER SIDE. The writer lives
|
|
6
|
-
* in `hooks/
|
|
6
|
+
* in `hooks/mcp_usage_observation_hook.ts`, which is inlined into the shared hook
|
|
7
7
|
* bundle `check_hook_bundle_composition` caps; the reader lives in
|
|
8
8
|
* `_lib/neighbour_census.ts`, which is not. Putting the pair here lets esbuild
|
|
9
9
|
* tree-shake {@link toolsUsedByServer} out of the hook bundle while both sides
|
|
@@ -113,16 +113,14 @@ export function toolBelongsTo(tool: string, segment: string): boolean {
|
|
|
113
113
|
/**
|
|
114
114
|
* The directory the store lives under, from any directory inside the project.
|
|
115
115
|
*
|
|
116
|
-
*
|
|
117
|
-
*
|
|
118
|
-
* subdirectory would otherwise scatter one store per
|
|
119
|
-
*
|
|
120
|
-
*
|
|
121
|
-
*
|
|
122
|
-
* pinned equal by a fixture rather than by assertion.
|
|
116
|
+
* Both the writer and the reader call this one function, so the store is
|
|
117
|
+
* rooted at the directory holding `.agent-settings.yml` on both sides: a
|
|
118
|
+
* session started in a subdirectory would otherwise scatter one store per
|
|
119
|
+
* directory, and a monorepo with settings at the repo root and `.mcp.json`
|
|
120
|
+
* under `packages/web` would read `0` for a server in daily use. A fixture
|
|
121
|
+
* still pins the two sides equal end to end.
|
|
123
122
|
*
|
|
124
|
-
* No settings file on any ancestor → `start
|
|
125
|
-
* fallback.
|
|
123
|
+
* No settings file on any ancestor → `start`.
|
|
126
124
|
*/
|
|
127
125
|
export function resolveStoreRoot(start: string): string {
|
|
128
126
|
let dir = path.resolve(start);
|
|
@@ -96,10 +96,227 @@ export interface RatificationProblem {
|
|
|
96
96
|
| 'no-providers'
|
|
97
97
|
| 'diversity-required'
|
|
98
98
|
| 'diversity-unverifiable'
|
|
99
|
-
| 'bad-effective-after'
|
|
99
|
+
| 'bad-effective-after'
|
|
100
|
+
| 'no-seats'
|
|
101
|
+
| 'malformed-seats'
|
|
102
|
+
| 'bad-seat-provider'
|
|
103
|
+
| 'unknown-seat-verdict'
|
|
104
|
+
| 'seat-dissent'
|
|
105
|
+
| 'providers-exceed-seats'
|
|
106
|
+
| 'header-not-derived';
|
|
100
107
|
message: string;
|
|
101
108
|
}
|
|
102
109
|
|
|
110
|
+
/** A seat that closed, timed out or was absent at the last round gave no final verdict. */
|
|
111
|
+
export const SEAT_NO_FINAL_VERDICT = 'no-final-verdict';
|
|
112
|
+
export type SeatVerdict = RatificationVerdict | typeof SEAT_NO_FINAL_VERDICT;
|
|
113
|
+
/** Every value a seat's final verdict may take — shared by the writer and the reader. */
|
|
114
|
+
export const SEAT_VERDICTS: readonly string[] = [...RATIFICATION_VERDICTS, SEAT_NO_FINAL_VERDICT];
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* A provider id as a seat key: lower-case, no whitespace and no YAML
|
|
118
|
+
* metacharacter, so a rendered header cannot be split or extended by a name
|
|
119
|
+
* and two spellings of one provider cannot collide after normalisation.
|
|
120
|
+
*/
|
|
121
|
+
export const SEAT_PROVIDER_RE = /^[a-z0-9][a-z0-9._-]*$/u;
|
|
122
|
+
|
|
123
|
+
export type SeatsReading =
|
|
124
|
+
| { kind: 'absent' }
|
|
125
|
+
| { kind: 'invalid'; problems: RatificationProblem[] }
|
|
126
|
+
| { kind: 'present'; seats: Map<string, SeatVerdict> };
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* The `seats:` map — provider id to that seat's FINAL verdict — in source
|
|
130
|
+
* order, which is the order `providers` is derived in.
|
|
131
|
+
*
|
|
132
|
+
* Absent and present-but-unusable are reported apart: a missing key and a
|
|
133
|
+
* `seats:` key that is empty, a scalar, a list, or carries a bad id or verdict
|
|
134
|
+
* are both refusals, with different codes.
|
|
135
|
+
*/
|
|
136
|
+
export function readSeats(fm: AdrFrontmatter, text = ''): SeatsReading {
|
|
137
|
+
const duplicates = duplicateSeatKeys(text);
|
|
138
|
+
if (duplicates.length > 0) {
|
|
139
|
+
return {
|
|
140
|
+
kind: 'invalid',
|
|
141
|
+
problems: [
|
|
142
|
+
{
|
|
143
|
+
code: 'bad-seat-provider',
|
|
144
|
+
message: `seat \`${duplicates.join('`, `')}\` is recorded more than once — the parser keeps only the last`,
|
|
145
|
+
},
|
|
146
|
+
],
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
const hasScalar = Object.prototype.hasOwnProperty.call(fm.scalars, 'seats');
|
|
150
|
+
const node = fm.nested['seats'];
|
|
151
|
+
if (node === undefined && !hasScalar) {
|
|
152
|
+
return { kind: 'absent' };
|
|
153
|
+
}
|
|
154
|
+
if (node === undefined || typeof node === 'string' || Array.isArray(node)) {
|
|
155
|
+
return {
|
|
156
|
+
kind: 'invalid',
|
|
157
|
+
problems: [{ code: 'malformed-seats', message: '`seats:` is present but is not a provider-to-verdict map' }],
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
const problems: RatificationProblem[] = [];
|
|
161
|
+
const seats = new Map<string, SeatVerdict>();
|
|
162
|
+
for (const [provider, verdict] of Object.entries(node)) {
|
|
163
|
+
if (!SEAT_PROVIDER_RE.test(provider)) {
|
|
164
|
+
problems.push({
|
|
165
|
+
code: 'bad-seat-provider',
|
|
166
|
+
message: `seat key \`${provider}\` is not a provider id (${String(SEAT_PROVIDER_RE)})`,
|
|
167
|
+
});
|
|
168
|
+
continue;
|
|
169
|
+
}
|
|
170
|
+
if (typeof verdict !== 'string' || !SEAT_VERDICTS.includes(verdict.trim())) {
|
|
171
|
+
problems.push({
|
|
172
|
+
code: 'unknown-seat-verdict',
|
|
173
|
+
message: `seat \`${provider}\` records \`${String(verdict)}\`, not one of ${SEAT_VERDICTS.join(', ')}`,
|
|
174
|
+
});
|
|
175
|
+
continue;
|
|
176
|
+
}
|
|
177
|
+
seats.set(provider, verdict.trim() as SeatVerdict);
|
|
178
|
+
}
|
|
179
|
+
if (problems.length === 0 && seats.size === 0) {
|
|
180
|
+
problems.push({ code: 'malformed-seats', message: '`seats:` is present and empty' });
|
|
181
|
+
}
|
|
182
|
+
return problems.length > 0 ? { kind: 'invalid', problems } : { kind: 'present', seats };
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/** Keys repeated inside the frontmatter `seats:` block, read from the raw text. */
|
|
186
|
+
function duplicateSeatKeys(text: string): string[] {
|
|
187
|
+
if (!text.startsWith('---\n')) return [];
|
|
188
|
+
const end = text.indexOf('\n---\n', 4);
|
|
189
|
+
const lines = text.slice(4, end === -1 ? undefined : end).split('\n');
|
|
190
|
+
const start = lines.findIndex((l) => /^seats:\s*$/u.test(l));
|
|
191
|
+
if (start === -1) return [];
|
|
192
|
+
const seen = new Set<string>();
|
|
193
|
+
const dup = new Set<string>();
|
|
194
|
+
for (const line of lines.slice(start + 1)) {
|
|
195
|
+
if (!/^\s/u.test(line)) break;
|
|
196
|
+
// Compared by the identity a YAML parser would give the key, so a quoted
|
|
197
|
+
// spelling cannot hide a second entry for the same seat.
|
|
198
|
+
const key = line
|
|
199
|
+
.slice(0, line.indexOf(':') === -1 ? undefined : line.indexOf(':'))
|
|
200
|
+
.trim()
|
|
201
|
+
.replace(/^(["'])(.*)\1$/u, '$2')
|
|
202
|
+
.trim();
|
|
203
|
+
if (key === '' || key.startsWith('#')) continue;
|
|
204
|
+
if (seen.has(key)) dup.add(key);
|
|
205
|
+
seen.add(key);
|
|
206
|
+
}
|
|
207
|
+
return [...dup];
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* The header a set of seat verdicts supports, and no more.
|
|
212
|
+
*
|
|
213
|
+
* `providers` is every seat that gave a final verdict — a closed seat is not a
|
|
214
|
+
* provider the verdict came from. Any refusing seat makes the verdict `refused`,
|
|
215
|
+
* any non-convergent one `non-convergent`; only when every final seat passed is
|
|
216
|
+
* the verdict passing, and then `ratified` if any seat said so. No final seat at
|
|
217
|
+
* all is `non-convergent`: a review that ended without a verdict did not pass.
|
|
218
|
+
*/
|
|
219
|
+
export function deriveRatificationHeader(
|
|
220
|
+
seats: ReadonlyMap<string, SeatVerdict> | Readonly<Record<string, SeatVerdict>>,
|
|
221
|
+
): { providers: string[]; verdict: RatificationVerdict } {
|
|
222
|
+
const entries = seats instanceof Map ? [...seats.entries()] : Object.entries(seats);
|
|
223
|
+
const final = entries.filter(([, v]) => v !== SEAT_NO_FINAL_VERDICT) as [string, RatificationVerdict][];
|
|
224
|
+
const verdicts = final.map(([, v]) => v);
|
|
225
|
+
let verdict: RatificationVerdict;
|
|
226
|
+
if (verdicts.includes('refused')) {
|
|
227
|
+
verdict = 'refused';
|
|
228
|
+
} else if (final.length === 0 || verdicts.includes('non-convergent')) {
|
|
229
|
+
verdict = 'non-convergent';
|
|
230
|
+
} else {
|
|
231
|
+
verdict = verdicts.includes('ratified') ? 'ratified' : 'confirmed-non-expanding';
|
|
232
|
+
}
|
|
233
|
+
return { providers: final.map(([p]) => p), verdict };
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* The `providers:`, `verdict:` and `seats:` frontmatter lines, derived — never
|
|
238
|
+
* typed. Throws on a provider id outside {@link SEAT_PROVIDER_RE}: a name that
|
|
239
|
+
* could carry YAML syntax is refused rather than escaped.
|
|
240
|
+
*/
|
|
241
|
+
export function renderRatificationHeader(
|
|
242
|
+
seats: ReadonlyMap<string, SeatVerdict> | Readonly<Record<string, SeatVerdict>>,
|
|
243
|
+
): string {
|
|
244
|
+
const entries = seats instanceof Map ? [...seats.entries()] : Object.entries(seats);
|
|
245
|
+
for (const [p, v] of entries) {
|
|
246
|
+
if (!SEAT_PROVIDER_RE.test(p)) throw new Error(`not a provider id: ${JSON.stringify(p)}`);
|
|
247
|
+
if (!SEAT_VERDICTS.includes(v)) throw new Error(`not a seat verdict: ${JSON.stringify(v)}`);
|
|
248
|
+
}
|
|
249
|
+
const { providers, verdict } = deriveRatificationHeader(seats);
|
|
250
|
+
return [
|
|
251
|
+
`providers: [${providers.join(', ')}]`,
|
|
252
|
+
`verdict: ${verdict}`,
|
|
253
|
+
'seats:',
|
|
254
|
+
...entries.map(([p, v]) => ` ${p}: ${v}`),
|
|
255
|
+
].join('\n');
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* The header claims the seats do not support.
|
|
260
|
+
*
|
|
261
|
+
* `seats:` is required. Optional, it left `providers` and `verdict` free-written
|
|
262
|
+
* for every author who omitted it, which is the defect it exists to close; the
|
|
263
|
+
* round-2 ratification review refused that shape. The gate reads only the
|
|
264
|
+
* artifacts in the diff under review, so an artifact merged before the field
|
|
265
|
+
* existed is never re-read and needs no migration to stay valid.
|
|
266
|
+
*
|
|
267
|
+
* The recorded header must EQUAL the derived one; the two named overclaims get
|
|
268
|
+
* their own codes so a reader sees which one it was.
|
|
269
|
+
*/
|
|
270
|
+
function seatProblems(reading: SeatsReading, providers: readonly string[], verdict: string | null): RatificationProblem[] {
|
|
271
|
+
if (reading.kind === 'absent') {
|
|
272
|
+
return [
|
|
273
|
+
{
|
|
274
|
+
code: 'no-seats',
|
|
275
|
+
message:
|
|
276
|
+
'missing `seats:` — `providers` and `verdict` are derived from the per-seat final ' +
|
|
277
|
+
'verdicts, never typed (print all three with `ratification_header`)',
|
|
278
|
+
},
|
|
279
|
+
];
|
|
280
|
+
}
|
|
281
|
+
if (reading.kind === 'invalid') return reading.problems;
|
|
282
|
+
const seats = reading.seats;
|
|
283
|
+
const problems: RatificationProblem[] = [];
|
|
284
|
+
if (verdict !== null && PASSING_VERDICTS.has(verdict)) {
|
|
285
|
+
const dissent = [...seats].filter(([, v]) => v === 'refused' || v === 'non-convergent');
|
|
286
|
+
if (dissent.length > 0) {
|
|
287
|
+
problems.push({
|
|
288
|
+
code: 'seat-dissent',
|
|
289
|
+
message:
|
|
290
|
+
`verdict \`${verdict}\` while ${dissent.map(([p, v]) => `${p} was \`${v}\``).join(', ')} ` +
|
|
291
|
+
'at the final round — a header cannot say more than its seats said',
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
const derived = deriveRatificationHeader(seats);
|
|
296
|
+
const extra = providers.filter((p) => !derived.providers.includes(p));
|
|
297
|
+
if (extra.length > 0) {
|
|
298
|
+
problems.push({
|
|
299
|
+
code: 'providers-exceed-seats',
|
|
300
|
+
message:
|
|
301
|
+
`providers names ${extra.join(', ')}, which gave no final verdict ` +
|
|
302
|
+
`(${String(derived.providers.length)} seat(s) did) — diversity is counted over seats that answered`,
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
if (
|
|
306
|
+
problems.length === 0 &&
|
|
307
|
+
(verdict !== derived.verdict || providers.join('\u0000') !== derived.providers.join('\u0000'))
|
|
308
|
+
) {
|
|
309
|
+
problems.push({
|
|
310
|
+
code: 'header-not-derived',
|
|
311
|
+
message:
|
|
312
|
+
`recorded providers [${providers.join(', ')}] / verdict \`${String(verdict)}\` differ from the ` +
|
|
313
|
+
`header the seats derive: [${derived.providers.join(', ')}] / \`${derived.verdict}\` ` +
|
|
314
|
+
'(print it with `ratification_header`)',
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
return problems;
|
|
318
|
+
}
|
|
319
|
+
|
|
103
320
|
export interface RatificationReading {
|
|
104
321
|
artifact: RatificationArtifact | null;
|
|
105
322
|
problems: RatificationProblem[];
|
|
@@ -208,6 +425,8 @@ export function readRatification(
|
|
|
208
425
|
}
|
|
209
426
|
}
|
|
210
427
|
|
|
428
|
+
problems.push(...seatProblems(readSeats(fm, text), providers, verdict));
|
|
429
|
+
|
|
211
430
|
// Diversity, against the REQUIRED count.
|
|
212
431
|
//
|
|
213
432
|
// Round 1 refused the previous shape: the count came from the user-global
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ratification_subject — the digest a ratification record carries over the
|
|
3
|
+
* gated content it reviewed.
|
|
4
|
+
*
|
|
5
|
+
* `road-to-self-modification-that-a-council-must-pass` step 2.1, the half that
|
|
6
|
+
* does not touch the gate. The record says WHO reviewed; without a digest it
|
|
7
|
+
* does not say WHAT, so a gated file edited after its review still ships under
|
|
8
|
+
* a record that approved something else.
|
|
9
|
+
*
|
|
10
|
+
* WHY IT RE-ASSEMBLES THE LIST RATHER THAN RE-DECIDING IT. The gate's
|
|
11
|
+
* `classifyPaths` returns the kernel rules, hooks and plumbing by name but
|
|
12
|
+
* reports the self surface as a BOOLEAN, so the gated list cannot be read back
|
|
13
|
+
* out of it. This module therefore imports the gate's own path constants and
|
|
14
|
+
* re-assembles — it never restates the membership rule, because a second
|
|
15
|
+
* definition of "gated" is a second thing to keep in step, and the first time
|
|
16
|
+
* the two diverged a file would be reviewed by one and digested by the other.
|
|
17
|
+
*
|
|
18
|
+
* NO BASE DIFF. The digest is over the tree as it stands, so it is the same on
|
|
19
|
+
* a branch and on the merged checkout CI judges — unless the base changed a
|
|
20
|
+
* gated file the change also touches, and then the content that would land is
|
|
21
|
+
* not the content that was reviewed, which is precisely what should move it.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
import { createHash } from 'node:crypto';
|
|
25
|
+
import * as fs from 'node:fs';
|
|
26
|
+
import * as path from 'node:path';
|
|
27
|
+
|
|
28
|
+
import {
|
|
29
|
+
ANCHOR_PATHS,
|
|
30
|
+
CLOSURE_READER_PATH,
|
|
31
|
+
POLICY_PATH,
|
|
32
|
+
READER_PATH,
|
|
33
|
+
SELF_PATH,
|
|
34
|
+
WORKFLOW_PATH,
|
|
35
|
+
classifyPaths,
|
|
36
|
+
} from '../check_kernel_edit_ratified.js';
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Stands in for the content of a gated file the change deletes. A deleted file
|
|
40
|
+
* is ENTERED, never skipped: skipping would make "this file was removed"
|
|
41
|
+
* digest-identical to "this file was never gated", and removing a governance
|
|
42
|
+
* hook is exactly the change the record most needs to be bound to.
|
|
43
|
+
*/
|
|
44
|
+
export const DELETED_MARKER = '\u0000deleted\u0000';
|
|
45
|
+
|
|
46
|
+
/** The self surface, by the gate's own constants rather than a copy of them. */
|
|
47
|
+
const SELF_SET: ReadonlySet<string> = new Set<string>([
|
|
48
|
+
SELF_PATH,
|
|
49
|
+
READER_PATH,
|
|
50
|
+
CLOSURE_READER_PATH,
|
|
51
|
+
POLICY_PATH,
|
|
52
|
+
WORKFLOW_PATH,
|
|
53
|
+
...ANCHOR_PATHS,
|
|
54
|
+
]);
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* The gated subset of a changed-file list: sorted, deduplicated, and stable
|
|
58
|
+
* against the order the caller happened to collect the paths in.
|
|
59
|
+
*/
|
|
60
|
+
export function gatedFilesOf(
|
|
61
|
+
files: readonly string[],
|
|
62
|
+
derived?: readonly string[],
|
|
63
|
+
): readonly string[] {
|
|
64
|
+
const normalized = files.map((f) => f.replace(/\\/g, '/').trim()).filter((f) => f !== '');
|
|
65
|
+
const g = derived === undefined ? classifyPaths(normalized) : classifyPaths(normalized, derived);
|
|
66
|
+
const out = new Set<string>([...g.kernelRules, ...g.governanceHooks, ...g.plumbing]);
|
|
67
|
+
for (const f of normalized) {
|
|
68
|
+
if (SELF_SET.has(f)) out.add(f);
|
|
69
|
+
}
|
|
70
|
+
return [...out].sort();
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* One digest over the sorted paths AND contents of the gated files, read from
|
|
75
|
+
* `root`.
|
|
76
|
+
*
|
|
77
|
+
* The path is hashed alongside its content, so two files with identical bytes
|
|
78
|
+
* do not collide and a rename moves the digest — a gated file moved to another
|
|
79
|
+
* gated path is a different change, not the same one.
|
|
80
|
+
*/
|
|
81
|
+
export function subjectDigest(root: string, files: readonly string[]): string {
|
|
82
|
+
const h = createHash('sha256');
|
|
83
|
+
for (const rel of gatedFilesOf(files)) {
|
|
84
|
+
const abs = path.join(root, rel);
|
|
85
|
+
const content = fs.existsSync(abs) ? fs.readFileSync(abs) : Buffer.from(DELETED_MARKER);
|
|
86
|
+
h.update(rel);
|
|
87
|
+
h.update('\u0000');
|
|
88
|
+
h.update(createHash('sha256').update(content).digest('hex'));
|
|
89
|
+
h.update('\n');
|
|
90
|
+
}
|
|
91
|
+
return h.digest('hex');
|
|
92
|
+
}
|
|
@@ -160,7 +160,9 @@ export function dispositionStopMessage(
|
|
|
160
160
|
resumeCmd: string,
|
|
161
161
|
): string {
|
|
162
162
|
const count =
|
|
163
|
-
blocking > 0
|
|
163
|
+
blocking > 0
|
|
164
|
+
? ` ${blocking} of them are blocking findings with no disposition yet (no status, or still_open).\n`
|
|
165
|
+
: '';
|
|
164
166
|
return (
|
|
165
167
|
`the disposition gate refuses ${version}. Its own report:\n` +
|
|
166
168
|
`${gateOutput.trim() || ' (the gate printed nothing — run it directly to see why)'}\n` +
|
|
@@ -296,7 +298,7 @@ export function settleFindingsLedger(
|
|
|
296
298
|
// the operator `scanned: N` and discarded the entire reason the
|
|
297
299
|
// release stopped.
|
|
298
300
|
[verdict.stdout, verdict.stderr].map((s) => s.trim()).filter(Boolean).join('\n'),
|
|
299
|
-
blockingWithoutDisposition(path.join(deps.repoRoot, rel)),
|
|
301
|
+
blockingWithoutDisposition(path.join(deps.repoRoot, rel), version),
|
|
300
302
|
'task release -- --resume --yes',
|
|
301
303
|
),
|
|
302
304
|
);
|
|
@@ -304,20 +306,26 @@ export function settleFindingsLedger(
|
|
|
304
306
|
}
|
|
305
307
|
|
|
306
308
|
/**
|
|
307
|
-
* How many blocking findings carry no
|
|
308
|
-
*
|
|
309
|
+
* How many blocking findings carry no disposition — no status, or `still_open`,
|
|
310
|
+
* which a blocking row may not rest on — a figure for the stop message, never
|
|
311
|
+
* the reason for it.
|
|
309
312
|
*
|
|
310
313
|
* The gate refuses for more shapes than this counts (an unknown status, an
|
|
311
314
|
* empty rationale or verifier, a `fixed` with no commit, a malformed ledger),
|
|
312
|
-
* which is why the gate's own output is what the operator is shown.
|
|
315
|
+
* which is why the gate's own output is what the operator is shown. It
|
|
316
|
+
* classifies against the version being released, the same input the gate
|
|
317
|
+
* reads from `--release`, not the ledger file's own `release` field.
|
|
313
318
|
*/
|
|
314
|
-
function blockingWithoutDisposition(ledgerAbs: string): number {
|
|
319
|
+
export function blockingWithoutDisposition(ledgerAbs: string, version: string): number {
|
|
315
320
|
if (!fs.existsSync(ledgerAbs)) {
|
|
316
321
|
return 0;
|
|
317
322
|
}
|
|
318
323
|
try {
|
|
319
324
|
const ledger = parse_ledger(fs.readFileSync(ledgerAbs, 'utf-8'), ledgerAbs);
|
|
320
|
-
return ledger.findings.filter((f) =>
|
|
325
|
+
return ledger.findings.filter((f) => {
|
|
326
|
+
const status = (f.status ?? '').trim();
|
|
327
|
+
return isBlocking(f, version) && (status === '' || status === 'still_open');
|
|
328
|
+
}).length;
|
|
321
329
|
} catch {
|
|
322
330
|
return 0;
|
|
323
331
|
}
|
|
@@ -610,6 +610,11 @@ const TEST_FOOTER_RE = /^Tests: \d+/mu;
|
|
|
610
610
|
*/
|
|
611
611
|
export interface MixObligation {
|
|
612
612
|
level: string;
|
|
613
|
+
/** The resolved ends the counts were read on; the rendered level names both. */
|
|
614
|
+
from_sha?: string;
|
|
615
|
+
to_sha?: string;
|
|
616
|
+
governance_only?: number;
|
|
617
|
+
consumer_only?: number;
|
|
613
618
|
}
|
|
614
619
|
|
|
615
620
|
/** Labels whose curated value is still the generator's unedited draft. */
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
* law in bold prose or a bare fence under no heading at all — several of the 35
|
|
26
26
|
* routed rules without a heading match do exactly that. A heading match is a
|
|
27
27
|
* STRUCTURAL property, checkable; "this sentence is the obligation" is not, and
|
|
28
|
-
* `report_obligation_carriers` plus the
|
|
28
|
+
* `report_obligation_carriers` plus the ids in `src/config/rule-obligations.json` is where
|
|
29
29
|
* that question already lives.
|
|
30
30
|
*/
|
|
31
31
|
import * as fs from 'node:fs';
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stable obligation ids per rule, and which `enforced_by` entry refuses which.
|
|
3
|
+
*
|
|
4
|
+
* A rule holds several obligations, and `enforced_by` used to credit all of
|
|
5
|
+
* them for whatever one gate refuses: `git-history-discipline` counted as gated
|
|
6
|
+
* because a hook refuses `--no-verify`, while its laws against unsolicited
|
|
7
|
+
* rebase and dropped commits are refused by nothing. The inventory names each
|
|
8
|
+
* obligation `<rule>.<obligation>` so a gate is credited with the ids it
|
|
9
|
+
* actually refuses and nothing else.
|
|
10
|
+
*
|
|
11
|
+
* WHY A SIDECAR AND NOT RULE FRONTMATTER. `check_preamble_payload_budget`
|
|
12
|
+
* measures every byte of every projected rule file, frontmatter included, as
|
|
13
|
+
* per-spawn payload, and holds it at zero net growth. Some 290 ids in
|
|
14
|
+
* frontmatter would have been payload no model needs. The ids are routing and
|
|
15
|
+
* accounting data, so they live with the rest of that data in `src/config/`.
|
|
16
|
+
*
|
|
17
|
+
* WHY THE ID IS NOT DERIVED FROM THE LAW TEXT. A hash or an excerpt changes
|
|
18
|
+
* when the law is reworded, and an id that moves on every reword is not stable.
|
|
19
|
+
* The slug names the clause; the law text is free to change under it. That is
|
|
20
|
+
* also why the `# obligation: line N` frontmatter marker is retired: a line
|
|
21
|
+
* number drifts on every edit above it, and at retirement 34 of the 68 markers
|
|
22
|
+
* on rules with a law section no longer pointed inside that section.
|
|
23
|
+
*
|
|
24
|
+
* BINDINGS. `bindings` maps an `enforced_by` entry, spelled exactly as in the
|
|
25
|
+
* rule's frontmatter, to the ids it refuses. An entry with no key here is
|
|
26
|
+
* UNBOUND: it keeps the rule-level credit it always had and is counted
|
|
27
|
+
* separately, never as obligation-level coverage. An entry bound to `[]` is a
|
|
28
|
+
* recorded finding that it refuses none of this rule's obligations.
|
|
29
|
+
*
|
|
30
|
+
* The nine kernel rules are absent by construction: agents cannot write them,
|
|
31
|
+
* and they stay at rule granularity.
|
|
32
|
+
*/
|
|
33
|
+
import * as fs from 'node:fs';
|
|
34
|
+
import * as path from 'node:path';
|
|
35
|
+
|
|
36
|
+
import { parse as parseYaml } from 'yaml';
|
|
37
|
+
|
|
38
|
+
import { KERNEL_RULE_ID_SET } from './kernel_rules.js';
|
|
39
|
+
import { lawSections, ruleBody, splitFrontmatter } from './rule_law_section.js';
|
|
40
|
+
|
|
41
|
+
export const RULE_OBLIGATIONS_PATH = 'src/config/rule-obligations.json';
|
|
42
|
+
|
|
43
|
+
const SLUG = '[a-z0-9]+(?:-[a-z0-9]+)*';
|
|
44
|
+
|
|
45
|
+
/** `<rule>.<obligation>`, both halves kebab-case. */
|
|
46
|
+
export const OBLIGATION_ID_RE = new RegExp(`^(${SLUG})\\.(${SLUG})$`);
|
|
47
|
+
|
|
48
|
+
/** The retired per-rule pointer. Kept only so the lint can refuse its return. */
|
|
49
|
+
export const OBLIGATION_MARKER_RE = /^#\s*obligation:\s*line\s+\d+\s*$/m;
|
|
50
|
+
|
|
51
|
+
export interface RuleObligationEntry {
|
|
52
|
+
obligations: string[];
|
|
53
|
+
bindings?: Record<string, string[]>;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export interface RuleObligationInventory {
|
|
57
|
+
rules: Record<string, RuleObligationEntry>;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function loadRuleObligations(root: string): RuleObligationInventory {
|
|
61
|
+
const file = path.join(root, RULE_OBLIGATIONS_PATH);
|
|
62
|
+
const raw = JSON.parse(fs.readFileSync(file, 'utf-8')) as Partial<RuleObligationInventory>;
|
|
63
|
+
return { rules: raw.rules ?? {} };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** What the lint needs to know about one rule file. */
|
|
67
|
+
export interface RuleFacts {
|
|
68
|
+
id: string;
|
|
69
|
+
kernel: boolean;
|
|
70
|
+
hasLawSection: boolean;
|
|
71
|
+
enforcedBy: string[];
|
|
72
|
+
hasMarker: boolean;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export function readRuleFacts(id: string, text: string): RuleFacts {
|
|
76
|
+
const [fmBlock] = splitFrontmatter(text);
|
|
77
|
+
let enforcedBy: string[] = [];
|
|
78
|
+
if (fmBlock !== '') {
|
|
79
|
+
const inner = fmBlock.replace(/^---\n/, '').replace(/\n---\n$/, '');
|
|
80
|
+
const fm = (parseYaml(inner) ?? {}) as Record<string, unknown>;
|
|
81
|
+
const raw = fm['enforced_by'];
|
|
82
|
+
if (Array.isArray(raw)) enforcedBy = raw.map(String);
|
|
83
|
+
else if (typeof raw === 'string' && raw !== '') enforcedBy = [raw];
|
|
84
|
+
}
|
|
85
|
+
return {
|
|
86
|
+
id,
|
|
87
|
+
kernel: KERNEL_RULE_ID_SET.has(id),
|
|
88
|
+
hasLawSection: lawSections(ruleBody(text)).length > 0,
|
|
89
|
+
enforcedBy,
|
|
90
|
+
hasMarker: OBLIGATION_MARKER_RE.test(fmBlock),
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export function collectRuleFacts(rulesDir: string): RuleFacts[] {
|
|
95
|
+
return fs
|
|
96
|
+
.readdirSync(rulesDir)
|
|
97
|
+
.filter((n) => n.endsWith('.md'))
|
|
98
|
+
.sort()
|
|
99
|
+
.map((n) => readRuleFacts(n.slice(0, -3), fs.readFileSync(path.join(rulesDir, n), 'utf-8')));
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export interface ObligationFinding {
|
|
103
|
+
rule: string;
|
|
104
|
+
code:
|
|
105
|
+
| 'unknown-rule'
|
|
106
|
+
| 'kernel-declared'
|
|
107
|
+
| 'missing'
|
|
108
|
+
| 'empty'
|
|
109
|
+
| 'bad-id'
|
|
110
|
+
| 'wrong-prefix'
|
|
111
|
+
| 'duplicate'
|
|
112
|
+
| 'binding-entry-not-declared'
|
|
113
|
+
| 'binding-id-unknown'
|
|
114
|
+
| 'marker-present';
|
|
115
|
+
message: string;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Every finding the inventory and the rule tree produce together.
|
|
120
|
+
*
|
|
121
|
+
* Required: every non-kernel rule with a law section has an entry. A rule
|
|
122
|
+
* without one MAY have an entry, and if it does the same checks apply.
|
|
123
|
+
*/
|
|
124
|
+
export function lintRuleObligations(
|
|
125
|
+
inventory: RuleObligationInventory,
|
|
126
|
+
facts: readonly RuleFacts[],
|
|
127
|
+
): ObligationFinding[] {
|
|
128
|
+
const out: ObligationFinding[] = [];
|
|
129
|
+
const byId = new Map(facts.map((f) => [f.id, f]));
|
|
130
|
+
const seen = new Map<string, string>();
|
|
131
|
+
|
|
132
|
+
for (const f of facts) {
|
|
133
|
+
if (f.hasMarker) {
|
|
134
|
+
out.push({
|
|
135
|
+
rule: f.id,
|
|
136
|
+
code: 'marker-present',
|
|
137
|
+
message:
|
|
138
|
+
'carries a `# obligation: line N` marker; it is retired — the ids in ' +
|
|
139
|
+
`${RULE_OBLIGATIONS_PATH} name the obligation`,
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
if (!f.kernel && f.hasLawSection && inventory.rules[f.id] === undefined) {
|
|
143
|
+
out.push({
|
|
144
|
+
rule: f.id,
|
|
145
|
+
code: 'missing',
|
|
146
|
+
message: `has a law section and no entry in ${RULE_OBLIGATIONS_PATH}`,
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
for (const [rule, entry] of Object.entries(inventory.rules)) {
|
|
152
|
+
const fact = byId.get(rule);
|
|
153
|
+
if (fact === undefined) {
|
|
154
|
+
out.push({ rule, code: 'unknown-rule', message: 'no such rule under src/rules/' });
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
if (fact.kernel) {
|
|
158
|
+
out.push({
|
|
159
|
+
rule,
|
|
160
|
+
code: 'kernel-declared',
|
|
161
|
+
message: 'kernel rules stay at rule granularity and carry no obligation ids',
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
const ids = Array.isArray(entry.obligations) ? entry.obligations : [];
|
|
165
|
+
if (ids.length === 0) {
|
|
166
|
+
out.push({ rule, code: 'empty', message: 'declares no obligations' });
|
|
167
|
+
}
|
|
168
|
+
const own = new Set<string>();
|
|
169
|
+
for (const id of ids) {
|
|
170
|
+
const m = OBLIGATION_ID_RE.exec(id);
|
|
171
|
+
if (m === null) {
|
|
172
|
+
out.push({ rule, code: 'bad-id', message: `\`${id}\` is not \`<rule>.<obligation>\` kebab-case` });
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
175
|
+
if (m[1] !== rule) {
|
|
176
|
+
out.push({ rule, code: 'wrong-prefix', message: `\`${id}\` is not prefixed with \`${rule}.\`` });
|
|
177
|
+
}
|
|
178
|
+
const prior = seen.get(id);
|
|
179
|
+
if (prior !== undefined) {
|
|
180
|
+
out.push({ rule, code: 'duplicate', message: `\`${id}\` is declared twice (also under ${prior})` });
|
|
181
|
+
}
|
|
182
|
+
seen.set(id, rule);
|
|
183
|
+
own.add(id);
|
|
184
|
+
}
|
|
185
|
+
for (const [entryName, bound] of Object.entries(entry.bindings ?? {})) {
|
|
186
|
+
if (!fact.enforcedBy.includes(entryName)) {
|
|
187
|
+
out.push({
|
|
188
|
+
rule,
|
|
189
|
+
code: 'binding-entry-not-declared',
|
|
190
|
+
message: `binds \`${entryName}\`, which is not in the rule's enforced_by`,
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
for (const id of bound) {
|
|
194
|
+
if (!own.has(id)) {
|
|
195
|
+
out.push({
|
|
196
|
+
rule,
|
|
197
|
+
code: 'binding-id-unknown',
|
|
198
|
+
message: `\`${entryName}\` binds \`${id}\`, which this rule does not declare`,
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
return out;
|
|
205
|
+
}
|
|
@@ -322,7 +322,7 @@ function stemOf(token: string): string {
|
|
|
322
322
|
return base.replace(/\.md$/, '');
|
|
323
323
|
}
|
|
324
324
|
|
|
325
|
-
/** Dotted settings-key shape — `personal.autonomy`, `
|
|
325
|
+
/** Dotted settings-key shape — `personal.autonomy`, `memory.redact_patterns`, … */
|
|
326
326
|
const DOTTED_KEY_RE = /^[a-z0-9_]+(\.[a-z0-9_]+)+$/;
|
|
327
327
|
|
|
328
328
|
/**
|