@event4u/agent-config 9.9.0 → 9.10.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/.claude-plugin/marketplace.json +1 -1
- package/CHANGELOG.md +153 -194
- package/README.md +1 -1
- package/dist/agent-src/commands/agent-status.md +12 -11
- package/dist/agent-src/commands/agents/user/accept.md +115 -30
- package/dist/agent-src/commands/agents/user/delete.md +147 -0
- package/dist/agent-src/commands/agents/user/review.md +65 -28
- package/dist/agent-src/commands/agents/user/show.md +82 -29
- package/dist/agent-src/commands/agents/user/update.md +2 -2
- package/dist/agent-src/commands/agents/user.md +16 -3
- package/dist/agent-src/commands/check-current-md.md +0 -10
- package/dist/agent-src/commands/condense.md +28 -97
- package/dist/agent-src/commands/cost/report.md +7 -0
- package/dist/agent-src/commands/fix/portability.md +0 -8
- package/dist/agent-src/commands/fix/refs.md +0 -8
- package/dist/agent-src/commands/humanize.md +2 -2
- package/dist/agent-src/commands/jira-ticket.md +1 -1
- package/dist/agent-src/commands/memory/load.md +11 -0
- package/dist/agent-src/commands/optimize/skills.md +1 -1
- package/dist/agent-src/commands/pr/create.md +4 -4
- package/dist/agent-src/commands/profile/show.md +3 -3
- package/dist/agent-src/commands/rule-compliance-audit.md +5 -4
- package/dist/agent-src/commands/security-audit-config.md +1 -1
- package/dist/agent-src/commands/sync/gitignore/fix.md +1 -1
- package/dist/agent-src/commands/tdd.md +21 -0
- package/dist/agent-src/commands/team/delegate.md +2 -2
- package/dist/agent-src/commands/team-knowledge/bootstrap.md +9 -8
- package/dist/agent-src/commands/team-knowledge/consolidate.md +8 -7
- package/dist/agent-src/commands/team.md +4 -1
- package/dist/agent-src/commands/upstream-contribute.md +0 -1
- package/dist/agent-src/contexts/authority/commit-mechanics.md +7 -0
- package/dist/agent-src/contexts/authority/destructive-mechanics.md +16 -0
- package/dist/agent-src/contexts/authority/scope-mechanics.md +61 -0
- package/dist/agent-src/contexts/communication/rules-auto/reply-close-mechanics.md +20 -18
- package/dist/agent-src/contexts/communication/rules-auto/skill-quality-mechanics.md +1 -1
- package/dist/agent-src/contexts/communication/rules-auto/slash-command-routing-policy-mechanics.md +1 -1
- package/dist/agent-src/contexts/communication/rules-auto/source-of-truth-mechanics.md +9 -10
- package/dist/agent-src/contexts/communication/rules-auto/think-before-action-mechanics.md +1 -1
- package/dist/agent-src/contexts/communication/rules-auto/user-interaction-mechanics.md +11 -0
- package/dist/agent-src/contexts/contracts/artifact-engagement-flow.md +26 -15
- package/dist/agent-src/contexts/contracts/command-suggestion-flow.md +3 -0
- package/dist/agent-src/contexts/execution/auto-dispatch-classification.md +67 -37
- package/dist/agent-src/contexts/execution/auto-orchestration-activation.md +1 -1
- package/dist/agent-src/contexts/execution/autonomy-examples.md +1 -1
- package/dist/agent-src/contexts/execution/autonomy-mechanics.md +7 -6
- package/dist/agent-src/contexts/execution/evidence-discipline.md +92 -77
- package/dist/agent-src/contexts/execution/orchestration-benchmark-gate.md +22 -21
- package/dist/agent-src/contexts/execution/orchestration-telemetry.md +24 -0
- package/dist/agent-src/contexts/execution/project-intelligence.md +122 -103
- package/dist/agent-src/contexts/execution/roadmap-execution-contract.md +23 -21
- package/dist/agent-src/contexts/execution/subagent-response-contract.md +5 -5
- package/dist/agent-src/contexts/execution/subagent-routing.md +11 -0
- package/dist/agent-src/contexts/execution/subagent-spawn-contract.md +13 -13
- package/dist/agent-src/contexts/execution/subagent-steering.md +36 -34
- package/dist/agent-src/contexts/execution/toolchain-resolver.md +1 -1
- package/dist/agent-src/contexts/execution/user-memory-channels.md +189 -0
- package/dist/agent-src/contexts/execution/verification-mechanics.md +9 -0
- package/dist/agent-src/contexts/model-recommendations.md +3 -2
- package/dist/agent-src/contexts/subagent-configuration.md +3 -3
- package/dist/agent-src/ghostwriter/README.md +2 -2
- package/dist/agent-src/packs/README.md +1 -1
- package/dist/agent-src/personas/README.md +5 -5
- package/dist/agent-src/personas/_template-specialist/persona.md +3 -3
- package/dist/agent-src/personas/backend-architect.md +24 -23
- package/dist/agent-src/personas/eloquent-tamer.md +30 -30
- package/dist/agent-src/personas/frontend-engineer.md +30 -30
- package/dist/agent-src/personas/product-owner.md +41 -38
- package/dist/agent-src/personas/qa.md +13 -13
- package/dist/agent-src/personas/security-engineer.md +22 -22
- package/dist/agent-src/presets/README.md +3 -3
- package/dist/agent-src/profiles/README.md +3 -3
- package/dist/agent-src/rules/active-remediation.md +2 -2
- package/dist/agent-src/rules/artifact-engagement-recording.md +4 -4
- package/dist/agent-src/rules/ask-when-uncertain.md +0 -4
- package/dist/agent-src/rules/augment-edit-discipline.md +1 -1
- package/dist/agent-src/rules/broken-access-control.md +4 -4
- package/dist/agent-src/rules/code-comment-discipline.md +1 -1
- package/dist/agent-src/rules/code-provenance.md +2 -2
- package/dist/agent-src/rules/commit-policy.md +4 -9
- package/dist/agent-src/rules/communication-through-line.md +5 -5
- package/dist/agent-src/rules/decision-revisit-gate.md +1 -1
- package/dist/agent-src/rules/doc-screenshot-hygiene.md +1 -1
- package/dist/agent-src/rules/domain-adoption-policy.md +1 -1
- package/dist/agent-src/rules/domain-safety-pii.md +1 -1
- package/dist/agent-src/rules/external-code-graph-interop.md +1 -1
- package/dist/agent-src/rules/fast-path-marker-visibility.md +43 -7
- package/dist/agent-src/rules/finance-safety-floor.md +1 -1
- package/dist/agent-src/rules/framework-neutrality-in-generic-skills.md +3 -3
- package/dist/agent-src/rules/history-discipline.md +1 -1
- package/dist/agent-src/rules/legal-safety-floor.md +1 -1
- package/dist/agent-src/rules/lethal-trifecta-guard.md +5 -5
- package/dist/agent-src/rules/low-impact-corpus-privacy-floor.md +10 -8
- package/dist/agent-src/rules/media-governance-routing.md +13 -13
- package/dist/agent-src/rules/media-sync-ground-truth.md +29 -11
- package/dist/agent-src/rules/no-cheap-questions.md +7 -7
- package/dist/agent-src/rules/no-decorative-emojis-in-git-surfaces.md +29 -18
- package/dist/agent-src/rules/non-destructive-by-default.md +5 -23
- package/dist/agent-src/rules/onboarding-gate.md +14 -4
- package/dist/agent-src/rules/persona-governance.md +2 -2
- package/dist/agent-src/rules/prefer-enums-over-literals.md +9 -9
- package/dist/agent-src/rules/preservation-guard.md +10 -2
- package/dist/agent-src/rules/question-not-instruction.md +1 -1
- package/dist/agent-src/rules/roadmap-progress-sync.md +6 -6
- package/dist/agent-src/rules/rule-type-governance.md +1 -1
- package/dist/agent-src/rules/scale-discipline.md +1 -1
- package/dist/agent-src/rules/scope-control.md +1 -1
- package/dist/agent-src/rules/secret-vcs-guard.md +1 -1
- package/dist/agent-src/rules/security-sensitive-stop.md +17 -11
- package/dist/agent-src/rules/senior-engineering-discipline.md +17 -17
- package/dist/agent-src/rules/session-canary.md +1 -1
- package/dist/agent-src/rules/skill-quality.md +1 -1
- package/dist/agent-src/rules/source-confidentiality.md +21 -19
- package/dist/agent-src/rules/source-of-truth.md +6 -5
- package/dist/agent-src/rules/spreadsheet-source-quality.md +2 -2
- package/dist/agent-src/rules/strategy-safety-floor.md +1 -1
- package/dist/agent-src/rules/think-before-action.md +5 -5
- package/dist/agent-src/rules/token-budget-discipline.md +5 -3
- package/dist/agent-src/rules/token-efficiency.md +2 -2
- package/dist/agent-src/rules/untrusted-input-defense.md +4 -4
- package/dist/agent-src/rules/user-interaction.md +12 -0
- package/dist/agent-src/rules/verify-before-complete.md +4 -24
- package/dist/agent-src/skills/adversarial-review/SKILL.md +2 -2
- package/dist/agent-src/skills/agent-security-review/SKILL.md +52 -45
- package/dist/agent-src/skills/agents-md-thin-root/SKILL.md +2 -2
- package/dist/agent-src/skills/ai-code-blindspots/SKILL.md +20 -20
- package/dist/agent-src/skills/ai-council/SKILL.md +185 -128
- package/dist/agent-src/skills/analysis-skill-router/SKILL.md +3 -3
- package/dist/agent-src/skills/api-endpoint/SKILL.md +2 -2
- package/dist/agent-src/skills/artisan-commands/SKILL.md +2 -2
- package/dist/agent-src/skills/async-python-patterns/SKILL.md +8 -7
- package/dist/agent-src/skills/authz-review/SKILL.md +16 -15
- package/dist/agent-src/skills/aws-infrastructure/SKILL.md +5 -5
- package/dist/agent-src/skills/blade-ui/SKILL.md +1 -1
- package/dist/agent-src/skills/bug-analyzer/SKILL.md +30 -12
- package/dist/agent-src/skills/code-intelligence/SKILL.md +0 -1
- package/dist/agent-src/skills/code-intelligence/evals/triggers.json +52 -0
- package/dist/agent-src/skills/code-review/SKILL.md +6 -6
- package/dist/agent-src/skills/command-routing/SKILL.md +5 -4
- package/dist/agent-src/skills/composer-packages/SKILL.md +2 -2
- package/dist/agent-src/skills/condense-memory/SKILL.md +43 -41
- package/dist/agent-src/skills/context-document/SKILL.md +23 -21
- package/dist/agent-src/skills/copilot-config/SKILL.md +6 -1
- package/dist/agent-src/skills/decision-record/SKILL.md +20 -18
- package/dist/agent-src/skills/design-intelligence/SKILL.md +4 -4
- package/dist/agent-src/skills/design-review/SKILL.md +15 -14
- package/dist/agent-src/skills/design-system-capture/reference/design-system-json.md +28 -25
- package/dist/agent-src/skills/devcontainer/SKILL.md +2 -2
- package/dist/agent-src/skills/docker/SKILL.md +1 -1
- package/dist/agent-src/skills/docx-authoring/evals/triggers.json +52 -0
- package/dist/agent-src/skills/emit-tickets/SKILL.md +2 -2
- package/dist/agent-src/skills/error-handling-patterns/SKILL.md +1 -1
- package/dist/agent-src/skills/estimate-ticket/SKILL.md +2 -2
- package/dist/agent-src/skills/evaluate-llm-feature/SKILL.md +0 -1
- package/dist/agent-src/skills/evaluate-llm-feature/evals/triggers.json +52 -0
- package/dist/agent-src/skills/existing-ui-audit/SKILL.md +6 -5
- package/dist/agent-src/skills/feature-planning/SKILL.md +2 -2
- package/dist/agent-src/skills/finishing-a-development-branch/SKILL.md +3 -3
- package/dist/agent-src/skills/flux/SKILL.md +1 -1
- package/dist/agent-src/skills/frontend-render-security/SKILL.md +13 -13
- package/dist/agent-src/skills/git-workflow/SKILL.md +33 -33
- package/dist/agent-src/skills/grafana/SKILL.md +5 -0
- package/dist/agent-src/skills/humanizer/SKILL.md +83 -66
- package/dist/agent-src/skills/humanizer/data/patterns.md +14 -14
- package/dist/agent-src/skills/humanizer/references/anti-aiisms.md +4 -4
- package/dist/agent-src/skills/judge-artifact-completeness/SKILL.md +82 -37
- package/dist/agent-src/skills/judge-bug-hunter/SKILL.md +17 -5
- package/dist/agent-src/skills/judge-injection-defense/SKILL.md +63 -25
- package/dist/agent-src/skills/judge-security-auditor/SKILL.md +2 -0
- package/dist/agent-src/skills/judge-synthesis/SKILL.md +72 -28
- package/dist/agent-src/skills/laravel-horizon/SKILL.md +5 -0
- package/dist/agent-src/skills/laravel-mail/SKILL.md +5 -4
- package/dist/agent-src/skills/laravel-middleware/SKILL.md +5 -4
- package/dist/agent-src/skills/laravel-notifications/SKILL.md +10 -9
- package/dist/agent-src/skills/laravel-pennant/SKILL.md +1 -1
- package/dist/agent-src/skills/laravel-pulse/SKILL.md +9 -8
- package/dist/agent-src/skills/laravel-reverb/SKILL.md +6 -5
- package/dist/agent-src/skills/laravel-scheduling/SKILL.md +6 -1
- package/dist/agent-src/skills/laravel-validation/SKILL.md +12 -0
- package/dist/agent-src/skills/launch-readiness/SKILL.md +1 -1
- package/dist/agent-src/skills/learning-to-rule-or-skill/SKILL.md +7 -7
- package/dist/agent-src/skills/learning-tutor/SKILL.md +44 -43
- package/dist/agent-src/skills/learning-tutor/evals/triggers.json +52 -0
- package/dist/agent-src/skills/license-compliance-audit/SKILL.md +7 -7
- package/dist/agent-src/skills/license-compliance-borrow-check/SKILL.md +3 -3
- package/dist/agent-src/skills/license-compliance-credits/SKILL.md +4 -4
- package/dist/agent-src/skills/llm-provider-knowledge/SKILL.md +0 -1
- package/dist/agent-src/skills/memory-consolidation/SKILL.md +69 -64
- package/dist/agent-src/skills/motion-choreographer/SKILL.md +6 -6
- package/dist/agent-src/skills/pdf-tools/evals/triggers.json +52 -0
- package/dist/agent-src/skills/performance-analysis/SKILL.md +7 -3
- package/dist/agent-src/skills/persona-improvement/SKILL.md +0 -2
- package/dist/agent-src/skills/persona-improvement/evals/triggers.json +52 -0
- package/dist/agent-src/skills/php-coder/SKILL.md +10 -0
- package/dist/agent-src/skills/prediction-pool-optimizer/SKILL.md +129 -120
- package/dist/agent-src/skills/prediction-pool-optimizer/reference/ev-fixtures.md +36 -35
- package/dist/agent-src/skills/prediction-pool-optimizer/reference/odds-and-bonus.md +45 -41
- package/dist/agent-src/skills/project-analysis-core/SKILL.md +4 -4
- package/dist/agent-src/skills/project-analysis-hypothesis-driven/SKILL.md +3 -3
- package/dist/agent-src/skills/project-analysis-react/SKILL.md +1 -1
- package/dist/agent-src/skills/project-analysis-symfony/SKILL.md +1 -1
- package/dist/agent-src/skills/project-analysis-zend-laminas/SKILL.md +2 -2
- package/dist/agent-src/skills/prompt-engineering-patterns/SKILL.md +1 -1
- package/dist/agent-src/skills/prompt-validator/SKILL.md +39 -39
- package/dist/agent-src/skills/quality-tools/SKILL.md +15 -15
- package/dist/agent-src/skills/react-shadcn-ui/SKILL.md +55 -19
- package/dist/agent-src/skills/react-shadcn-ui/reference/registry.md +29 -27
- package/dist/agent-src/skills/readme-writing-package/SKILL.md +2 -2
- package/dist/agent-src/skills/reasoning-orchestrator/SKILL.md +25 -25
- package/dist/agent-src/skills/recursive-verification/SKILL.md +6 -5
- package/dist/agent-src/skills/refine-prompt/SKILL.md +1 -1
- package/dist/agent-src/skills/refine-ticket/SKILL.md +20 -2
- package/dist/agent-src/skills/repomix-packer/SKILL.md +1 -1
- package/dist/agent-src/skills/requesting-code-review/SKILL.md +2 -2
- package/dist/agent-src/skills/roadmap-management/SKILL.md +80 -66
- package/dist/agent-src/skills/roadmap-writing/SKILL.md +84 -77
- package/dist/agent-src/skills/rule-refactor/SKILL.md +1 -1
- package/dist/agent-src/skills/screenshot-hygiene/evals/triggers.json +52 -0
- package/dist/agent-src/skills/security-audit/SKILL.md +26 -9
- package/dist/agent-src/skills/security-maturity-assessment/SKILL.md +38 -16
- package/dist/agent-src/skills/skill-improvement-pipeline/SKILL.md +14 -11
- package/dist/agent-src/skills/skill-writing/SKILL.md +9 -8
- package/dist/agent-src/skills/song-to-script/SKILL.md +93 -85
- package/dist/agent-src/skills/sql-writing/SKILL.md +2 -2
- package/dist/agent-src/skills/stakeholder-tradeoff/SKILL.md +4 -3
- package/dist/agent-src/skills/standards-from-config/SKILL.md +61 -43
- package/dist/agent-src/skills/subagent-orchestration/SKILL.md +22 -8
- package/dist/agent-src/skills/subagent-orchestration/prompts/README.md +28 -2
- package/dist/agent-src/skills/supply-chain-intake/SKILL.md +13 -13
- package/dist/agent-src/skills/systematic-debugging/SKILL.md +22 -21
- package/dist/agent-src/skills/tailwind-engineer/SKILL.md +1 -1
- package/dist/agent-src/skills/technical-specification/SKILL.md +5 -0
- package/dist/agent-src/skills/terraform/SKILL.md +2 -2
- package/dist/agent-src/skills/terragrunt/SKILL.md +8 -8
- package/dist/agent-src/skills/testing-anti-patterns/process-anti-patterns.md +1 -1
- package/dist/agent-src/skills/threat-modeling/SKILL.md +19 -13
- package/dist/agent-src/skills/universal-project-analysis/SKILL.md +1 -1
- package/dist/agent-src/skills/upstream-contribute/SKILL.md +0 -1
- package/dist/agent-src/skills/using-git-worktrees/SKILL.md +9 -9
- package/dist/agent-src/skills/validate-feature-fit/SKILL.md +3 -1
- package/dist/agent-src/skills/verify-completion-evidence/SKILL.md +4 -6
- package/dist/agent-src/skills/verify-repair-loop/SKILL.md +67 -61
- package/dist/agent-src/skills/video-director/SKILL.md +7 -7
- package/dist/agent-src/skills/worktree-lifecycle/SKILL.md +3 -3
- package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
- package/dist/agent-src/templates/contexts/knowledge-pages.md +47 -45
- package/dist/agent-src/templates/contexts/lesson-card.md +2 -2
- package/dist/agent-src/templates/contexts/tenant-boundaries.md +2 -2
- package/dist/agent-src/templates/contexts.md +1 -1
- package/dist/agent-src/templates/features.md +1 -1
- package/dist/agent-src/templates/roadmaps.md +125 -116
- package/dist/agent-src/templates/rule.md +2 -1
- package/dist/agent-src/templates/scripts/pr_risk_review.ts +0 -0
- package/dist/agent-src/templates/scripts/telemetry/aggregator.ts +0 -0
- package/dist/agent-src/templates/scripts/tier_usage_report.ts +2 -2
- package/dist/agent-src/templates/scripts/work_engine/directives/ui/polish.ts +2 -2
- package/dist/agent-src/templates/scripts/work_engine/directives/ui/review.ts +1 -1
- package/dist/agent-src/user-types/README.md +8 -8
- package/dist/agent-src/user-types/galabau-field-crew.md +34 -33
- package/dist/agent-src/user-types/metalworking-shop.md +35 -34
- package/dist/agent-src/user-types/truck-driver.md +22 -20
- package/dist/cli/python/workspace_roles.js +2 -2
- package/dist/cli/python/workspace_roles.js.map +1 -1
- package/dist/discovery/deprecation-report.md +1 -1
- package/dist/discovery/discovery-manifest.json +88 -62
- package/dist/discovery/discovery-manifest.json.sha256 +1 -1
- package/dist/discovery/discovery-manifest.summary.md +3 -3
- package/dist/discovery/orphan-report.md +1 -1
- package/dist/discovery/packs.json +6 -5
- package/dist/discovery/trust-report.md +2 -2
- package/dist/discovery/workspaces.json +5 -4
- package/dist/hooks/dispatch.js +186 -12
- package/dist/install/install.mjs +8 -0
- package/dist/mcp/registry-manifest.json +3 -3
- package/dist/mcp/server.json +2 -2
- package/dist/mcp/server.mjs +1199 -857
- package/dist/router.json +1 -1
- package/dist/scripts/_lib/compile_time_toggles.js +48 -0
- package/dist/scripts/_lib/compile_time_toggles.js.map +1 -0
- package/dist/scripts/condense.js +234 -217
- package/dist/scripts/condense.js.map +1 -1
- package/dist/server/schemas/settings.js +4 -0
- package/dist/server/schemas/settings.js.map +1 -1
- package/dist/shared/userMd/utils.js +30 -0
- package/dist/shared/userMd/utils.js.map +1 -1
- package/dist/ui/assets/index-BpSQt5vm.js.map +1 -1
- package/docs/guidelines/agent-infra/api-cost-levers.md +56 -5
- package/docs/guidelines/agent-infra/simplicity-and-goal-demos.md +93 -33
- package/package.json +1 -1
- package/src/agent-src/templates/scripts/pr_risk_review.ts +0 -0
- package/src/agent-src/templates/scripts/telemetry/aggregator.ts +0 -0
- package/src/agent-src/templates/scripts/tier_usage_report.ts +2 -2
- package/src/agent-src/templates/scripts/work_engine/directives/ui/polish.ts +2 -2
- package/src/agent-src/templates/scripts/work_engine/directives/ui/review.ts +1 -1
- package/src/config/agent-settings.template.yml +32 -2
- package/src/config/ci-local-parity.yml +142 -0
- package/src/config/gate-coverage.yml +127 -0
- package/src/config/preamble-payload-budget.json +44 -0
- package/src/scripts/_cli/cmd_doctor.ts +78 -2
- package/src/scripts/_lib/agent_user_profile.ts +381 -0
- package/src/scripts/_lib/agents_overlay.ts +9 -0
- package/src/scripts/_lib/anchor_eval.ts +311 -0
- package/src/scripts/_lib/cc_transcript.ts +316 -0
- package/src/scripts/_lib/compile_time_toggles.ts +79 -0
- package/src/scripts/_lib/confusables.ts +101 -0
- package/src/scripts/_lib/duplicate_scope_census.ts +77 -0
- package/src/scripts/_lib/managed_agents_folder.ts +181 -0
- package/src/scripts/_lib/payload_hash_drift.ts +126 -0
- package/src/scripts/_lib/preservation_migration.ts +291 -0
- package/src/scripts/_lib/retrieval_sanitize.ts +166 -2
- package/src/scripts/_lib/scan_scope.ts +115 -0
- package/src/scripts/_lib/user_global_memory_audit.ts +187 -0
- package/src/scripts/_lib/user_global_observations.ts +830 -0
- package/src/scripts/_lib/user_global_revocations.ts +110 -0
- package/src/scripts/_lib/user_memory_gate_counters.ts +291 -0
- package/src/scripts/ai_council/blind_review.ts +0 -0
- package/src/scripts/ai_council/budget_guard.ts +90 -11
- package/src/scripts/ai_council/bundler.ts +2 -2
- package/src/scripts/ai_council/clients.ts +59 -3
- package/src/scripts/ai_council/config.ts +38 -0
- package/src/scripts/ai_council/orchestrator.ts +54 -9
- package/src/scripts/ai_team/team_dispatch.ts +17 -6
- package/src/scripts/anchor_eval_falsify.ts +198 -0
- package/src/scripts/anchor_scoring_run.ts +239 -0
- package/src/scripts/annotate_discovery.ts +1 -65
- package/src/scripts/bench_quality_run.ts +168 -4
- package/src/scripts/cache_realization_report.ts +782 -0
- package/src/scripts/check_artefact_count_messaging.ts +5 -1
- package/src/scripts/check_augment_description_cap.ts +15 -4
- package/src/scripts/check_ci_local_parity.ts +317 -0
- package/src/scripts/check_condensation.ts +96 -191
- package/src/scripts/check_context_paths.ts +38 -9
- package/src/scripts/check_gate_coverage.ts +299 -0
- package/src/scripts/check_iron_law_prominence.ts +22 -1
- package/src/scripts/check_kernel_prefix_stability.ts +0 -0
- package/src/scripts/check_no_new_legacy_path.ts +16 -6
- package/src/scripts/check_no_roadmap_refs.ts +22 -9
- package/src/scripts/check_originality_freshness.ts +167 -0
- package/src/scripts/check_preamble_payload_budget.ts +130 -0
- package/src/scripts/check_proposal.ts +0 -0
- package/src/scripts/check_references.ts +0 -1
- package/src/scripts/check_safety_floor_untouched.ts +171 -24
- package/src/scripts/check_site_links.ts +62 -3
- package/src/scripts/ci_status.ts +0 -0
- package/src/scripts/cluster_near_miss_patterns.ts +0 -0
- package/src/scripts/code_graph/build.ts +0 -0
- package/src/scripts/command_suggester/types.ts +0 -0
- package/src/scripts/compile_router.ts +10 -14
- package/src/scripts/condense.sh +3 -4
- package/src/scripts/condense.ts +247 -228
- package/src/scripts/cost/track.mjs +168 -33
- package/src/scripts/cost_summary.ts +33 -1
- package/src/scripts/council_cli.ts +45 -3
- package/src/scripts/discovery_graph.ts +0 -0
- package/src/scripts/encoding_corpus.ts +500 -0
- package/src/scripts/encoding_corpus_report.ts +216 -0
- package/src/scripts/generate_index.ts +19 -2
- package/src/scripts/install-hooks.sh +2 -2
- package/src/scripts/learning_sidecar.ts +0 -0
- package/src/scripts/lint_confusables.ts +10 -72
- package/src/scripts/lint_featured_skills.ts +0 -0
- package/src/scripts/lint_hidden_unicode.ts +208 -0
- package/src/scripts/lint_load_context.ts +17 -5
- package/src/scripts/lint_new_skill_gate.ts +9 -3
- package/src/scripts/lint_no_python_twin_rationale.ts +17 -2
- package/src/scripts/lint_originality.ts +1 -1
- package/src/scripts/lint_skill_originality.ts +1 -1
- package/src/scripts/lint_ticket_buildable.ts +1 -1
- package/src/scripts/mcp_server/tools.ts +4 -4
- package/src/scripts/measure_scope_dedup.ts +224 -0
- package/src/scripts/memory_lookup.ts +0 -0
- package/src/scripts/mine_session.ts +98 -0
- package/src/scripts/orchestration_payload_hash_drift.ts +145 -0
- package/src/scripts/preamble_byte_census.ts +574 -0
- package/src/scripts/probe_skill_registration.ts +2 -2
- package/src/scripts/redact_hook_capture.ts +13 -0
- package/src/scripts/release.ts +65 -10
- package/src/scripts/router_telemetry.ts +1 -1
- package/src/scripts/second_brain_retrieval.ts +14 -4
- package/src/scripts/skill_linter.ts +5 -0
- package/src/scripts/surface-tiers.yml +2 -0
- package/src/scripts/validate_pack_yaml.ts +0 -0
- package/src/server/schemas/settings.ts +8 -0
- package/src/shared/userMd/utils.ts +31 -0
- package/dist/agent-src/rules/telegraph-speak.md +0 -87
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
#!/usr/bin/env tsx
|
|
2
|
+
/**
|
|
3
|
+
* Measure the encoding floor against the FROZEN corpus
|
|
4
|
+
* (road-to-runtime-encoding-hardening Phase 4).
|
|
5
|
+
*
|
|
6
|
+
* Renders the numbers from the corpus — never hand-typed — and evaluates them
|
|
7
|
+
* against the thresholds the roadmap pre-registered BEFORE the detector existed:
|
|
8
|
+
*
|
|
9
|
+
* recall on in-scope positives >= 0.95
|
|
10
|
+
* recall on unambiguous classes >= 0.99
|
|
11
|
+
* (zero-width, bidi, variation-selector)
|
|
12
|
+
* false-positive rate on clean negatives <= 0.005
|
|
13
|
+
* added runtime model spend == 0 (structural: no model call)
|
|
14
|
+
* added latency < 2 ms p95 per message
|
|
15
|
+
*
|
|
16
|
+
* A positive counts as DETECTED when the floor either strips its signal
|
|
17
|
+
* (`sanitize_text` changed the value) or reports it (`scan_encoding_findings`
|
|
18
|
+
* returned a finding). Both are "the vector did not reach the model unnoticed",
|
|
19
|
+
* which is the property under test.
|
|
20
|
+
*
|
|
21
|
+
* Usage:
|
|
22
|
+
* encoding_corpus_report # human summary + exit 1 if a gate fails
|
|
23
|
+
* encoding_corpus_report --json # machine payload
|
|
24
|
+
*/
|
|
25
|
+
import * as fs from 'node:fs';
|
|
26
|
+
import * as path from 'node:path';
|
|
27
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
28
|
+
|
|
29
|
+
import { OUT_DIR, type CorpusEntry } from './encoding_corpus.js';
|
|
30
|
+
import { sanitize_text, scan_encoding_findings } from './_lib/retrieval_sanitize.js';
|
|
31
|
+
|
|
32
|
+
/** Pre-registered acceptance thresholds — do not edit to make a run pass. */
|
|
33
|
+
export const THRESHOLDS = {
|
|
34
|
+
recall_all: 0.95,
|
|
35
|
+
recall_unambiguous: 0.99,
|
|
36
|
+
fp_rate_max: 0.005,
|
|
37
|
+
latency_p95_ms_max: 2,
|
|
38
|
+
} as const;
|
|
39
|
+
|
|
40
|
+
/** Classes the roadmap singled out as unambiguous → higher recall bar. */
|
|
41
|
+
export const UNAMBIGUOUS_CHANNELS: readonly string[] = [
|
|
42
|
+
'zero-width',
|
|
43
|
+
'zero-width-joiner-bom',
|
|
44
|
+
'bidi-control',
|
|
45
|
+
'variation-selector-run',
|
|
46
|
+
];
|
|
47
|
+
|
|
48
|
+
function _readJsonl(name: string): CorpusEntry[] {
|
|
49
|
+
return fs
|
|
50
|
+
.readFileSync(path.join(OUT_DIR, name), 'utf-8')
|
|
51
|
+
.split('\n')
|
|
52
|
+
.filter((l) => l !== '')
|
|
53
|
+
.map((l) => JSON.parse(l) as CorpusEntry);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** True when the floor either stripped or reported the entry's signal. */
|
|
57
|
+
export function detected(text: string): { hit: boolean; stripped: boolean; flagged: boolean } {
|
|
58
|
+
const stripped = sanitize_text(text) !== text;
|
|
59
|
+
const flagged = scan_encoding_findings(text).length > 0;
|
|
60
|
+
return { hit: stripped || flagged, stripped, flagged };
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export interface Report {
|
|
64
|
+
readonly corpus: { positives: number; negatives: number };
|
|
65
|
+
readonly recall_all: number;
|
|
66
|
+
readonly recall_unambiguous: number;
|
|
67
|
+
readonly fp_rate: number;
|
|
68
|
+
readonly per_channel: Record<string, { n: number; detected: number; recall: number }>;
|
|
69
|
+
readonly false_positives: readonly string[];
|
|
70
|
+
readonly latency_p95_ms: number;
|
|
71
|
+
readonly model_calls: 0;
|
|
72
|
+
readonly gates: Record<string, { value: number; threshold: number; pass: boolean }>;
|
|
73
|
+
readonly verdict: 'ADOPT' | 'DETECT-AND-FLAG-ONLY' | 'FAIL';
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function _p95(samples: readonly number[]): number {
|
|
77
|
+
const sorted = [...samples].sort((a, b) => a - b);
|
|
78
|
+
const idx = Math.min(sorted.length - 1, Math.floor(sorted.length * 0.95));
|
|
79
|
+
return sorted[idx] ?? 0;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export function measure(): Report {
|
|
83
|
+
const positives = _readJsonl('positives.jsonl');
|
|
84
|
+
const negatives = _readJsonl('negatives.jsonl');
|
|
85
|
+
|
|
86
|
+
const perChannel: Record<string, { n: number; detected: number; recall: number }> = {};
|
|
87
|
+
const latencies: number[] = [];
|
|
88
|
+
let hits = 0;
|
|
89
|
+
for (const e of positives) {
|
|
90
|
+
const t0 = process.hrtime.bigint();
|
|
91
|
+
const d = detected(e.text);
|
|
92
|
+
latencies.push(Number(process.hrtime.bigint() - t0) / 1e6);
|
|
93
|
+
const slot = perChannel[e.channel] ?? { n: 0, detected: 0, recall: 0 };
|
|
94
|
+
slot.n += 1;
|
|
95
|
+
if (d.hit) {
|
|
96
|
+
slot.detected += 1;
|
|
97
|
+
hits += 1;
|
|
98
|
+
}
|
|
99
|
+
slot.recall = slot.detected / slot.n;
|
|
100
|
+
perChannel[e.channel] = slot;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const falsePositives: string[] = [];
|
|
104
|
+
for (const e of negatives) {
|
|
105
|
+
const t0 = process.hrtime.bigint();
|
|
106
|
+
const d = detected(e.text);
|
|
107
|
+
latencies.push(Number(process.hrtime.bigint() - t0) / 1e6);
|
|
108
|
+
if (d.hit) falsePositives.push(`${e.id}: ${e.text.slice(0, 90)}`);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const unambiguous = positives.filter((e) => UNAMBIGUOUS_CHANNELS.includes(e.channel));
|
|
112
|
+
const unambiguousHits = unambiguous.filter((e) => detected(e.text).hit).length;
|
|
113
|
+
|
|
114
|
+
const recallAll = positives.length === 0 ? 0 : hits / positives.length;
|
|
115
|
+
const recallUnambiguous = unambiguous.length === 0 ? 0 : unambiguousHits / unambiguous.length;
|
|
116
|
+
const fpRate = negatives.length === 0 ? 0 : falsePositives.length / negatives.length;
|
|
117
|
+
const p95 = _p95(latencies);
|
|
118
|
+
|
|
119
|
+
const gates = {
|
|
120
|
+
recall_all: { value: recallAll, threshold: THRESHOLDS.recall_all, pass: recallAll >= THRESHOLDS.recall_all },
|
|
121
|
+
recall_unambiguous: {
|
|
122
|
+
value: recallUnambiguous,
|
|
123
|
+
threshold: THRESHOLDS.recall_unambiguous,
|
|
124
|
+
pass: recallUnambiguous >= THRESHOLDS.recall_unambiguous,
|
|
125
|
+
},
|
|
126
|
+
fp_rate: { value: fpRate, threshold: THRESHOLDS.fp_rate_max, pass: fpRate <= THRESHOLDS.fp_rate_max },
|
|
127
|
+
latency_p95_ms: {
|
|
128
|
+
value: p95,
|
|
129
|
+
threshold: THRESHOLDS.latency_p95_ms_max,
|
|
130
|
+
pass: p95 < THRESHOLDS.latency_p95_ms_max,
|
|
131
|
+
},
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
// The roadmap's pre-registered branch: recall clears but FP is over budget →
|
|
135
|
+
// ship detect-and-flag only, never automatic quarantine, and publish the FP
|
|
136
|
+
// number. Nothing here quarantines anyway, so this records which branch the
|
|
137
|
+
// measurement selected rather than switching behaviour after the fact.
|
|
138
|
+
const recallOk = gates.recall_all.pass && gates.recall_unambiguous.pass;
|
|
139
|
+
const verdict: Report['verdict'] = !recallOk
|
|
140
|
+
? 'FAIL'
|
|
141
|
+
: gates.fp_rate.pass && gates.latency_p95_ms.pass
|
|
142
|
+
? 'ADOPT'
|
|
143
|
+
: 'DETECT-AND-FLAG-ONLY';
|
|
144
|
+
|
|
145
|
+
return {
|
|
146
|
+
corpus: { positives: positives.length, negatives: negatives.length },
|
|
147
|
+
recall_all: recallAll,
|
|
148
|
+
recall_unambiguous: recallUnambiguous,
|
|
149
|
+
fp_rate: fpRate,
|
|
150
|
+
per_channel: perChannel,
|
|
151
|
+
false_positives: falsePositives,
|
|
152
|
+
latency_p95_ms: p95,
|
|
153
|
+
model_calls: 0,
|
|
154
|
+
gates,
|
|
155
|
+
verdict,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function _render(r: Report): string {
|
|
160
|
+
const pct = (n: number): string => (n * 100).toFixed(2) + '%';
|
|
161
|
+
const lines: string[] = [];
|
|
162
|
+
lines.push(`corpus: ${r.corpus.positives} positives · ${r.corpus.negatives} negatives`);
|
|
163
|
+
lines.push('');
|
|
164
|
+
lines.push('per-channel recall');
|
|
165
|
+
for (const [ch, s] of Object.entries(r.per_channel).sort()) {
|
|
166
|
+
const mark = s.recall === 1 ? ' ' : '!!';
|
|
167
|
+
lines.push(` ${mark} ${ch.padEnd(24)} ${s.detected}/${s.n} ${pct(s.recall)}`);
|
|
168
|
+
}
|
|
169
|
+
lines.push('');
|
|
170
|
+
for (const [name, g] of Object.entries(r.gates)) {
|
|
171
|
+
const cmp = name === 'fp_rate' || name === 'latency_p95_ms' ? '<=' : '>=';
|
|
172
|
+
const val = name === 'latency_p95_ms' ? `${g.value.toFixed(3)} ms` : pct(g.value);
|
|
173
|
+
const thr = name === 'latency_p95_ms' ? `${g.threshold} ms` : pct(g.threshold);
|
|
174
|
+
lines.push(` ${g.pass ? 'PASS' : 'FAIL'} ${name.padEnd(20)} ${val} ${cmp} ${thr}`);
|
|
175
|
+
}
|
|
176
|
+
lines.push(` PASS ${'model_calls'.padEnd(20)} 0 (structural: no model call on this path)`);
|
|
177
|
+
if (r.false_positives.length > 0) {
|
|
178
|
+
lines.push('');
|
|
179
|
+
lines.push(`false positives (${r.false_positives.length}):`);
|
|
180
|
+
for (const fp of r.false_positives.slice(0, 12)) lines.push(` - ${fp}`);
|
|
181
|
+
if (r.false_positives.length > 12) {
|
|
182
|
+
lines.push(` … ${r.false_positives.length - 12} more`);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
lines.push('');
|
|
186
|
+
lines.push(`verdict: ${r.verdict}`);
|
|
187
|
+
return lines.join('\n') + '\n';
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
export function main(argv: readonly string[] | null = null): number {
|
|
191
|
+
const args = argv ?? process.argv.slice(2);
|
|
192
|
+
const r = measure();
|
|
193
|
+
if (args.includes('--json')) {
|
|
194
|
+
process.stdout.write(JSON.stringify(r, null, 2) + '\n');
|
|
195
|
+
} else {
|
|
196
|
+
process.stdout.write(_render(r));
|
|
197
|
+
}
|
|
198
|
+
return r.verdict === 'FAIL' ? 1 : 0;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
declare const __AGENT_CONFIG_BUNDLE__: boolean | undefined;
|
|
202
|
+
function _isCliEntry(): boolean {
|
|
203
|
+
if (typeof __AGENT_CONFIG_BUNDLE__ !== 'undefined' && __AGENT_CONFIG_BUNDLE__) return false;
|
|
204
|
+
if (process.argv[1] === undefined) return false;
|
|
205
|
+
const argvUrl = pathToFileURL(path.resolve(process.argv[1])).href;
|
|
206
|
+
if (import.meta.url === argvUrl) return true;
|
|
207
|
+
try {
|
|
208
|
+
return fs.realpathSync(fileURLToPath(import.meta.url)) === fs.realpathSync(path.resolve(process.argv[1]));
|
|
209
|
+
} catch {
|
|
210
|
+
return false;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
if (_isCliEntry()) {
|
|
215
|
+
process.exitCode = main();
|
|
216
|
+
}
|
|
@@ -25,6 +25,7 @@ import {
|
|
|
25
25
|
strip_source_prefix,
|
|
26
26
|
} from './_lib/agent_src.js';
|
|
27
27
|
import { computeStatus as domainSoundnessStatus } from './domain_soundness_status.js';
|
|
28
|
+
import { skip_compile_disabled_rule } from './condense.js';
|
|
28
29
|
|
|
29
30
|
const _HERE = fileURLToPath(import.meta.url);
|
|
30
31
|
|
|
@@ -243,6 +244,12 @@ export function _collect_skills(): Entry[] {
|
|
|
243
244
|
return _sortedKeys(seen).map((k) => seen.get(k) as Entry);
|
|
244
245
|
}
|
|
245
246
|
|
|
247
|
+
// docs/catalog.md is a SHIPPED file, so a relative link from it must resolve inside
|
|
248
|
+
// package.json#files. docs/proof.md is not shipped and cannot be: it is 58KB of
|
|
249
|
+
// generated output linking eight paths under internal/bench/, tests/ and src/scripts/
|
|
250
|
+
// that are themselves unshipped, so shipping it would hand consumers a page of dead
|
|
251
|
+
// links. The published site URL is the honest target for a consumer-facing pointer.
|
|
252
|
+
|
|
246
253
|
export function _collect_rules(): Entry[] {
|
|
247
254
|
const seen = new Map<string, Entry>();
|
|
248
255
|
for (const srcRoot of artefact_roots()) {
|
|
@@ -255,6 +262,14 @@ export function _collect_rules(): Entry[] {
|
|
|
255
262
|
if (seen.has(stem)) {
|
|
256
263
|
continue;
|
|
257
264
|
}
|
|
265
|
+
// A compile-disabled rule has no dist/agent-src/ counterpart, and the
|
|
266
|
+
// table links the SHIPPED path — so listing it emits a dead link. The
|
|
267
|
+
// toggle is a separate axis from the source file's existence; this is its
|
|
268
|
+
// fifth consumer after the router compiler, the dist writer, check_sync,
|
|
269
|
+
// and the per-tool symlink generator. Same predicate in all five.
|
|
270
|
+
if (skip_compile_disabled_rule(`rules/${stem}.md`)) {
|
|
271
|
+
continue;
|
|
272
|
+
}
|
|
258
273
|
const fm = _parse_frontmatter(fs.readFileSync(ruleMd, 'utf-8'));
|
|
259
274
|
seen.set(stem, {
|
|
260
275
|
kind: 'rule',
|
|
@@ -420,7 +435,8 @@ export function _render_catalog(
|
|
|
420
435
|
'> not yet. Coverage is reported per tier (`./scripts-run',
|
|
421
436
|
'> src/scripts/skill_eval_coverage`) and CI-ratcheted so it can only rise.',
|
|
422
437
|
'> Listing here never implies a skill is behaviourally tested — the',
|
|
423
|
-
'> current coverage and its gap are published on [the proof page]
|
|
438
|
+
'> current coverage and its gap are published on [the proof page]' +
|
|
439
|
+
'(https://event4u-app.github.io/agent-config/proof/).',
|
|
424
440
|
'',
|
|
425
441
|
'> **Non-coding domain correctness is scoped, not proven.** The',
|
|
426
442
|
'> `finance` / `founder` / `ops` / `content` skills are forged on TS/PHP',
|
|
@@ -429,7 +445,8 @@ export function _render_catalog(
|
|
|
429
445
|
'> cited formulas, or rubric targets matching a named external practice);',
|
|
430
446
|
'> the rest are labeled `unvalidated` until they pass one',
|
|
431
447
|
'> (`./scripts-run src/scripts/domain_soundness_status`). A disclaimer',
|
|
432
|
-
'> floor bounds liability, not correctness — see [the proof page]
|
|
448
|
+
'> floor bounds liability, not correctness — see [the proof page]' +
|
|
449
|
+
'(https://event4u-app.github.io/agent-config/proof/).',
|
|
433
450
|
'',
|
|
434
451
|
_render_table(skills, ['kind', 'name', 'source', 'description'], '../', _to_shipped_path),
|
|
435
452
|
'',
|
|
@@ -30,8 +30,8 @@ cat > "$HOOKS_DIR/pre-push" << 'EOF'
|
|
|
30
30
|
# the old hook only verified dist sync + the COMMAND count — it missed
|
|
31
31
|
# guideline/skill/rule count drift and the generate-tools / router / corpus
|
|
32
32
|
# outputs (e.g. a new guideline bumping the count in README + docs/architecture).
|
|
33
|
-
# `task consistency` is the EXACT CI mirror (sync-check + sync
|
|
34
|
-
#
|
|
33
|
+
# `task consistency` is the EXACT CI mirror (sync-check + sync + generate-tools
|
|
34
|
+
# + compile-router + compile-corpus + `git diff --quiet`),
|
|
35
35
|
# so any derived-output drift blocks the push here. Runtime ~15-40s (it
|
|
36
36
|
# regenerates the tool trees) — cheaper than a red CI run and a fixup re-push.
|
|
37
37
|
|
|
Binary file
|
|
@@ -34,86 +34,24 @@ import * as fs from 'node:fs';
|
|
|
34
34
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
35
35
|
|
|
36
36
|
import * as sl from './_lib/security_lint.js';
|
|
37
|
+
import { TOKEN_RE, classifyToken } from './_lib/confusables.js';
|
|
37
38
|
|
|
38
39
|
export const CHECK = 'mixed-script-confusable';
|
|
39
40
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
//
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
const
|
|
46
|
-
const _LETTER = /\p{L}/u;
|
|
47
|
-
// A "token" is a maximal run of letters / combining marks / decimal digits.
|
|
48
|
-
const _TOKEN = /[\p{L}\p{M}\p{Nd}]+/gu;
|
|
49
|
-
|
|
50
|
-
// Cyrillic + Greek codepoints that have a basic-Latin lookalike (Unicode TR39
|
|
51
|
-
// confusables, common subset). A foreign letter only counts toward the
|
|
52
|
-
// confusable signal when it is in this set — Greek math operators with no Latin
|
|
53
|
-
// twin (Δ Σ Π Ω Φ Θ Λ Ξ Ψ Γ δ π ω …) are deliberately excluded so legit
|
|
54
|
-
// notation does not false-positive.
|
|
55
|
-
const _CONFUSABLE_FOREIGN: ReadonlySet<number> = new Set([
|
|
56
|
-
// Cyrillic lowercase ↔ Latin
|
|
57
|
-
0x0430 /*а→a*/, 0x0435 /*е→e*/, 0x043e /*о→o*/, 0x0440 /*р→p*/, 0x0441 /*с→c*/,
|
|
58
|
-
0x0443 /*у→y*/, 0x0445 /*х→x*/, 0x0455 /*ѕ→s*/, 0x0456 /*і→i*/, 0x0458 /*ј→j*/,
|
|
59
|
-
0x04bb /*һ→h*/, 0x0501 /*ԁ→d*/, 0x051b /*ԛ→q*/, 0x0577 /*no*/,
|
|
60
|
-
// Cyrillic uppercase ↔ Latin
|
|
61
|
-
0x0410 /*А→A*/, 0x0412 /*В→B*/, 0x0415 /*Е→E*/, 0x041a /*К→K*/, 0x041c /*М→M*/,
|
|
62
|
-
0x041d /*Н→H*/, 0x041e /*О→O*/, 0x0420 /*Р→P*/, 0x0421 /*С→C*/, 0x0422 /*Т→T*/,
|
|
63
|
-
0x0423 /*У→Y*/, 0x0425 /*Х→X*/, 0x0405 /*Ѕ→S*/, 0x0406 /*І→I*/, 0x0408 /*Ј→J*/,
|
|
64
|
-
// Greek lowercase ↔ Latin
|
|
65
|
-
0x03b1 /*α→a*/, 0x03b5 /*ε→e*/, 0x03bf /*ο→o*/, 0x03bd /*ν→v*/, 0x03c1 /*ρ→p*/,
|
|
66
|
-
0x03c5 /*υ→u*/, 0x03c7 /*χ→x*/, 0x03ba /*κ→k*/,
|
|
67
|
-
// Greek uppercase ↔ Latin (only those with a real Latin twin)
|
|
68
|
-
0x0391 /*Α→A*/, 0x0392 /*Β→B*/, 0x0395 /*Ε→E*/, 0x0396 /*Ζ→Z*/, 0x0397 /*Η→H*/,
|
|
69
|
-
0x0399 /*Ι→I*/, 0x039a /*Κ→K*/, 0x039c /*Μ→M*/, 0x039d /*Ν→N*/, 0x039f /*Ο→O*/,
|
|
70
|
-
0x03a1 /*Ρ→P*/, 0x03a4 /*Τ→T*/, 0x03a5 /*Υ→Y*/, 0x03a7 /*Χ→X*/, 0x0392 /*Β→B*/,
|
|
71
|
-
]);
|
|
72
|
-
|
|
73
|
-
type ScriptName = 'latin' | 'cyrillic' | 'greek' | 'other';
|
|
74
|
-
|
|
75
|
-
function _script_of(ch: string): ScriptName {
|
|
76
|
-
if (_LATIN.test(ch)) return 'latin';
|
|
77
|
-
if (_CYRILLIC.test(ch)) return 'cyrillic';
|
|
78
|
-
if (_GREEK.test(ch)) return 'greek';
|
|
79
|
-
return 'other';
|
|
80
|
-
}
|
|
41
|
+
// The confusable table + the mixed-script decision rule live in ONE place so
|
|
42
|
+
// this authoring-time linter and the runtime encoding scanner cannot drift apart
|
|
43
|
+
// (road-to-runtime-encoding-hardening Phase 3). `_classify_token` stays exported
|
|
44
|
+
// under its original name — it is this module's published surface and its tests
|
|
45
|
+
// address it here.
|
|
46
|
+
const _TOKEN = TOKEN_RE;
|
|
81
47
|
|
|
82
48
|
/**
|
|
83
49
|
* Classify a single token. Returns the offending mixed-script descriptor when
|
|
84
50
|
* the token matches the homoglyph signature, else null.
|
|
51
|
+
*
|
|
52
|
+
* Thin re-export of the shared implementation — see `_lib/confusables.ts`.
|
|
85
53
|
*/
|
|
86
|
-
export
|
|
87
|
-
let latin = 0;
|
|
88
|
-
let cyrillic = 0;
|
|
89
|
-
let greek = 0;
|
|
90
|
-
let letters = 0;
|
|
91
|
-
for (const ch of token) {
|
|
92
|
-
if (!_LETTER.test(ch)) continue; // skip combining marks / digits for script vote
|
|
93
|
-
letters += 1;
|
|
94
|
-
const script = _script_of(ch);
|
|
95
|
-
if (script === 'latin') {
|
|
96
|
-
latin += 1;
|
|
97
|
-
} else if (script === 'cyrillic' || script === 'greek') {
|
|
98
|
-
// Only confusable foreign letters (TR39) count — math operators with
|
|
99
|
-
// no Latin twin are ignored, so legit notation never trips.
|
|
100
|
-
if (_CONFUSABLE_FOREIGN.has(ch.codePointAt(0) as number)) {
|
|
101
|
-
if (script === 'cyrillic') cyrillic += 1;
|
|
102
|
-
else greek += 1;
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
if (letters < MIN_LETTERS) return null;
|
|
107
|
-
const foreign = cyrillic + greek;
|
|
108
|
-
if (latin === 0 || foreign === 0) return null; // single-script (or no Latin) → legit
|
|
109
|
-
if (latin <= foreign) return null; // majority must be Latin (foreign word ≠ attack)
|
|
110
|
-
const which = cyrillic > 0 && greek > 0
|
|
111
|
-
? 'cyrillic+greek'
|
|
112
|
-
: cyrillic > 0
|
|
113
|
-
? 'cyrillic'
|
|
114
|
-
: 'greek';
|
|
115
|
-
return `latin+${which} (${latin} latin / ${foreign} foreign-confusable)`;
|
|
116
|
-
}
|
|
54
|
+
export const _classify_token = classifyToken;
|
|
117
55
|
|
|
118
56
|
export function _scan(sf: sl.ScannedFile): sl.Finding[] {
|
|
119
57
|
if (sf.pragma_allows(CHECK)) {
|
|
Binary file
|
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
|
|
31
31
|
import * as fs from 'node:fs';
|
|
32
32
|
import * as path from 'node:path';
|
|
33
|
+
import { spawnSync } from 'node:child_process';
|
|
33
34
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
34
35
|
|
|
35
36
|
import * as sl from './_lib/security_lint.js';
|
|
@@ -261,6 +262,191 @@ export function _scan(sf: sl.ScannedFile): sl.Finding[] {
|
|
|
261
262
|
return out;
|
|
262
263
|
}
|
|
263
264
|
|
|
265
|
+
// ---------------------------------------------------------------------------
|
|
266
|
+
// Source-file pass — raw C0 control bytes make a text file INVISIBLE to tools
|
|
267
|
+
// ---------------------------------------------------------------------------
|
|
268
|
+
//
|
|
269
|
+
// The `.md` corpus pass above defends the model's eyes. This pass defends the
|
|
270
|
+
// *reviewer's* and the *gate's* eyes, which is a different failure.
|
|
271
|
+
//
|
|
272
|
+
// A raw control byte (NUL above all) in a text-intended source file makes
|
|
273
|
+
// `file(1)`, `grep`, and every tool that sniffs for binary content classify the
|
|
274
|
+
// file as binary and SKIP IT SILENTLY. A linter grepping such a file reads
|
|
275
|
+
// nothing and reports clean; an agent gathering evidence concludes the code
|
|
276
|
+
// does not exist. That is indistinguishable from a pass, which is the point.
|
|
277
|
+
//
|
|
278
|
+
// Discovered the hard way: `road-to-runtime-encoding-hardening` was authored on
|
|
279
|
+
// the premise that `memory_lookup.ts` had "zero imports" and that the sanitize
|
|
280
|
+
// floor therefore ran nowhere. The file simply carried a raw NUL as a composite
|
|
281
|
+
// map-key separator, so `grep` never read it. The premise was a measurement
|
|
282
|
+
// artifact. See `agents/evidence/reports/nul-byte-source-census.md`.
|
|
283
|
+
//
|
|
284
|
+
// Precision by construction — this pass flags ONLY raw C0 controls, never the
|
|
285
|
+
// invisible/confusable classes `_scan` owns. In a `.ts` file a real bidi or
|
|
286
|
+
// zero-width codepoint is usually a deliberate test fixture or a regex
|
|
287
|
+
// character class (this repo has several, legitimately), so flagging those here
|
|
288
|
+
// would demand an allowlist that grows until the gate is worthless. A raw
|
|
289
|
+
// control byte has NO legitimate use in text-intended source: the language
|
|
290
|
+
// escape (`\0`, `\x01`) compiles to the identical runtime string and keeps the
|
|
291
|
+
// file readable by tools. The fix is always available, so the false-positive
|
|
292
|
+
// rate is structurally zero rather than merely low.
|
|
293
|
+
|
|
294
|
+
/** Extensions whose content is legitimately binary — never flagged. */
|
|
295
|
+
const _BINARY_EXT: ReadonlySet<string> = new Set([
|
|
296
|
+
'png', 'jpg', 'jpeg', 'gif', 'ico', 'webp', 'avif', 'bmp', 'tiff',
|
|
297
|
+
'pdf', 'woff', 'woff2', 'ttf', 'otf', 'eot',
|
|
298
|
+
'zip', 'gz', 'tgz', 'bz2', 'xz', 'zst', '7z', 'rar',
|
|
299
|
+
'wasm', 'mp3', 'mp4', 'wav', 'ogg', 'webm', 'mov', 'avi',
|
|
300
|
+
'so', 'dylib', 'dll', 'node', 'a', 'o', 'exe',
|
|
301
|
+
'jar', 'class', 'pyc', 'pyo',
|
|
302
|
+
'db', 'sqlite', 'sqlite3', 'bin', 'dat',
|
|
303
|
+
'docx', 'xlsx', 'pptx', 'doc', 'xls', 'ppt', 'odt', 'ods', 'odp',
|
|
304
|
+
]);
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* Generated trees — skipped, because this is an AUTHORING rule.
|
|
308
|
+
*
|
|
309
|
+
* `dist/` and the per-tool projections are outputs of `src/` (see the
|
|
310
|
+
* `source-of-truth` rule: a projection is never edited by hand). Flagging a
|
|
311
|
+
* control byte there would point the author at a file they must not touch, and
|
|
312
|
+
* `dist/install/*.mjs` is a bundler artifact whose bytes are not even
|
|
313
|
+
* hand-authorable. Fix the source; the projection follows on `task sync`.
|
|
314
|
+
*/
|
|
315
|
+
const _GENERATED_PREFIXES: readonly string[] = [
|
|
316
|
+
'dist/',
|
|
317
|
+
'.augment/',
|
|
318
|
+
'.claude/',
|
|
319
|
+
'.cursor/',
|
|
320
|
+
'.clinerules/',
|
|
321
|
+
// Verbatim captured tool output, not authored text. These are RECORDINGS:
|
|
322
|
+
// escaping a control byte inside one would falsify the record it exists to
|
|
323
|
+
// preserve. If a capture needs to stay grep-readable, the fix is to escape
|
|
324
|
+
// at capture time — never to edit the artifact afterwards.
|
|
325
|
+
'agents/evidence/analysis/',
|
|
326
|
+
// The frozen hostile-encoding corpus. Its positives ARE raw control
|
|
327
|
+
// characters — that is the thing under test, so flagging them would demand
|
|
328
|
+
// escaping the fixtures and thereby delete the test. The corpus has its own,
|
|
329
|
+
// stronger integrity controls: a sha256 freeze plus a scope-guard test that
|
|
330
|
+
// is itself falsified by an out-of-scope fixture. Excluded here because it
|
|
331
|
+
// is DATA, on the same principle as the captures above.
|
|
332
|
+
'internal/bench/corpora/encoding-channels/',
|
|
333
|
+
];
|
|
334
|
+
|
|
335
|
+
function _isGenerated(rel: string): boolean {
|
|
336
|
+
return _GENERATED_PREFIXES.some((p) => rel.startsWith(p));
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* True for a raw control CODEPOINT that is not benign whitespace.
|
|
341
|
+
*
|
|
342
|
+
* Codepoints, not bytes, and that distinction is the whole point. A first cut
|
|
343
|
+
* of this pass tested bytes ≤ 0x1F and let two classes through: DEL (U+007F,
|
|
344
|
+
* a single byte above the range) and the C1 controls (U+0080–009F, which encode
|
|
345
|
+
* to a two-byte UTF-8 sequence containing no byte ≤ 0x1F). Both make `file(1)`
|
|
346
|
+
* and git classify a file as binary — `tests/scripts/retrieval_sanitize.test.ts`
|
|
347
|
+
* carried U+007F and U+009F, rendered as `Bin` in `git diff`, unreviewable on a
|
|
348
|
+
* PR, and passed the byte-level check. A gate that misses the thing it exists to
|
|
349
|
+
* catch is the failure this whole roadmap is about, so the range now matches
|
|
350
|
+
* `_classify`'s: C0 + DEL + C1, minus tab / LF / CR.
|
|
351
|
+
*/
|
|
352
|
+
function _isRawControlCodepoint(cp: number): boolean {
|
|
353
|
+
if (cp === 0x09 || cp === 0x0a || cp === 0x0d) return false;
|
|
354
|
+
return cp <= 0x1f || (cp >= 0x7f && cp <= 0x9f);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
function _extensionOf(rel: string): string {
|
|
358
|
+
const base = rel.slice(rel.lastIndexOf('/') + 1);
|
|
359
|
+
const dot = base.lastIndexOf('.');
|
|
360
|
+
return dot <= 0 ? '' : base.slice(dot + 1).toLowerCase();
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/** Tracked files, or `null` when git is unavailable (then the pass no-ops). */
|
|
364
|
+
function _trackedFiles(): string[] | null {
|
|
365
|
+
const res = spawnSync('git', ['ls-files', '-z'], {
|
|
366
|
+
cwd: sl.ROOT,
|
|
367
|
+
maxBuffer: 64 * 1024 * 1024,
|
|
368
|
+
});
|
|
369
|
+
if (res.status !== 0 || res.stdout === null) return null;
|
|
370
|
+
return res.stdout
|
|
371
|
+
.toString('utf-8')
|
|
372
|
+
.split('\0')
|
|
373
|
+
.filter((p) => p !== '');
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
/**
|
|
377
|
+
* Tracked files this pass is responsible for — text-intended, non-generated.
|
|
378
|
+
*
|
|
379
|
+
* Exported so a green run can state HOW MANY files it actually read. A check
|
|
380
|
+
* that reports "clean" without naming its scope is indistinguishable from a
|
|
381
|
+
* check whose scope matched nothing, which is the failure class this repo has
|
|
382
|
+
* already been bitten by.
|
|
383
|
+
*/
|
|
384
|
+
export function _eligibleSourceFiles(): string[] | null {
|
|
385
|
+
const tracked = _trackedFiles();
|
|
386
|
+
if (tracked === null) return null;
|
|
387
|
+
return tracked.filter((rel) => !_isGenerated(rel) && !_BINARY_EXT.has(_extensionOf(rel)));
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
/**
|
|
391
|
+
* Flag raw C0 control bytes in every tracked, text-intended file.
|
|
392
|
+
*
|
|
393
|
+
* `files` is injectable so the regression test can assert a synthetic NUL is
|
|
394
|
+
* caught — without that assertion there is no way to know this pass can still
|
|
395
|
+
* fail, which is the failure mode the whole check exists to prevent.
|
|
396
|
+
*/
|
|
397
|
+
export function _scanSourceControlBytes(files: readonly string[] | null = null): sl.Finding[] {
|
|
398
|
+
const tracked = files ?? _eligibleSourceFiles();
|
|
399
|
+
if (tracked === null) return [];
|
|
400
|
+
const out: sl.Finding[] = [];
|
|
401
|
+
for (const rel of tracked) {
|
|
402
|
+
// Re-applied here, not only in `_eligibleSourceFiles`: the exclusions
|
|
403
|
+
// are a property of the CHECK, so an explicit `files` list (a caller, a
|
|
404
|
+
// test) can never widen the scan past what the check is willing to
|
|
405
|
+
// claim. `_eligibleSourceFiles` exists to count, not to be the gate.
|
|
406
|
+
if (_isGenerated(rel)) continue;
|
|
407
|
+
if (_BINARY_EXT.has(_extensionOf(rel))) continue;
|
|
408
|
+
let data: Buffer;
|
|
409
|
+
try {
|
|
410
|
+
data = fs.readFileSync(path.join(sl.ROOT, rel));
|
|
411
|
+
} catch {
|
|
412
|
+
continue; // deleted, unreadable, or a submodule pointer
|
|
413
|
+
}
|
|
414
|
+
// A file that is not valid UTF-8 is binary regardless of its extension
|
|
415
|
+
// (an extensionless fixture, say) — not this check's business.
|
|
416
|
+
try {
|
|
417
|
+
new TextDecoder('utf-8', { fatal: true }).decode(data);
|
|
418
|
+
} catch {
|
|
419
|
+
continue;
|
|
420
|
+
}
|
|
421
|
+
// Iterate CODEPOINTS (the string is already known-valid UTF-8 here), so
|
|
422
|
+
// DEL and the C1 range are visible — see `_isRawControlCodepoint`.
|
|
423
|
+
let line = 1;
|
|
424
|
+
for (const ch of data.toString('utf-8')) {
|
|
425
|
+
const cp = ch.codePointAt(0);
|
|
426
|
+
if (cp === undefined) continue;
|
|
427
|
+
if (cp === 0x0a) {
|
|
428
|
+
line += 1;
|
|
429
|
+
continue;
|
|
430
|
+
}
|
|
431
|
+
if (!_isRawControlCodepoint(cp)) continue;
|
|
432
|
+
const hex = cp.toString(16).toUpperCase().padStart(2, '0');
|
|
433
|
+
out.push(
|
|
434
|
+
new sl.Finding(
|
|
435
|
+
rel,
|
|
436
|
+
line,
|
|
437
|
+
CHECK,
|
|
438
|
+
'HIGH',
|
|
439
|
+
`raw control character U+${hex} in a text source — makes ` +
|
|
440
|
+
'grep/file(1)/git treat this file as binary and skip it ' +
|
|
441
|
+
'silently; use the language escape (e.g. \\0, \\x7F) instead',
|
|
442
|
+
1.0,
|
|
443
|
+
),
|
|
444
|
+
);
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
return out;
|
|
448
|
+
}
|
|
449
|
+
|
|
264
450
|
export function _sanitize(filePath: string): string {
|
|
265
451
|
const raw = fs.readFileSync(filePath, 'utf-8');
|
|
266
452
|
let cleaned = '';
|
|
@@ -323,6 +509,28 @@ export function main(argv: readonly string[] | null = null): number {
|
|
|
323
509
|
}
|
|
324
510
|
}
|
|
325
511
|
|
|
512
|
+
// Second pass — raw control bytes in text-intended SOURCE files, which the
|
|
513
|
+
// `.md` corpus above does not cover. Deliberately not folded into
|
|
514
|
+
// `iter_corpus`: different scope (all tracked text files, not the `.md`
|
|
515
|
+
// corpus) and a narrower codepoint set (C0 only). `--fix` does not touch
|
|
516
|
+
// these — rewriting a source file's bytes is the author's call, and the
|
|
517
|
+
// finding names the escape to use.
|
|
518
|
+
const sourceFiles = _eligibleSourceFiles();
|
|
519
|
+
for (const h of _scanSourceControlBytes(sourceFiles)) {
|
|
520
|
+
findings.push(h);
|
|
521
|
+
}
|
|
522
|
+
if (!args.json) {
|
|
523
|
+
// State the scope on every run. A bare "clean" cannot be told apart
|
|
524
|
+
// from a pass whose file list was empty.
|
|
525
|
+
const n = sourceFiles === null ? 0 : sourceFiles.length;
|
|
526
|
+
process.stdout.write(
|
|
527
|
+
sourceFiles === null
|
|
528
|
+
? ' source pass: SKIPPED — `git ls-files` unavailable, 0 files read for raw control bytes\n'
|
|
529
|
+
: ` source pass: ${n} tracked text file(s) read for raw C0 control bytes ` +
|
|
530
|
+
'(generated trees + verbatim captures excluded)\n',
|
|
531
|
+
);
|
|
532
|
+
}
|
|
533
|
+
|
|
326
534
|
if (args.fix) {
|
|
327
535
|
// sorted(flagged) — by path string.
|
|
328
536
|
for (const p of [...flagged].sort()) {
|
|
@@ -36,9 +36,15 @@ const QUIET = process.argv.slice(2).includes('--quiet');
|
|
|
36
36
|
// src/scripts/lint_load_context.ts → three dirs up is the repo root.
|
|
37
37
|
const ROOT = path.resolve(path.dirname(_HERE), '..', '..');
|
|
38
38
|
|
|
39
|
+
// Retargeted 2026-07-29: the first two pointed at the ADR-051-emptied tree, so
|
|
40
|
+
// this gate reported "clean (0 declarer(s))" in CI — the count was in its own
|
|
41
|
+
// output the whole time. `agents/contexts` does not exist either (project-local
|
|
42
|
+
// contexts live under agents/settings/contexts) and is kept only as a no-op for
|
|
43
|
+
// consumer layouts that do have it.
|
|
39
44
|
const SCAN_DIRS = [
|
|
40
|
-
path.join(ROOT, '
|
|
41
|
-
path.join(ROOT, '
|
|
45
|
+
path.join(ROOT, 'src', 'rules'),
|
|
46
|
+
path.join(ROOT, 'src', 'agent-src', 'contexts'),
|
|
47
|
+
path.join(ROOT, 'agents', 'settings', 'contexts'),
|
|
42
48
|
path.join(ROOT, 'agents', 'contexts'),
|
|
43
49
|
];
|
|
44
50
|
|
|
@@ -50,9 +56,10 @@ const ALLOWED_PREFIXES = [
|
|
|
50
56
|
|
|
51
57
|
const LEGACY_PREFIX = '.agent-src.uncondensed/contexts/';
|
|
52
58
|
|
|
53
|
-
|
|
59
|
+
// Base for resolving a logical `contexts/...` entry to a real file.
|
|
60
|
+
const SOURCE_ROOT = path.join(ROOT, 'src', 'agent-src');
|
|
54
61
|
|
|
55
|
-
const PUBLIC_RULE_PREFIX = '
|
|
62
|
+
const PUBLIC_RULE_PREFIX = 'src/rules/';
|
|
56
63
|
const PROJECT_LOCAL_PREFIX = 'agents/settings/contexts/';
|
|
57
64
|
|
|
58
65
|
const HARD_FLOOR_RULES: ReadonlySet<string> = new Set([
|
|
@@ -217,7 +224,12 @@ function main(): number {
|
|
|
217
224
|
const warnings: string[] = [];
|
|
218
225
|
const graph: Record<string, string[]> = {};
|
|
219
226
|
|
|
220
|
-
|
|
227
|
+
const _files = collect_files();
|
|
228
|
+
// Gate-coverage contract (src/config/gate-coverage.yml): count FILES INSPECTED,
|
|
229
|
+
// not declarers — "0 declarers" over 400 files is a legitimate result, "0 files"
|
|
230
|
+
// is blindness. Reporting declarers here would have hidden the dead root.
|
|
231
|
+
process.stdout.write(`scanned: ${String(_files.length)}\n`);
|
|
232
|
+
for (const f of _files) {
|
|
221
233
|
const fm = parse_frontmatter(f);
|
|
222
234
|
const lazyRaw = fm['load_context'];
|
|
223
235
|
const eagerRaw = fm['load_context_eager'];
|