@event4u/agent-config 16.3.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 +251 -0
- 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/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/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 +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 +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/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/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/estimate-ticket/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/jira-integration/SKILL.md +3 -3
- 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/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/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/registry.js +1 -0
- package/dist/cli-delegate/check_update_banner.js +195 -119
- 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-NQ7WT43P.js → chunk-AED2VQGM.js} +65 -11
- 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-S4UMZNES.js → chunk-MSUL5NYP.js} +5 -5
- 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-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 +105 -35
- 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 +129 -90
- 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 +24 -9
- package/dist/discovery/trust-report.md +3 -3
- package/dist/discovery/workspaces.json +5 -4
- package/dist/hooks/dispatch.js +2151 -1820
- package/dist/hooks/dispatch.sha256 +1 -1
- 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 +2702 -1995
- 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/settings_removed_keys.js +168 -0
- package/dist/scripts/_lib/thin_rules.js +80 -3
- 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/php/git.md +15 -2
- package/package.json +1 -1
- 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 +53 -13
- 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 +48 -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/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/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/concern_registry.ts +2 -0
- package/src/scripts/hooks/delegation_nudge_hook.ts +2 -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/telemetry_usage_hook.ts +7 -46
- package/src/scripts/hooks/turn_end_gate_hook.ts +1 -0
- 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_evidence_artifacts.ts +9 -0
- package/src/scripts/lint_hook_concern_budget.ts +10 -21
- 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_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
|
@@ -0,0 +1,584 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* What the graph feeder costs the stop slot (`road-to-a-graph-that-feeds-the-gate`
|
|
4
|
+
* step 3.5).
|
|
5
|
+
*
|
|
6
|
+
* `_lib/graph_feeder_record.ts` withdrew its "costs nothing" claim and left the
|
|
7
|
+
* latency unmeasured. It matters because the feeder runs inside the blocking
|
|
8
|
+
* `turn-end-gate` concern: a host that timed the stop hook out would discard F's
|
|
9
|
+
* refusal, so a slow shadow arm weakens the gate it is meant to feed.
|
|
10
|
+
*
|
|
11
|
+
* WHAT IS MEASURED. The hook's own `main()`, in-process, with the stdin override
|
|
12
|
+
* the in-process dispatcher uses — the route a host actually takes, so a spawn
|
|
13
|
+
* or `tsx` start-up cost does not drown the difference. Two arms over the SAME
|
|
14
|
+
* generated repository and the SAME turn (an absolute-path edit to a production
|
|
15
|
+
* file, no test touched, a completion claim, so detector F fires):
|
|
16
|
+
*
|
|
17
|
+
* with — a native graph exists and the edit is uncommitted, so the feeder
|
|
18
|
+
* takes its full path: `git` freshness probes, a graph open, an
|
|
19
|
+
* `untested` walk, a row append.
|
|
20
|
+
* without — the identical tree with no graph cache, so the feeder stops at
|
|
21
|
+
* `absent`. This is the cost a consumer who never builds a graph
|
|
22
|
+
* pays, and the baseline the feeder's increment is read against.
|
|
23
|
+
*
|
|
24
|
+
* Arms are INTERLEAVED, one call each per round, and the rounds cycle through
|
|
25
|
+
* all six ORDERS of the three calls: interleaving spreads slow drift across the
|
|
26
|
+
* arms, and the full permutation cycle balances the position of every call and
|
|
27
|
+
* its direct predecessor WITHIN a round (GC after the heavier arm, cache
|
|
28
|
+
* warmth) — exactly so when the round count is a multiple of six. Across a round
|
|
29
|
+
* boundary it does not: the first call of a round follows the previous round's
|
|
30
|
+
* last call, and those six transitions are unbalanced (two are a call following
|
|
31
|
+
* itself). A third
|
|
32
|
+
* reading, inside the same rotation, times the feeder's own work (`graphState` + `graphUntestedVerdict`) in isolation on the
|
|
33
|
+
* `with` tree, because the difference of two noisy totals is a weaker number
|
|
34
|
+
* than a direct one.
|
|
35
|
+
*
|
|
36
|
+
* WHAT IT DOES NOT CLAIM. One machine, one generated repository shape. The size
|
|
37
|
+
* is a flag rather than a constant so a reader can re-take the reading at the
|
|
38
|
+
* scale they care about; the published page names the machine and the flags it
|
|
39
|
+
* was taken with. Not a gate: it prints and exits 0 on any completed run.
|
|
40
|
+
*
|
|
41
|
+
* `--repo P --edit F` takes the third reading only, over an EXISTING repository
|
|
42
|
+
* and its own graph: no hook call and no feeder row written, but NOT read-only.
|
|
43
|
+
* `loadGraph` re-emits the repository's SQLite twin
|
|
44
|
+
* (`sqliteTwinPath` of its graph cache) whenever no valid one exists,
|
|
45
|
+
* and the unbilled warm-up round absorbs that write. A generated tree
|
|
46
|
+
* prices the walk; it does not price opening a real index, whose load time grows
|
|
47
|
+
* with the graph and is the larger term on a repository of any size.
|
|
48
|
+
*
|
|
49
|
+
* Usage:
|
|
50
|
+
* ./scripts-run src/scripts/bench_graph_feeder_latency [--runs N] [--files N]
|
|
51
|
+
* [--format text|json]
|
|
52
|
+
* ./scripts-run src/scripts/bench_graph_feeder_latency --repo P --edit F [--runs N]
|
|
53
|
+
* ./scripts-run src/scripts/bench_graph_feeder_latency --load-split GRAPH_JSON [--runs N]
|
|
54
|
+
*
|
|
55
|
+
* `--load-split` decomposes the load term `--repo` reports (`benchLoadSplit`)
|
|
56
|
+
* over a scratch copy of the graph file, so it writes nothing to the repository.
|
|
57
|
+
*/
|
|
58
|
+
import { execFileSync } from 'node:child_process';
|
|
59
|
+
import * as fs from 'node:fs';
|
|
60
|
+
import * as os from 'node:os';
|
|
61
|
+
import * as path from 'node:path';
|
|
62
|
+
import { performance } from 'node:perf_hooks';
|
|
63
|
+
import { pathToFileURL } from 'node:url';
|
|
64
|
+
|
|
65
|
+
import { gitEnv } from './_lib/git_env.js';
|
|
66
|
+
import { graphUntestedVerdict } from './_lib/graph_feeder_record.js';
|
|
67
|
+
import { LexicalIndex } from './_lib/lexical_index.js';
|
|
68
|
+
import { buildFromRepo } from './code_graph/build.js';
|
|
69
|
+
import { graphState, NATIVE_CACHE_REL } from './code_graph/detect.js';
|
|
70
|
+
import { loadGraph } from './code_graph/query.js';
|
|
71
|
+
import { emitSqliteTwin, sqliteTwinPath } from './code_graph/sqlite_store.js';
|
|
72
|
+
import type { CodeEdge, CodeGraph } from './code_graph/types.js';
|
|
73
|
+
import { validateGraph } from './code_graph/validate.js';
|
|
74
|
+
import { clearHookStdinOverride, setHookStdinOverride } from './hooks/hook_stdin.js';
|
|
75
|
+
import { main as turnEndGateMain } from './hooks/turn_end_gate_hook.js';
|
|
76
|
+
|
|
77
|
+
export interface Distribution {
|
|
78
|
+
n: number;
|
|
79
|
+
p50: number;
|
|
80
|
+
p95: number;
|
|
81
|
+
max: number;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export interface LatencyReport {
|
|
85
|
+
runs: number;
|
|
86
|
+
files: number;
|
|
87
|
+
graphState: string;
|
|
88
|
+
/** The `without` tree's state — `absent`, or that arm is not the no-feeder baseline. */
|
|
89
|
+
graphStateWithout: string;
|
|
90
|
+
/** Exit codes seen per arm — a shadow arm must not move them. */
|
|
91
|
+
exitCodes: { with: number[]; without: number[] };
|
|
92
|
+
with: Distribution;
|
|
93
|
+
without: Distribution;
|
|
94
|
+
/** `with.p50 - without.p50`, and the same for p95. */
|
|
95
|
+
delta: { p50: number; p95: number };
|
|
96
|
+
/** The feeder's own work, timed directly on the `with` tree. */
|
|
97
|
+
feederOnly: Distribution;
|
|
98
|
+
/**
|
|
99
|
+
* Every verdict the timed feeder calls returned. `graphUntestedVerdict`
|
|
100
|
+
* turns any throw into `null`, so a broken graph load would otherwise be
|
|
101
|
+
* timed as if it were the real walk.
|
|
102
|
+
*/
|
|
103
|
+
feederVerdicts: (string | null)[];
|
|
104
|
+
node: string;
|
|
105
|
+
platform: string;
|
|
106
|
+
cpu: string;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/** Nearest-rank percentile over an ascending array. */
|
|
110
|
+
export function percentile(sorted: readonly number[], p: number): number {
|
|
111
|
+
if (sorted.length === 0) return Number.NaN;
|
|
112
|
+
const rank = Math.ceil((p / 100) * sorted.length);
|
|
113
|
+
return sorted[Math.min(sorted.length, Math.max(1, rank)) - 1] as number;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export function distribution(samples: readonly number[]): Distribution {
|
|
117
|
+
const s = [...samples].sort((a, b) => a - b);
|
|
118
|
+
const r = (x: number): number => Math.round(x * 100) / 100;
|
|
119
|
+
return { n: s.length, p50: r(percentile(s, 50)), p95: r(percentile(s, 95)), max: r(s[s.length - 1] ?? Number.NaN) };
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function git(dir: string, args: string[]): void {
|
|
123
|
+
// An inherited GIT_DIR (every git hook exports one) overrides `-C`, which
|
|
124
|
+
// would commit the fixture onto the host repository.
|
|
125
|
+
const env = {
|
|
126
|
+
...gitEnv(),
|
|
127
|
+
GIT_AUTHOR_NAME: 'bench',
|
|
128
|
+
GIT_AUTHOR_EMAIL: 'bench@example.com',
|
|
129
|
+
GIT_COMMITTER_NAME: 'bench',
|
|
130
|
+
GIT_COMMITTER_EMAIL: 'bench@example.com',
|
|
131
|
+
};
|
|
132
|
+
execFileSync('git', ['-C', dir, ...args], { env, stdio: 'ignore' });
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* A committed repository of `files` production modules chained by imports, one
|
|
137
|
+
* test file covering the first module only, and the edited `src/service.ts`
|
|
138
|
+
* left uncommitted — the shape a real stop sees mid-session.
|
|
139
|
+
*/
|
|
140
|
+
export async function makeFixture(root: string, files: number, withGraph: boolean): Promise<string> {
|
|
141
|
+
const dir = fs.realpathSync(fs.mkdtempSync(path.join(root, withGraph ? 'with-' : 'without-')));
|
|
142
|
+
fs.mkdirSync(path.join(dir, 'src'));
|
|
143
|
+
fs.mkdirSync(path.join(dir, 'tests'));
|
|
144
|
+
fs.writeFileSync(path.join(dir, 'src', 'service.ts'), 'export function handle(x: string): string {\n return x;\n}\n');
|
|
145
|
+
for (let i = 0; i < files; i++) {
|
|
146
|
+
const prev = i === 0 ? './service.js' : `./mod${i - 1}.js`;
|
|
147
|
+
const prevFn = i === 0 ? 'handle' : `run${i - 1}`;
|
|
148
|
+
fs.writeFileSync(
|
|
149
|
+
path.join(dir, 'src', `mod${i}.ts`),
|
|
150
|
+
`import { ${prevFn} } from '${prev}';\n\nexport function run${i}(x: string): string {\n return ${prevFn}(x) + '${i}';\n}\n`,
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
fs.writeFileSync(
|
|
154
|
+
path.join(dir, 'tests', 'mod0.test.ts'),
|
|
155
|
+
// The specifier is assembled so `prepack-check`, which scans shipped
|
|
156
|
+
// sources for relative imports, does not read fixture text as one.
|
|
157
|
+
`import { run0 } from '${['..', 'src', 'mod0.js'].join('/')}';\n\nexport function check(): boolean {\n return run0('a') === 'a0';\n}\n`,
|
|
158
|
+
);
|
|
159
|
+
git(dir, ['init', '-q']);
|
|
160
|
+
git(dir, ['add', '-A']);
|
|
161
|
+
git(dir, ['commit', '-q', '-m', 'fixture']);
|
|
162
|
+
if (withGraph) await buildFromRepo(dir, path.join(dir, NATIVE_CACHE_REL));
|
|
163
|
+
fs.writeFileSync(path.join(dir, 'src', 'service.ts'), 'export function handle(x: string): string {\n return x.trim();\n}\n');
|
|
164
|
+
return dir;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function writeTranscript(dir: string, home: string, tag: string): string {
|
|
168
|
+
const file = path.join(home, `transcript-${tag}.jsonl`);
|
|
169
|
+
const lines = [
|
|
170
|
+
{ type: 'user', message: { content: 'mach das fertig' } },
|
|
171
|
+
{
|
|
172
|
+
type: 'assistant',
|
|
173
|
+
message: {
|
|
174
|
+
content: [{ type: 'tool_use', name: 'Edit', input: { file_path: path.join(dir, 'src', 'service.ts') } }],
|
|
175
|
+
},
|
|
176
|
+
},
|
|
177
|
+
{ type: 'assistant', message: { content: [{ type: 'text', text: 'Fertig. Der Service trimmt jetzt.' }] } },
|
|
178
|
+
];
|
|
179
|
+
fs.writeFileSync(file, lines.map((l) => JSON.stringify(l)).join('\n') + '\n');
|
|
180
|
+
return file;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/** One in-process stop. A fresh session id per call keeps every call on the live path. */
|
|
184
|
+
function timeStop(dir: string, transcriptPath: string, session: string): { ms: number; rc: number } {
|
|
185
|
+
setHookStdinOverride(
|
|
186
|
+
JSON.stringify({
|
|
187
|
+
schema_version: 1,
|
|
188
|
+
platform: 'claude',
|
|
189
|
+
event: 'stop',
|
|
190
|
+
native_event: 'Stop',
|
|
191
|
+
session_id: session,
|
|
192
|
+
workspace_root: dir,
|
|
193
|
+
payload: { transcript_path: transcriptPath },
|
|
194
|
+
settings: {},
|
|
195
|
+
}),
|
|
196
|
+
);
|
|
197
|
+
const write = process.stderr.write.bind(process.stderr);
|
|
198
|
+
process.stderr.write = (() => true) as typeof process.stderr.write;
|
|
199
|
+
const t0 = performance.now();
|
|
200
|
+
try {
|
|
201
|
+
const rc = turnEndGateMain();
|
|
202
|
+
return { ms: performance.now() - t0, rc };
|
|
203
|
+
} finally {
|
|
204
|
+
process.stderr.write = write;
|
|
205
|
+
clearHookStdinOverride();
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Every order of the three calls. Cycling through all six puts each call first
|
|
211
|
+
* equally often AND, within a round, gives every call each other call as its
|
|
212
|
+
* direct predecessor equally often; rotating the start of one fixed cycle only
|
|
213
|
+
* does the former. The round-boundary transitions are not balanced.
|
|
214
|
+
*/
|
|
215
|
+
export const ORDERS: readonly (readonly number[])[] = [
|
|
216
|
+
[0, 1, 2],
|
|
217
|
+
[0, 2, 1],
|
|
218
|
+
[1, 0, 2],
|
|
219
|
+
[1, 2, 0],
|
|
220
|
+
[2, 0, 1],
|
|
221
|
+
[2, 1, 0],
|
|
222
|
+
];
|
|
223
|
+
|
|
224
|
+
/** Assigning `undefined` to `process.env` stores the string "undefined"; an unset variable must be deleted. */
|
|
225
|
+
function restoreEnv(key: string, value: string | undefined): void {
|
|
226
|
+
if (value === undefined) delete process.env[key];
|
|
227
|
+
else process.env[key] = value;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
export async function bench(opts: { runs?: number; files?: number }): Promise<LatencyReport> {
|
|
231
|
+
const runs = Math.max(1, opts.runs ?? 30);
|
|
232
|
+
const files = Math.max(1, opts.files ?? 200);
|
|
233
|
+
const root = fs.realpathSync(fs.mkdtempSync(path.join(os.tmpdir(), 'graph-feeder-latency-')));
|
|
234
|
+
const home = path.join(root, 'home');
|
|
235
|
+
fs.mkdirSync(home);
|
|
236
|
+
const savedEnv = { HOME: process.env.HOME, USERPROFILE: process.env.USERPROFILE, CFG: process.env.EVENT4U_CONFIG_HOME };
|
|
237
|
+
process.env.HOME = home;
|
|
238
|
+
process.env.USERPROFILE = home;
|
|
239
|
+
process.env.EVENT4U_CONFIG_HOME = path.join(home, '.event4u', 'agent-config');
|
|
240
|
+
try {
|
|
241
|
+
const withDir = await makeFixture(root, files, true);
|
|
242
|
+
const withoutDir = await makeFixture(root, files, false);
|
|
243
|
+
const tWith = writeTranscript(withDir, home, 'with');
|
|
244
|
+
const tWithout = writeTranscript(withoutDir, home, 'without');
|
|
245
|
+
const state = graphState(withDir);
|
|
246
|
+
const stateWithout = graphState(withoutDir);
|
|
247
|
+
|
|
248
|
+
const edit = [path.join(withDir, 'src', 'service.ts')];
|
|
249
|
+
const feederCall = (): { ms: number; verdict: string | null } => {
|
|
250
|
+
const t0 = performance.now();
|
|
251
|
+
const v = graphUntestedVerdict(withDir, graphState(withDir), edit).verdict;
|
|
252
|
+
return { ms: performance.now() - t0, verdict: v };
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
// Warm every path once so module-level initialisation is not billed to round one.
|
|
256
|
+
timeStop(withDir, tWith, 'warm-with');
|
|
257
|
+
timeStop(withoutDir, tWithout, 'warm-without');
|
|
258
|
+
feederCall();
|
|
259
|
+
|
|
260
|
+
const a: number[] = [];
|
|
261
|
+
const b: number[] = [];
|
|
262
|
+
const feeder: number[] = [];
|
|
263
|
+
const feederVerdicts: (string | null)[] = [];
|
|
264
|
+
const exitCodes = { with: [] as number[], without: [] as number[] };
|
|
265
|
+
for (let i = 0; i < runs; i++) {
|
|
266
|
+
const calls = [
|
|
267
|
+
(): void => {
|
|
268
|
+
const x = timeStop(withDir, tWith, `with-${i}`);
|
|
269
|
+
a.push(x.ms);
|
|
270
|
+
if (!exitCodes.with.includes(x.rc)) exitCodes.with.push(x.rc);
|
|
271
|
+
},
|
|
272
|
+
(): void => {
|
|
273
|
+
const y = timeStop(withoutDir, tWithout, `without-${i}`);
|
|
274
|
+
b.push(y.ms);
|
|
275
|
+
if (!exitCodes.without.includes(y.rc)) exitCodes.without.push(y.rc);
|
|
276
|
+
},
|
|
277
|
+
(): void => {
|
|
278
|
+
const f = feederCall();
|
|
279
|
+
feeder.push(f.ms);
|
|
280
|
+
if (!feederVerdicts.includes(f.verdict)) feederVerdicts.push(f.verdict);
|
|
281
|
+
},
|
|
282
|
+
];
|
|
283
|
+
for (const k of ORDERS[i % ORDERS.length] ?? []) calls[k]?.();
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
const w = distribution(a);
|
|
287
|
+
const wo = distribution(b);
|
|
288
|
+
const round = (x: number): number => Math.round(x * 100) / 100;
|
|
289
|
+
return {
|
|
290
|
+
runs,
|
|
291
|
+
files,
|
|
292
|
+
graphState: state,
|
|
293
|
+
graphStateWithout: stateWithout,
|
|
294
|
+
exitCodes,
|
|
295
|
+
with: w,
|
|
296
|
+
without: wo,
|
|
297
|
+
delta: { p50: round(w.p50 - wo.p50), p95: round(w.p95 - wo.p95) },
|
|
298
|
+
feederOnly: distribution(feeder),
|
|
299
|
+
feederVerdicts,
|
|
300
|
+
node: process.version,
|
|
301
|
+
platform: `${os.platform()} ${os.release()} ${os.arch()}`,
|
|
302
|
+
cpu: `${os.cpus()[0]?.model ?? 'unknown'} x${os.cpus().length}`,
|
|
303
|
+
};
|
|
304
|
+
} finally {
|
|
305
|
+
restoreEnv('HOME', savedEnv.HOME);
|
|
306
|
+
restoreEnv('USERPROFILE', savedEnv.USERPROFILE);
|
|
307
|
+
restoreEnv('EVENT4U_CONFIG_HOME', savedEnv.CFG);
|
|
308
|
+
fs.rmSync(root, { recursive: true, force: true });
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
export interface RepoReport {
|
|
313
|
+
runs: number;
|
|
314
|
+
repo: string;
|
|
315
|
+
edit: string;
|
|
316
|
+
graphState: string;
|
|
317
|
+
/** Every distinct verdict across the billed rounds — a swallowed load failure reads `null`. */
|
|
318
|
+
verdicts: (string | null)[];
|
|
319
|
+
feederOnly: Distribution;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* The feeder's work alone over an existing repository's graph. Writes no feeder
|
|
324
|
+
* row and calls no hook, but it is NOT read-only: `graphUntestedVerdict` ->
|
|
325
|
+
* `loadGraph` re-emits the graph cache's SQLite twin (`sqliteTwinPath`) into the
|
|
326
|
+
* measured repository whenever no valid twin exists, and the unbilled warm-up
|
|
327
|
+
* round absorbs that write, so billed rounds price whatever path the twin left
|
|
328
|
+
* (twin-backed when the emit succeeded, the JSON path when it failed).
|
|
329
|
+
*/
|
|
330
|
+
export function benchRepo(opts: { repo: string; edit: string; runs?: number | undefined }): RepoReport {
|
|
331
|
+
const runs = Math.max(1, opts.runs ?? 30);
|
|
332
|
+
const repo = path.resolve(opts.repo);
|
|
333
|
+
const edit = [path.resolve(repo, opts.edit)];
|
|
334
|
+
let state = graphState(repo);
|
|
335
|
+
const verdicts: (string | null)[] = [];
|
|
336
|
+
const samples: number[] = [];
|
|
337
|
+
for (let i = 0; i <= runs; i++) {
|
|
338
|
+
const t0 = performance.now();
|
|
339
|
+
state = graphState(repo);
|
|
340
|
+
const verdict = graphUntestedVerdict(repo, state, edit).verdict;
|
|
341
|
+
// Round zero is the warm-up and is not billed.
|
|
342
|
+
if (i > 0) {
|
|
343
|
+
samples.push(performance.now() - t0);
|
|
344
|
+
if (!verdicts.includes(verdict)) verdicts.push(verdict);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
return { runs, repo: path.basename(repo), edit: opts.edit, graphState: state, verdicts, feederOnly: distribution(samples) };
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
export interface LoadSplitReport {
|
|
351
|
+
runs: number;
|
|
352
|
+
graph: string;
|
|
353
|
+
bytes: number;
|
|
354
|
+
nodes: number;
|
|
355
|
+
edges: number;
|
|
356
|
+
/** `loadGraph`'s JSON path, stage by stage, each a distribution over the billed rounds. */
|
|
357
|
+
stages: { read: Distribution; parse: Distribution; validate: Distribution; twinEmit: Distribution; maps: Distribution; lexical: Distribution };
|
|
358
|
+
/** Billed rounds in which `emitSqliteTwin` returned true; 0 means every stop pays the attempt again. */
|
|
359
|
+
twinWritten: number;
|
|
360
|
+
/** The whole `loadGraph` over the same copy with its twin removed first: the term the stages decompose. */
|
|
361
|
+
loadGraph: Distribution;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
/**
|
|
365
|
+
* Decompose `loadGraph`'s JSON path (`code_graph/query.ts`) over a COPY of a
|
|
366
|
+
* graph cache, so the measured repository is never written. Each round removes
|
|
367
|
+
* the copy's twin first, then times the stages `loadGraph` runs when no valid
|
|
368
|
+
* twin exists: read, `JSON.parse`, `validateGraph`, `emitSqliteTwin`, and the
|
|
369
|
+
* node and edge maps, and the lexical index. The stages re-implement that sequence
|
|
370
|
+
* rather than instrument it, so the whole `loadGraph` is timed in the same round
|
|
371
|
+
* as the check that the split accounts for the term it names. Foreign-graph
|
|
372
|
+
* adaptation is not timed: a native cache never takes it.
|
|
373
|
+
*/
|
|
374
|
+
export function benchLoadSplit(opts: { graph: string; runs?: number | undefined }): LoadSplitReport {
|
|
375
|
+
const runs = Math.max(1, opts.runs ?? 5);
|
|
376
|
+
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'feeder-load-split-'));
|
|
377
|
+
const copy = path.join(root, 'code-graph-v1.json');
|
|
378
|
+
fs.copyFileSync(opts.graph, copy);
|
|
379
|
+
const twin = sqliteTwinPath(copy);
|
|
380
|
+
const read: number[] = [];
|
|
381
|
+
const parse: number[] = [];
|
|
382
|
+
const validate: number[] = [];
|
|
383
|
+
const twinEmit: number[] = [];
|
|
384
|
+
const maps: number[] = [];
|
|
385
|
+
const lexical: number[] = [];
|
|
386
|
+
const whole: number[] = [];
|
|
387
|
+
let twinWritten = 0;
|
|
388
|
+
let nodes = 0;
|
|
389
|
+
let edges = 0;
|
|
390
|
+
const push = (m: Map<string, CodeEdge[]>, k: string, e: CodeEdge): void => {
|
|
391
|
+
const arr = m.get(k);
|
|
392
|
+
if (arr) arr.push(e);
|
|
393
|
+
else m.set(k, [e]);
|
|
394
|
+
};
|
|
395
|
+
try {
|
|
396
|
+
for (let i = 0; i <= runs; i++) {
|
|
397
|
+
fs.rmSync(twin, { force: true });
|
|
398
|
+
const t0 = performance.now();
|
|
399
|
+
const raw = fs.readFileSync(copy, 'utf-8');
|
|
400
|
+
const t1 = performance.now();
|
|
401
|
+
const parsed = JSON.parse(raw) as unknown;
|
|
402
|
+
const t2 = performance.now();
|
|
403
|
+
const v = validateGraph(parsed);
|
|
404
|
+
if (!v.ok) throw new Error(`invalid graph at ${opts.graph}: ${v.errors.slice(0, 3).join('; ')}`);
|
|
405
|
+
const graph = parsed as CodeGraph;
|
|
406
|
+
const t3 = performance.now();
|
|
407
|
+
const wrote = emitSqliteTwin(graph, raw, copy);
|
|
408
|
+
const t4 = performance.now();
|
|
409
|
+
const byId = new Map(graph.nodes.map((n) => [n.id, n]));
|
|
410
|
+
const outM = new Map<string, CodeEdge[]>();
|
|
411
|
+
const inM = new Map<string, CodeEdge[]>();
|
|
412
|
+
for (const e of graph.edges) {
|
|
413
|
+
push(outM, e.source, e);
|
|
414
|
+
push(inM, e.target, e);
|
|
415
|
+
}
|
|
416
|
+
const t45 = performance.now();
|
|
417
|
+
new LexicalIndex(graph.nodes.map((n) => ({ id: n.id, text: `${n.label} ${n.id}` })));
|
|
418
|
+
const t5 = performance.now();
|
|
419
|
+
fs.rmSync(twin, { force: true });
|
|
420
|
+
const t6 = performance.now();
|
|
421
|
+
loadGraph(copy).close();
|
|
422
|
+
const t7 = performance.now();
|
|
423
|
+
void byId;
|
|
424
|
+
nodes = graph.nodes.length;
|
|
425
|
+
edges = graph.edges.length;
|
|
426
|
+
// Round zero is the warm-up and is not billed.
|
|
427
|
+
if (i === 0) continue;
|
|
428
|
+
read.push(t1 - t0);
|
|
429
|
+
parse.push(t2 - t1);
|
|
430
|
+
validate.push(t3 - t2);
|
|
431
|
+
twinEmit.push(t4 - t3);
|
|
432
|
+
maps.push(t45 - t4);
|
|
433
|
+
lexical.push(t5 - t45);
|
|
434
|
+
whole.push(t7 - t6);
|
|
435
|
+
if (wrote) twinWritten++;
|
|
436
|
+
}
|
|
437
|
+
} finally {
|
|
438
|
+
fs.rmSync(root, { recursive: true, force: true });
|
|
439
|
+
}
|
|
440
|
+
return {
|
|
441
|
+
runs,
|
|
442
|
+
graph: path.basename(opts.graph),
|
|
443
|
+
bytes: fs.statSync(opts.graph).size,
|
|
444
|
+
nodes,
|
|
445
|
+
edges,
|
|
446
|
+
stages: {
|
|
447
|
+
read: distribution(read),
|
|
448
|
+
parse: distribution(parse),
|
|
449
|
+
validate: distribution(validate),
|
|
450
|
+
twinEmit: distribution(twinEmit),
|
|
451
|
+
maps: distribution(maps),
|
|
452
|
+
lexical: distribution(lexical),
|
|
453
|
+
},
|
|
454
|
+
twinWritten,
|
|
455
|
+
loadGraph: distribution(whole),
|
|
456
|
+
};
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
function renderLoadSplit(r: LoadSplitReport): string {
|
|
460
|
+
const row = (name: string, d: Distribution): string => `| ${name} | ${d.n} | ${d.p50} | ${d.p95} | ${d.max} |`;
|
|
461
|
+
return [
|
|
462
|
+
`loadGraph JSON path over a copy of ${r.graph}: ${r.bytes} bytes, ${r.nodes} nodes, ${r.edges} edges, ${r.runs} billed rounds`,
|
|
463
|
+
'',
|
|
464
|
+
'| stage | n | p50 ms | p95 ms | max ms |',
|
|
465
|
+
'|---|---|---|---|---|',
|
|
466
|
+
row('read file', r.stages.read),
|
|
467
|
+
row('JSON.parse', r.stages.parse),
|
|
468
|
+
row('validateGraph', r.stages.validate),
|
|
469
|
+
row('emitSqliteTwin', r.stages.twinEmit),
|
|
470
|
+
row('node + edge maps', r.stages.maps),
|
|
471
|
+
row('lexical index', r.stages.lexical),
|
|
472
|
+
row('whole loadGraph', r.loadGraph),
|
|
473
|
+
'',
|
|
474
|
+
`twin written in ${r.twinWritten} of ${r.runs} rounds`,
|
|
475
|
+
].join('\n');
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
function renderRepo(r: RepoReport): string {
|
|
479
|
+
const d = r.feederOnly;
|
|
480
|
+
return [
|
|
481
|
+
`graph-feeder work over ${r.repo} — edit ${r.edit}, graph state ${r.graphState}, verdicts ${JSON.stringify(r.verdicts)}`,
|
|
482
|
+
`n ${d.n} · p50 ${d.p50} ms · p95 ${d.p95} ms · max ${d.max} ms`,
|
|
483
|
+
// A stop follows an edit, so the realistic state is `edited`; on a clean
|
|
484
|
+
// tree the git probes answer differently and the reading prices that.
|
|
485
|
+
...(r.graphState === 'edited' ? [] : [`note: graph state is ${r.graphState}, not edited — a stop normally follows an uncommitted edit`]),
|
|
486
|
+
].join('\n');
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
function render(r: LatencyReport): string {
|
|
490
|
+
const row = (name: string, d: Distribution): string =>
|
|
491
|
+
`| ${name} | ${d.n} | ${d.p50} | ${d.p95} | ${d.max} |`;
|
|
492
|
+
return [
|
|
493
|
+
`graph-feeder latency — ${r.runs} rounds, ${r.files} generated modules, graph state ${r.graphState} (without arm: ${r.graphStateWithout})`,
|
|
494
|
+
`machine: ${r.cpu}, ${r.platform}, node ${r.node}`,
|
|
495
|
+
'',
|
|
496
|
+
'| arm | n | p50 ms | p95 ms | max ms |',
|
|
497
|
+
'|---|---|---|---|---|',
|
|
498
|
+
row('stop hook, with feeder', r.with),
|
|
499
|
+
row('stop hook, without feeder', r.without),
|
|
500
|
+
row('feeder work alone', r.feederOnly),
|
|
501
|
+
'',
|
|
502
|
+
`delta: p50 ${r.delta.p50} ms, p95 ${r.delta.p95} ms`,
|
|
503
|
+
`exit codes: with ${JSON.stringify(r.exitCodes.with)}, without ${JSON.stringify(r.exitCodes.without)}`,
|
|
504
|
+
`feeder verdicts: ${JSON.stringify(r.feederVerdicts)}`,
|
|
505
|
+
].join('\n');
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
export interface Args {
|
|
509
|
+
runs?: number;
|
|
510
|
+
files?: number;
|
|
511
|
+
repo?: string;
|
|
512
|
+
edit?: string;
|
|
513
|
+
loadSplit?: string;
|
|
514
|
+
format: 'text' | 'json';
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
export function parseArgs(argv: readonly string[]): Args | null {
|
|
518
|
+
const out: Args = { format: 'text' };
|
|
519
|
+
for (let i = 0; i < argv.length; i++) {
|
|
520
|
+
const a = argv[i];
|
|
521
|
+
const v = argv[i + 1];
|
|
522
|
+
if (a === '--runs' || a === '--files') {
|
|
523
|
+
const n = Number(v);
|
|
524
|
+
if (!Number.isInteger(n) || n < 1) return null;
|
|
525
|
+
if (a === '--runs') out.runs = n;
|
|
526
|
+
else out.files = n;
|
|
527
|
+
i++;
|
|
528
|
+
} else if ((a === '--repo' || a === '--edit') && v !== undefined && v !== '' && !v.startsWith('--')) {
|
|
529
|
+
if (a === '--repo') out.repo = v;
|
|
530
|
+
else out.edit = v;
|
|
531
|
+
i++;
|
|
532
|
+
} else if (a === '--load-split' && v !== undefined && v !== '' && !v.startsWith('--')) {
|
|
533
|
+
out.loadSplit = v;
|
|
534
|
+
i++;
|
|
535
|
+
} else if (a === '--format' && (v === 'text' || v === 'json')) {
|
|
536
|
+
out.format = v;
|
|
537
|
+
i++;
|
|
538
|
+
} else {
|
|
539
|
+
return null;
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
// Both or neither: a repository with no edit to price is not a reading.
|
|
543
|
+
if ((out.repo === undefined) !== (out.edit === undefined)) return null;
|
|
544
|
+
// `--files` sizes the generated fixture, which `--repo` mode never builds.
|
|
545
|
+
if (out.repo !== undefined && out.files !== undefined) return null;
|
|
546
|
+
// `--load-split` reads one graph file; it neither builds a fixture nor prices an edit.
|
|
547
|
+
if (out.loadSplit !== undefined && (out.repo !== undefined || out.files !== undefined)) return null;
|
|
548
|
+
return out;
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
async function cli(): Promise<number> {
|
|
552
|
+
const args = parseArgs(process.argv.slice(2));
|
|
553
|
+
if (args === null) {
|
|
554
|
+
process.stderr.write(
|
|
555
|
+
'usage: bench_graph_feeder_latency [--runs N] [--files N] [--format text|json]\n' +
|
|
556
|
+
' bench_graph_feeder_latency --repo P --edit F [--runs N] [--format text|json]\n' +
|
|
557
|
+
' bench_graph_feeder_latency --load-split GRAPH_JSON [--runs N] [--format text|json]\n',
|
|
558
|
+
);
|
|
559
|
+
return 2;
|
|
560
|
+
}
|
|
561
|
+
if (args.loadSplit !== undefined) {
|
|
562
|
+
const r = benchLoadSplit({ graph: args.loadSplit, runs: args.runs });
|
|
563
|
+
process.stdout.write(args.format === 'json' ? `${JSON.stringify(r, null, 2)}\n` : `${renderLoadSplit(r)}\n`);
|
|
564
|
+
return 0;
|
|
565
|
+
}
|
|
566
|
+
if (args.repo !== undefined && args.edit !== undefined) {
|
|
567
|
+
const r = benchRepo({ repo: args.repo, edit: args.edit, runs: args.runs });
|
|
568
|
+
process.stdout.write(args.format === 'json' ? `${JSON.stringify(r, null, 2)}\n` : `${renderRepo(r)}\n`);
|
|
569
|
+
return 0;
|
|
570
|
+
}
|
|
571
|
+
const r = await bench(args);
|
|
572
|
+
process.stdout.write(args.format === 'json' ? `${JSON.stringify(r, null, 2)}\n` : `${render(r)}\n`);
|
|
573
|
+
return 0;
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
if (process.argv[1] !== undefined && import.meta.url === pathToFileURL(path.resolve(process.argv[1])).href) {
|
|
577
|
+
cli().then(
|
|
578
|
+
(rc) => process.exit(rc),
|
|
579
|
+
(e: unknown) => {
|
|
580
|
+
process.stderr.write(`bench_graph_feeder_latency: ${String(e)}\n`);
|
|
581
|
+
process.exit(2);
|
|
582
|
+
},
|
|
583
|
+
);
|
|
584
|
+
}
|
|
@@ -78,6 +78,23 @@ const SKIP_DIRS: ReadonlySet<string> = new Set([
|
|
|
78
78
|
|
|
79
79
|
/** Frontmatter keys that only a roadmap carries in this tree. */
|
|
80
80
|
const FM_ROADMAP_RE = /^complexity:[ \t]*(lightweight|structural)[ \t]*$/m;
|
|
81
|
+
/**
|
|
82
|
+
* Anchored at the absolute start of the string — `^` with no `m` flag — on
|
|
83
|
+
* purpose: this is a FRONTMATTER fence, which by the project's own convention
|
|
84
|
+
* only means something at byte offset 0 of the file. A `---` fence that
|
|
85
|
+
* begins three lines in is not frontmatter, it is a horizontal rule or a
|
|
86
|
+
* quoted example, so loosening this to `m` would create false positives of
|
|
87
|
+
* its own.
|
|
88
|
+
*
|
|
89
|
+
* The R2 review-input roadmap snapshots used to depend on that strictness as an
|
|
90
|
+
* INCIDENTAL exemption: their two-line HTML-comment header, written for a
|
|
91
|
+
* different gate, pushed this fence off offset 0. They are now exempt by path —
|
|
92
|
+
* see `isReviewInputRoadmapSnapshot` — so neither the header nor this regex's
|
|
93
|
+
* anchoring decides their fate any more.
|
|
94
|
+
*
|
|
95
|
+
* Tested in `check_agent_artifact_location.test.ts`
|
|
96
|
+
* (describe: "review-input snapshot convention").
|
|
97
|
+
*/
|
|
81
98
|
const FM_RE = /^---\n([\s\S]*?)\n---/;
|
|
82
99
|
const PHASE_RE = /^##[ \t]+Phase[ \t]/m;
|
|
83
100
|
const CHECKBOX_RE = /^[ \t]*- \[[ x~-]\][ \t]/m;
|
|
@@ -144,6 +161,24 @@ function walkMarkdown(root: string): string[] {
|
|
|
144
161
|
return out.sort();
|
|
145
162
|
}
|
|
146
163
|
|
|
164
|
+
/**
|
|
165
|
+
* The one declared exemption: an R2 review-input roadmap snapshot.
|
|
166
|
+
*
|
|
167
|
+
* `dispatch_r2_reviewer.ts` writes a verbatim roadmap copy to exactly this
|
|
168
|
+
* shape, and a file in it is non-authoritative evidence whoever wrote it —
|
|
169
|
+
* nothing reads it back as a roadmap. Decided by path, not by a marker an
|
|
170
|
+
* author could type anywhere (authority-routing roadmap 5.1, `## Decisions`):
|
|
171
|
+
* measured 2026-10-07, every roadmap-shaped file outside the root was one of
|
|
172
|
+
* these snapshots, so the path names the whole population and nothing else.
|
|
173
|
+
* Takes the repository-relative POSIX path the walker produces; the walker
|
|
174
|
+
* never follows a symlink, so a link cannot manufacture an exempt path.
|
|
175
|
+
*/
|
|
176
|
+
const REVIEW_INPUT_SNAPSHOT_PATH_RE = /^agents\/evidence\/(?:[^/]+\/)*[^/.][^/]*\.review-input\/roadmap\.md$/;
|
|
177
|
+
|
|
178
|
+
export function isReviewInputRoadmapSnapshot(rel: string): boolean {
|
|
179
|
+
return REVIEW_INPUT_SNAPSHOT_PATH_RE.test(rel) && !rel.split('/').some((s) => s === '..' || s === '.');
|
|
180
|
+
}
|
|
181
|
+
|
|
147
182
|
/** `true` when `child` is at or below `root`. */
|
|
148
183
|
function isUnder(child: string, root: string): boolean {
|
|
149
184
|
const rel = path.relative(root, child);
|
|
@@ -156,6 +191,7 @@ export function scan(root: string): ScanResult {
|
|
|
156
191
|
const findings: Finding[] = [];
|
|
157
192
|
for (const f of files) {
|
|
158
193
|
if (isUnder(f, roadmapRoot)) continue;
|
|
194
|
+
if (isReviewInputRoadmapSnapshot(path.relative(root, f).split(path.sep).join('/'))) continue;
|
|
159
195
|
// Cheap name pre-filter first — the SHARED definition, imported, so the
|
|
160
196
|
// roadmap corpus and this guard cannot drift apart.
|
|
161
197
|
if (!isRoadmapCandidate(f)) continue;
|