@event4u/agent-config 9.13.0 → 9.15.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 +113 -67
- package/README.md +9 -1
- package/dist/agent-src/commands/agents/audit.md +7 -7
- package/dist/agent-src/commands/agents/optimize.md +1 -1
- package/dist/agent-src/commands/analyze/reference-repo.md +1 -1
- package/dist/agent-src/commands/chat-history.md +1 -0
- package/dist/agent-src/commands/context.md +1 -0
- package/dist/agent-src/commands/council.md +1 -1
- package/dist/agent-src/commands/ghostwriter.md +1 -1
- package/dist/agent-src/commands/memory.md +2 -1
- package/dist/agent-src/commands/module.md +1 -0
- package/dist/agent-src/commands/optimize/agents-dir.md +1 -1
- package/dist/agent-src/commands/optimize/deep.md +150 -0
- package/dist/agent-src/commands/optimize/skills.md +5 -5
- package/dist/agent-src/commands/optimize.md +12 -6
- package/dist/agent-src/commands/override.md +1 -0
- package/dist/agent-src/commands/prediction-pool.md +4 -4
- package/dist/agent-src/commands/research/deep.md +1 -1
- package/dist/agent-src/commands/roadmap.md +2 -1
- package/dist/agent-src/commands/rule-compliance-audit.md +1 -1
- package/dist/agent-src/commands/tdd.md +1 -0
- package/dist/agent-src/commands/tests.md +1 -0
- package/dist/agent-src/commands/upstream-contribute.md +5 -5
- package/dist/agent-src/commands/video/from-script.md +1 -1
- package/dist/agent-src/commands/video/from-song.md +1 -1
- package/dist/agent-src/commands/video/scene.md +1 -1
- package/dist/agent-src/commands/video/stitch.md +1 -1
- package/dist/agent-src/commands/video/storyboard.md +1 -1
- package/dist/agent-src/commands/worktree/create.md +2 -1
- package/dist/agent-src/contexts/augment-infrastructure.md +1 -1
- package/dist/agent-src/contexts/contracts/artifact-engagement-flow.md +5 -5
- package/dist/agent-src/contexts/contracts/command-suggestion-flow.md +3 -3
- package/dist/agent-src/contexts/contracts/emergency-triage-block.md +2 -2
- package/dist/agent-src/contexts/execution/roadmap-process-loop.md +4 -3
- package/dist/agent-src/contexts/execution/subagent-routing.md +19 -0
- package/dist/agent-src/contexts/override-system.md +1 -1
- package/dist/agent-src/ghostwriter/fictional-fixture-v1.md +1 -1
- package/dist/agent-src/rules/augment-edit-discipline.md +1 -1
- package/dist/agent-src/rules/autonomous-execution.md +2 -1
- package/dist/agent-src/rules/brand-consistency.md +1 -0
- package/dist/agent-src/rules/brand-source-of-truth.md +1 -0
- package/dist/agent-src/rules/code-comment-discipline.md +2 -0
- package/dist/agent-src/rules/communication-through-line.md +1 -0
- package/dist/agent-src/rules/content-quoting-floor.md +1 -0
- package/dist/agent-src/rules/cross-source-consistency.md +0 -1
- package/dist/agent-src/rules/downstream-changes.md +1 -0
- package/dist/agent-src/rules/external-code-graph-interop.md +1 -0
- package/dist/agent-src/rules/external-reference-deep-dive.md +1 -0
- package/dist/agent-src/rules/fast-path-marker-visibility.md +1 -0
- package/dist/agent-src/rules/finance-safety-floor.md +0 -1
- package/dist/agent-src/rules/icon-consistency.md +1 -0
- package/dist/agent-src/rules/image-likeness-and-rights.md +1 -0
- package/dist/agent-src/rules/improve-before-implement.md +1 -1
- package/dist/agent-src/rules/invite-challenge.md +1 -0
- package/dist/agent-src/rules/lethal-trifecta-guard.md +1 -0
- package/dist/agent-src/rules/low-impact-corpus-privacy-floor.md +1 -0
- package/dist/agent-src/rules/markdown-safe-codeblocks.md +1 -0
- package/dist/agent-src/rules/media-governance-routing.md +4 -3
- package/dist/agent-src/rules/media-sync-ground-truth.md +1 -0
- package/dist/agent-src/rules/no-attribution-footers.md +1 -0
- package/dist/agent-src/rules/no-decorative-emojis-in-git-surfaces.md +1 -0
- package/dist/agent-src/rules/no-pr-progress-comments.md +1 -0
- package/dist/agent-src/rules/onboarding-gate.md +1 -0
- package/dist/agent-src/rules/output-discipline.md +1 -0
- package/dist/agent-src/rules/prefer-enums-over-literals.md +1 -0
- package/dist/agent-src/rules/preservation-guard.md +1 -0
- package/dist/agent-src/rules/question-not-instruction.md +1 -0
- package/dist/agent-src/rules/roadmap-ci-steps-policy.md +1 -0
- package/dist/agent-src/rules/role-mode-adherence.md +1 -0
- package/dist/agent-src/rules/runtime-safety.md +1 -0
- package/dist/agent-src/rules/security-sensitive-stop.md +1 -0
- package/dist/agent-src/rules/session-canary.md +9 -2
- package/dist/agent-src/rules/source-confidentiality.md +1 -0
- package/dist/agent-src/rules/source-of-truth.md +1 -0
- package/dist/agent-src/rules/spreadsheet-source-quality.md +1 -0
- package/dist/agent-src/rules/think-before-action.md +1 -0
- package/dist/agent-src/rules/token-budget-discipline.md +1 -0
- package/dist/agent-src/rules/token-efficiency.md +1 -0
- package/dist/agent-src/rules/token-optimizer-maintenance.md +5 -5
- package/dist/agent-src/rules/tool-safety.md +1 -0
- package/dist/agent-src/rules/untrusted-input-defense.md +2 -0
- package/dist/agent-src/rules/user-interaction.md +1 -0
- package/dist/agent-src/rules/user-interrupt-priority.md +1 -0
- package/dist/agent-src/skills/adr-create/SKILL.md +1 -1
- package/dist/agent-src/skills/check-refs/SKILL.md +1 -1
- package/dist/agent-src/skills/competitive-positioning/SKILL.md +1 -1
- package/dist/agent-src/skills/condense-memory/SKILL.md +1 -1
- package/dist/agent-src/skills/context-authoring/SKILL.md +1 -1
- package/dist/agent-src/skills/copilot-agents-optimization/SKILL.md +1 -1
- package/dist/agent-src/skills/description-assist/SKILL.md +1 -1
- package/dist/agent-src/skills/github-ci/SKILL.md +1 -0
- package/dist/agent-src/skills/guideline-writing/SKILL.md +1 -1
- package/dist/agent-src/skills/lint-skills/SKILL.md +1 -1
- package/dist/agent-src/skills/readme-reviewer/SKILL.md +1 -1
- package/dist/agent-src/skills/roadmap-management/SKILL.md +8 -1
- package/dist/agent-src/skills/roadmap-writing/SKILL.md +1 -1
- package/dist/agent-src/skills/skill-improvement-pipeline/SKILL.md +1 -1
- package/dist/agent-src/skills/skill-writing/SKILL.md +4 -4
- package/dist/agent-src/skills/token-optimizer/SKILL.md +4 -4
- package/dist/agent-src/skills/upstream-contribute/SKILL.md +6 -6
- package/dist/agent-src/skills/using-git-worktrees/SKILL.md +25 -0
- package/dist/agent-src/templates/AGENTS.md +1 -1
- package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
- package/dist/agent-src/templates/command.md +1 -1
- package/dist/agent-src/templates/copilot-instructions.md +3 -3
- package/dist/agent-src/templates/rule.md +2 -2
- package/dist/agent-src/templates/scripts/work_engine/orchestration.ts +1 -1
- package/dist/agent-src/templates/skill-archive-note.md +5 -5
- package/dist/agent-src/templates/skill.md +1 -1
- package/dist/cli/agent-config.js +66 -19
- package/dist/cli/agent-config.js.map +1 -1
- package/dist/cli/main.js +9 -4
- package/dist/cli/main.js.map +1 -1
- package/dist/cli/python/workspace_drive.js +2 -0
- package/dist/cli/python/workspace_drive.js.map +1 -1
- package/dist/cli/python/workspace_drive_health.js +2 -0
- package/dist/cli/python/workspace_drive_health.js.map +1 -1
- package/dist/cli/python/workspace_hosts.js +2 -0
- package/dist/cli/python/workspace_hosts.js.map +1 -1
- package/dist/cli/python/workspace_sessions.js +2 -0
- package/dist/cli/python/workspace_sessions.js.map +1 -1
- package/dist/cli/registry.js +1 -0
- package/dist/cli/registry.js.map +1 -1
- package/dist/cli-delegate/{chunk-A3HJ6NTI.js → chunk-63WSP6YZ.js} +2 -1
- package/dist/cli-delegate/{chunk-EJYIGZ3T.js → chunk-BKO2AZ7Z.js} +1 -1
- package/dist/cli-delegate/{chunk-QFKQLQ7X.js → chunk-H574KOUH.js} +340 -462
- package/dist/cli-delegate/chunk-NXFWFAYV.js +339 -0
- package/dist/cli-delegate/{chunk-TEGJU2JX.js → chunk-ZXXJLL5K.js} +2 -2
- package/dist/cli-delegate/cmd_conformance.js +29 -23
- package/dist/cli-delegate/cmd_doctor.js +3 -3
- package/dist/cli-delegate/cmd_export.js +4 -4
- package/dist/cli-delegate/cmd_preflight.js +2 -2
- package/dist/cli-delegate/cmd_prune.js +4 -4
- package/dist/cli-delegate/cmd_settings_migrate.js +4 -4
- package/dist/cli-delegate/cmd_sync.js +4 -4
- package/dist/cli-delegate/cmd_uninstall.js +4 -4
- package/dist/cli-delegate/cmd_validate.js +4 -4
- package/dist/discovery/deprecation-report.md +1 -1
- package/dist/discovery/discovery-manifest.json +163 -103
- 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 +3 -3
- package/dist/discovery/workspaces.json +5 -4
- package/dist/hooks/dispatch.js +729 -430
- package/dist/install/install.mjs +700 -625
- package/dist/install/rule_scope.js +18 -2
- package/dist/install/rule_scope.js.map +1 -1
- package/dist/mcp/registry-manifest.json +3 -3
- package/dist/mcp/server.json +2 -2
- package/dist/mcp/server.mjs +564 -424
- package/dist/router.json +1 -1
- package/dist/scripts/_lib/claude_settings_hooks.js +30 -5
- package/dist/scripts/_lib/claude_settings_hooks.js.map +1 -1
- package/dist/scripts/_lib/scoped_projection.js +223 -0
- package/dist/scripts/_lib/scoped_projection.js.map +1 -0
- package/dist/scripts/_lib/surface_tiers.js +125 -0
- package/dist/scripts/_lib/surface_tiers.js.map +1 -0
- package/dist/scripts/_lib/token_count.js +166 -0
- package/dist/scripts/_lib/token_count.js.map +1 -0
- package/dist/scripts/_lib/trigger_eval_floors.js +8 -0
- package/dist/scripts/_lib/trigger_eval_floors.js.map +1 -1
- package/dist/scripts/condense.js +139 -9
- package/dist/scripts/condense.js.map +1 -1
- package/dist/scripts/project_thin_rules.js +450 -0
- package/dist/scripts/project_thin_rules.js.map +1 -0
- package/dist/server/routes/workspace.js +2 -0
- package/dist/server/routes/workspace.js.map +1 -1
- package/dist/server/schemas/settings.js +9 -3
- package/dist/server/schemas/settings.js.map +1 -1
- package/dist/ui/assets/index-DJY_vQVn.js +40 -0
- package/dist/ui/assets/index-DJY_vQVn.js.map +1 -0
- package/dist/ui/index.html +1 -1
- package/docs/guidelines/agent-infra/roadmap-progress-mechanics.md +1 -1
- package/package.json +11 -11
- package/src/agent-src/templates/AGENTS.md +1 -1
- package/src/agent-src/templates/scripts/work_engine/orchestration.ts +1 -1
- package/src/config/agent-settings.template.yml +59 -11
- package/src/config/ci-local-parity.yml +8 -0
- package/src/config/discovery/unassigned-artefacts.yml +23 -25
- package/src/config/evaluator-budgets.json +35 -5
- package/src/config/gate-coverage.yml +147 -0
- package/src/config/gate-violation-baselines.json +25 -0
- package/src/install/rule_scope.ts +27 -2
- package/src/scripts/_cli/cmd_conformance.ts +13 -24
- package/src/scripts/_dispatch.bash +10 -0
- package/src/scripts/_lib/claude_settings_hooks.ts +30 -4
- package/src/scripts/_lib/exec_evidence.ts +5 -0
- package/src/scripts/_lib/gate_baseline.ts +213 -0
- package/src/scripts/_lib/scoped_projection.ts +262 -0
- package/src/scripts/_lib/tier_budget_routing.ts +234 -0
- package/src/scripts/_lib/tool_adapter_registry.ts +8 -1
- package/src/scripts/_lib/trigger_eval_floors.ts +9 -0
- package/src/scripts/_lib/trigger_routers.ts +17 -5
- package/src/scripts/adr/regenerate_index.ts +4 -3
- package/src/scripts/ai-video/lib/parse-blueprint.sh +1 -1
- package/src/scripts/ai_council/compile_corpus.ts +1 -1
- package/src/scripts/audit_cloud_compatibility.ts +37 -2
- package/src/scripts/audit_initial_context.ts +53 -19
- package/src/scripts/audit_likelihood.ts +31 -2
- package/src/scripts/audit_user_type_axis.ts +58 -11
- package/src/scripts/bench_hook_latency.ts +172 -41
- package/src/scripts/build_mcp_catalog.ts +12 -1
- package/src/scripts/check_always_budget.ts +122 -11
- package/src/scripts/check_artefact_count_messaging.ts +128 -15
- package/src/scripts/check_augment_description_cap.ts +24 -3
- package/src/scripts/check_ci_strict_superset.ts +150 -0
- package/src/scripts/check_cluster_patterns.ts +100 -0
- package/src/scripts/check_consumer_scope_flip.ts +26 -4
- package/src/scripts/check_evaluator_budgets.ts +198 -19
- package/src/scripts/check_gate_coverage.ts +337 -1
- package/src/scripts/check_generator_output_coverage.ts +2 -1
- package/src/scripts/check_iron_law_prominence.ts +1 -1
- package/src/scripts/check_kernel_rule_bundle.ts +1 -1
- package/src/scripts/check_no_new_legacy_path.ts +167 -3
- package/src/scripts/check_pack_size.ts +18 -4
- package/src/scripts/check_portability.ts +37 -7
- package/src/scripts/check_references.ts +19 -3
- package/src/scripts/check_release_pr_shape.ts +13 -0
- package/src/scripts/check_token_regression.ts +9 -2
- package/src/scripts/condense.ts +155 -12
- package/src/scripts/cost/budget.mjs +63 -2
- package/src/scripts/count_scoped_projection.ts +104 -0
- package/src/scripts/evaluator_umbrella.sh +34 -1
- package/src/scripts/generate_capability_matrix.ts +8 -0
- package/src/scripts/generate_router_coverage_from_matrix.ts +155 -0
- package/src/scripts/hook_manifest.yaml +17 -3
- package/src/scripts/hooks/block_config_weakening.ts +337 -0
- package/src/scripts/hooks/concern_registry.ts +2 -0
- package/src/scripts/hooks/probe_throttle.ts +75 -0
- package/src/scripts/install-hooks.sh +24 -2
- package/src/scripts/install.ts +15 -92
- package/src/scripts/lint_agents_md.ts +9 -3
- package/src/scripts/lint_archived_skills.ts +1 -1
- package/src/scripts/lint_artefact_frontmatter.ts +185 -37
- package/src/scripts/lint_command_routing.ts +21 -52
- package/src/scripts/lint_command_verbs.ts +47 -28
- package/src/scripts/lint_context_spine_usage.ts +38 -12
- package/src/scripts/lint_framework_leakage.ts +25 -6
- package/src/scripts/lint_handoffs.ts +49 -2
- package/src/scripts/lint_media_policy_linkage.ts +62 -30
- package/src/scripts/lint_namespace.ts +92 -7
- package/src/scripts/lint_new_skill_gate.ts +39 -31
- package/src/scripts/lint_originality_shingles.ts +7 -1
- package/src/scripts/lint_override_kernel_guard.ts +21 -3
- package/src/scripts/lint_pack_boundaries.ts +461 -91
- package/src/scripts/lint_pack_dependencies.ts +24 -57
- package/src/scripts/lint_persona_governance.ts +1 -1
- package/src/scripts/lint_role_experiences.ts +34 -5
- package/src/scripts/lint_trigger_precision.ts +142 -0
- package/src/scripts/mcp_server/consumer_tool_catalog.json +12 -201
- package/src/scripts/mcp_server/tools.ts +1 -1
- package/src/scripts/prepack-check.mjs +40 -1
- package/src/scripts/prepack_router_targets.d.mts +7 -0
- package/src/scripts/prepack_router_targets.mjs +93 -0
- package/src/scripts/print_required_checks.ts +72 -18
- package/src/scripts/profile_staleness_hook.ts +12 -0
- package/src/scripts/record_evaluator_measurements.mjs +105 -0
- package/src/scripts/recruit_preflight.sh +1 -1
- package/src/scripts/release.ts +105 -1
- package/src/scripts/router_target_paths.d.mts +4 -0
- package/src/scripts/router_target_paths.mjs +69 -0
- package/src/scripts/router_telemetry.ts +52 -1
- package/src/scripts/routing_doctor.ts +485 -0
- package/src/scripts/rule_trigger_eval.ts +403 -0
- package/src/scripts/schemas/rule.schema.json +5 -1
- package/src/scripts/session_canary_hook.ts +96 -5
- package/src/scripts/skill_linter.ts +90 -42
- package/src/scripts/smoke/skills.sh +1 -1
- package/src/scripts/sweep_dead_scan_roots.ts +356 -10
- package/src/scripts/trigger_coverage.ts +13 -2
- package/src/scripts/update_counts.ts +91 -12
- package/src/scripts/validate_telegraph_carveouts.ts +1 -1
- package/src/scripts/wrapper_freshness_hook.ts +14 -0
- package/src/server/schemas/settings.ts +19 -5
- package/dist/cli-delegate/chunk-TJIAM5FW.js +0 -133
- package/dist/ui/assets/index-BpSQt5vm.js +0 -40
- package/dist/ui/assets/index-BpSQt5vm.js.map +0 -1
package/src/scripts/condense.ts
CHANGED
|
@@ -35,15 +35,23 @@ import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
|
35
35
|
import * as YAML from 'yaml';
|
|
36
36
|
|
|
37
37
|
import {
|
|
38
|
+
_domains_command_logical,
|
|
38
39
|
artefact_roots as _agent_src_artefact_roots,
|
|
39
40
|
iter_all_sources as _agent_src_iter_all_sources,
|
|
40
41
|
resolve_logical as _agent_src_resolve_logical,
|
|
41
42
|
strip_source_prefix,
|
|
42
43
|
} from './_lib/agent_src.js';
|
|
44
|
+
// Import-safety note: `project_thin_rules` guards its CLI entry
|
|
45
|
+
// (`_isCliEntry()` before `process.exit(main())`), so importing it here is
|
|
46
|
+
// side-effect-free. condense.ts is bundled into the installer, where a bare
|
|
47
|
+
// top-level `process.exit` would fire at consumer runtime — the documented
|
|
48
|
+
// bundled-CLI-entry-guard landmine. Verified before wiring, not assumed.
|
|
49
|
+
import { build_thin } from './project_thin_rules.js';
|
|
43
50
|
import { build_claude_hook_matrix } from './_lib/claude_settings_hooks.js';
|
|
44
51
|
import { is_claude_builtin_name } from './_lib/claude_builtin_names.js';
|
|
45
52
|
import { project_settings_path, load_agent_settings } from './_lib/agent_settings.js';
|
|
46
53
|
import { rule_is_compile_enabled } from './_lib/compile_time_toggles.js';
|
|
54
|
+
import { resolve_rule_pack_scope } from './_lib/scoped_projection.js';
|
|
47
55
|
import { info, success, flush_summary, resolve_level } from './_lib/script_output.js';
|
|
48
56
|
import {
|
|
49
57
|
TIER_TO_CLAUDE_MODEL as _TIER_TO_CLAUDE_MODEL,
|
|
@@ -987,13 +995,16 @@ export { _HRR_BANNER_MARKER };
|
|
|
987
995
|
// (rule-router contract § Schema v2). Untagged rules fail safe: they ship.
|
|
988
996
|
// ADR-040: projection-time filtering only, no runtime resolver.
|
|
989
997
|
|
|
990
|
-
function
|
|
998
|
+
function _read_projection_raw(key: string): unknown {
|
|
991
999
|
const data = load_agent_settings({ project_path: MODULE_STATE.SETTINGS_FILE });
|
|
992
1000
|
const proj = data['projection'];
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
1001
|
+
return typeof proj === 'object' && proj !== null && !Array.isArray(proj)
|
|
1002
|
+
? (proj as Record<string, unknown>)[key]
|
|
1003
|
+
: null;
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1006
|
+
function _read_projection_list(key: string): string[] | null {
|
|
1007
|
+
const value = _read_projection_raw(key);
|
|
997
1008
|
if (Array.isArray(value) && value.length > 0) {
|
|
998
1009
|
return value.map((v) => String(v));
|
|
999
1010
|
}
|
|
@@ -1004,8 +1015,28 @@ function _read_rule_workspaces(): string[] | null {
|
|
|
1004
1015
|
return _read_projection_list('rule_workspaces');
|
|
1005
1016
|
}
|
|
1006
1017
|
|
|
1018
|
+
/** `runtime.active_packs` overlay, empty when unset (fresh-install default). */
|
|
1019
|
+
function _read_runtime_active_packs(): string[] {
|
|
1020
|
+
const data = load_agent_settings({ project_path: MODULE_STATE.SETTINGS_FILE });
|
|
1021
|
+
const rt = data['runtime'];
|
|
1022
|
+
const value =
|
|
1023
|
+
typeof rt === 'object' && rt !== null && !Array.isArray(rt)
|
|
1024
|
+
? (rt as Record<string, unknown>)['active_packs']
|
|
1025
|
+
: null;
|
|
1026
|
+
return Array.isArray(value) ? value.map((v) => String(v)) : [];
|
|
1027
|
+
}
|
|
1028
|
+
|
|
1029
|
+
/**
|
|
1030
|
+
* Pack scope for the rule layer. `rule_packs: auto` derives the active-pack
|
|
1031
|
+
* set (the same one the skill/command prune uses) instead of a hand-typed id
|
|
1032
|
+
* list; anything else keeps the historical list-or-inactive semantics.
|
|
1033
|
+
*/
|
|
1007
1034
|
function _read_rule_packs(): string[] | null {
|
|
1008
|
-
return
|
|
1035
|
+
return resolve_rule_pack_scope(
|
|
1036
|
+
_read_projection_raw('rule_packs'),
|
|
1037
|
+
MODULE_STATE.PROJECT_ROOT,
|
|
1038
|
+
_read_runtime_active_packs(),
|
|
1039
|
+
);
|
|
1009
1040
|
}
|
|
1010
1041
|
|
|
1011
1042
|
/**
|
|
@@ -1044,11 +1075,19 @@ export function generate_rule_symlinks(): number {
|
|
|
1044
1075
|
|
|
1045
1076
|
let thin_files: Record<string, string> | null = null;
|
|
1046
1077
|
if (_lean_projection_mode() === 'thin') {
|
|
1047
|
-
//
|
|
1048
|
-
//
|
|
1049
|
-
//
|
|
1050
|
-
|
|
1051
|
-
|
|
1078
|
+
// DEAD-SWITCH REPAIR (road-to-renewal-foundation Phase 2). This branch
|
|
1079
|
+
// used to THROW: the port was skipped as "out of scope, not exercised by
|
|
1080
|
+
// golden parity", which left a documented, settings-selectable mode that
|
|
1081
|
+
// could only ever crash. `build_thin` was ported and has been present the
|
|
1082
|
+
// whole time — the wiring was the only thing missing.
|
|
1083
|
+
//
|
|
1084
|
+
// Scope note: this repairs the SWITCH only. The default stays
|
|
1085
|
+
// `eager-all`. Flipping it is parked behind the thin-projection honest
|
|
1086
|
+
// null (thin win-rate 36.2% < the 48% pre-registered threshold), and
|
|
1087
|
+
// nothing here disturbs that verdict — but a mode that throws cannot
|
|
1088
|
+
// even be re-measured, which is why the repair earns its place alone.
|
|
1089
|
+
thin_files = Object.fromEntries(
|
|
1090
|
+
build_thin(MODULE_STATE.RULES_SOURCE, _read_rule_workspaces()),
|
|
1052
1091
|
);
|
|
1053
1092
|
}
|
|
1054
1093
|
|
|
@@ -1146,6 +1185,9 @@ function _WINDSURF_WORKFLOWS_DIR(): string {
|
|
|
1146
1185
|
function _CURSOR_COMMANDS_DIR(): string {
|
|
1147
1186
|
return path.join(MODULE_STATE.PROJECT_ROOT, '.cursor', 'commands');
|
|
1148
1187
|
}
|
|
1188
|
+
function _CLAUDE_COMMANDS_DIR(): string {
|
|
1189
|
+
return path.join(MODULE_STATE.PROJECT_ROOT, '.claude', 'commands');
|
|
1190
|
+
}
|
|
1149
1191
|
|
|
1150
1192
|
function _parse_frontmatter(content: string): [Record<string, unknown>, string] {
|
|
1151
1193
|
if (!content.startsWith('---')) {
|
|
@@ -1566,6 +1608,92 @@ export function extract_description_from_md(content: string): string {
|
|
|
1566
1608
|
return '';
|
|
1567
1609
|
}
|
|
1568
1610
|
|
|
1611
|
+
/**
|
|
1612
|
+
* The `commands/<sub>` path of a command source file, or `null` when it is a
|
|
1613
|
+
* FLAT command (`commands/<name>.md`).
|
|
1614
|
+
*
|
|
1615
|
+
* A nested path is what Claude Code renders as `/cluster:sub`. Flat command
|
|
1616
|
+
* FILES are not registered by Claude Code (probed ≤ 2.1.204 — see the
|
|
1617
|
+
* flat-command mitigation in `install.ts`), which is why flat commands keep a
|
|
1618
|
+
* skill wrapper and nested ones do not need one.
|
|
1619
|
+
*/
|
|
1620
|
+
function _nested_command_subpath(source_file: string): string | null {
|
|
1621
|
+
const rel = _relativeToPosix(source_file, MODULE_STATE.PROJECT_ROOT);
|
|
1622
|
+
const logical = _domains_command_logical(rel);
|
|
1623
|
+
if (logical === null || !logical.startsWith('commands/')) {
|
|
1624
|
+
return null;
|
|
1625
|
+
}
|
|
1626
|
+
const sub = logical.slice('commands/'.length).replace(/\.md$/, '');
|
|
1627
|
+
return sub.includes('/') ? sub : null;
|
|
1628
|
+
}
|
|
1629
|
+
|
|
1630
|
+
/**
|
|
1631
|
+
* Project-scope `.claude/commands/<cluster>/<sub>.md` — the colon form.
|
|
1632
|
+
*
|
|
1633
|
+
* Before this, the package repo's own `.claude/` carried NO commands
|
|
1634
|
+
* directory: every command reached Claude Code as a hyphen-named skill
|
|
1635
|
+
* wrapper in `.claude/skills/`, while the colon form existed only in the
|
|
1636
|
+
* user-global tree written by `install.ts`. Every clustered command was
|
|
1637
|
+
* therefore listed TWICE for anyone with a global install
|
|
1638
|
+
* (`roadmap-process-full` AND `roadmap:process-full`), costing 4,214 GPT tok
|
|
1639
|
+
* of always-loaded catalog for zero added reach — measured 2026-08-02.
|
|
1640
|
+
*
|
|
1641
|
+
* Emitting the nested form here lets `generate_claude_commands` stop wrapping
|
|
1642
|
+
* the clustered commands: one listing, same reachability, and reachability no
|
|
1643
|
+
* longer depends on a global deploy having run. Claude Code dedupes project
|
|
1644
|
+
* and user scope by name, so the two copies of `/cluster:sub` collapse.
|
|
1645
|
+
*
|
|
1646
|
+
* ADR-003 (colon canonical for clusters) and ADR-044 (flat commands stay
|
|
1647
|
+
* hyphenated) both hold: flat commands are untouched and keep their wrapper.
|
|
1648
|
+
*/
|
|
1649
|
+
export function generate_claude_project_commands(
|
|
1650
|
+
active_command_slugs: ReadonlySet<string> | null = null,
|
|
1651
|
+
): number {
|
|
1652
|
+
if (!_isDir(path.join(MODULE_STATE.PROJECT_ROOT, 'src', 'domains'))) {
|
|
1653
|
+
return 0;
|
|
1654
|
+
}
|
|
1655
|
+
const nested: Array<[string, string]> = [];
|
|
1656
|
+
for (const [source_file, slug] of iterCommands()) {
|
|
1657
|
+
if (active_command_slugs !== null && !active_command_slugs.has(slug)) {
|
|
1658
|
+
continue;
|
|
1659
|
+
}
|
|
1660
|
+
const sub = _nested_command_subpath(source_file);
|
|
1661
|
+
if (sub !== null) {
|
|
1662
|
+
nested.push([source_file, sub]);
|
|
1663
|
+
}
|
|
1664
|
+
}
|
|
1665
|
+
|
|
1666
|
+
const target_root = _CLAUDE_COMMANDS_DIR();
|
|
1667
|
+
const valid = new Set(nested.map(([, sub]) => `${sub}.md`));
|
|
1668
|
+
// Sweep stale links first: a renamed or deleted command must not linger as
|
|
1669
|
+
// a dangling `/cluster:sub`.
|
|
1670
|
+
if (_exists(target_root)) {
|
|
1671
|
+
for (const item of _rglobSorted(target_root, '*.md')) {
|
|
1672
|
+
const rel = _relativeToPosix(item, target_root);
|
|
1673
|
+
if (!valid.has(rel)) {
|
|
1674
|
+
fs.unlinkSync(item);
|
|
1675
|
+
}
|
|
1676
|
+
}
|
|
1677
|
+
}
|
|
1678
|
+
|
|
1679
|
+
let count = 0;
|
|
1680
|
+
for (const [source_file, sub] of nested) {
|
|
1681
|
+
const link = path.join(target_root, `${sub}.md`);
|
|
1682
|
+
_mkdirp(path.dirname(link));
|
|
1683
|
+
if (_existsOrSymlink(link)) {
|
|
1684
|
+
fs.unlinkSync(link);
|
|
1685
|
+
}
|
|
1686
|
+
// path.relative, not a hand-counted `../` run: the nesting depth
|
|
1687
|
+
// varies (2 and 3 segments both occur) and an off-by-one here writes
|
|
1688
|
+
// a dangling link that still LOOKS right in `ls -l`.
|
|
1689
|
+
const rel_target = path.relative(path.dirname(link), source_file);
|
|
1690
|
+
fs.symlinkSync(rel_target, link);
|
|
1691
|
+
count += 1;
|
|
1692
|
+
}
|
|
1693
|
+
info(` ✅ Linked ${count} \`.claude/commands/**/*.md\` files (colon form)`);
|
|
1694
|
+
return count;
|
|
1695
|
+
}
|
|
1696
|
+
|
|
1569
1697
|
export function generate_claude_commands(active_command_slugs: ReadonlySet<string> | null = null): number {
|
|
1570
1698
|
if (!_isDir(path.join(MODULE_STATE.PROJECT_ROOT, 'src', 'domains'))) {
|
|
1571
1699
|
process.stderr.write(' ⚠️ src/domains/ not found — skipping commands\n');
|
|
@@ -1597,6 +1725,15 @@ export function generate_claude_commands(active_command_slugs: ReadonlySet<strin
|
|
|
1597
1725
|
skipped += 1;
|
|
1598
1726
|
continue;
|
|
1599
1727
|
}
|
|
1728
|
+
// A clustered command already reaches Claude Code as `/cluster:sub`
|
|
1729
|
+
// from `.claude/commands/` (generate_claude_project_commands). A
|
|
1730
|
+
// hyphen wrapper on top would be a second listing of the same command
|
|
1731
|
+
// — the double-listing this de-duplicates. Flat commands still need
|
|
1732
|
+
// the wrapper: Claude Code does not register flat command FILES.
|
|
1733
|
+
if (_nested_command_subpath(source_file) !== null) {
|
|
1734
|
+
skipped += 1;
|
|
1735
|
+
continue;
|
|
1736
|
+
}
|
|
1600
1737
|
// Never claim a `/name` that is a Claude Code built-in command or
|
|
1601
1738
|
// bundled skill — a same-name entry shadows the built-in (e.g. a
|
|
1602
1739
|
// `review` entry hides Claude Code's own /review). Nested
|
|
@@ -2144,6 +2281,11 @@ function _generate_tools_inner(
|
|
|
2144
2281
|
generate_gemini_md();
|
|
2145
2282
|
}
|
|
2146
2283
|
const skills = _tool_active('claude-code') ? generate_claude_skills(skill_names) : 0;
|
|
2284
|
+
// Colon form first: `generate_claude_commands` skips every command this
|
|
2285
|
+
// one emitted, so the ordering is what keeps the two from double-listing.
|
|
2286
|
+
const claude_cmds = _tool_active('claude-code')
|
|
2287
|
+
? generate_claude_project_commands(cmd_slugs)
|
|
2288
|
+
: 0;
|
|
2147
2289
|
const commands = _tool_active('claude-code') ? generate_claude_commands(cmd_slugs) : 0;
|
|
2148
2290
|
const subagents = _tool_active('claude-code') ? generate_claude_subagents() : 0;
|
|
2149
2291
|
const subagent_contexts = generate_subagent_host_contexts();
|
|
@@ -2157,7 +2299,8 @@ function _generate_tools_inner(
|
|
|
2157
2299
|
const windsurf_wf = _tool_active('windsurf') ? generate_windsurf_workflows(cmd_slugs) : 0;
|
|
2158
2300
|
const summary =
|
|
2159
2301
|
`✅ generate-tools — rules=${rules} skills=${skills} ` +
|
|
2160
|
-
`
|
|
2302
|
+
`claude_commands=${claude_cmds} command_skills=${commands} ` +
|
|
2303
|
+
`subagents=${subagents} subagent_contexts=${subagent_contexts} plugin_cmd_skills=${plugin_cmd_skills} ` +
|
|
2161
2304
|
`plugin_hooks=${plugin_hooks} ` +
|
|
2162
2305
|
`personas=${personas} user_types=${user_types} ` +
|
|
2163
2306
|
`cursor_mdc=${cursor_mdc} windsurf_rules=${windsurf_modern} ` +
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
// Env: BUDGET_STORE, BUDGET_CONFIG, BUDGET_PERIOD={today|week|month|all}, BUDGET_QUIET=1
|
|
11
11
|
|
|
12
12
|
import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'node:fs';
|
|
13
|
-
import { dirname } from 'node:path';
|
|
13
|
+
import { dirname, join } from 'node:path';
|
|
14
14
|
|
|
15
15
|
const STORE = process.env.BUDGET_STORE || 'agents/cost-tracking/sessions.jsonl';
|
|
16
16
|
const CONFIG = process.env.BUDGET_CONFIG || 'agents/cost-tracking/budget.json';
|
|
@@ -198,14 +198,75 @@ function cmdCheck() {
|
|
|
198
198
|
if (hardStop) process.exit(1);
|
|
199
199
|
}
|
|
200
200
|
|
|
201
|
+
// --- Per-tier budget state (docs/contracts/budget-routing.md) -------------
|
|
202
|
+
// v1 window for per-tier ceilings: rolling 24h. Sums ledger entries tagged
|
|
203
|
+
// `tier:` plus pending reserves from tier-reserves.jsonl (the atomic-permit
|
|
204
|
+
// file) so a concurrent dispatch cannot read a stale "available".
|
|
205
|
+
|
|
206
|
+
function loadPerTierCeilings() {
|
|
207
|
+
const out = { cheap: null, medium: null, strong: null };
|
|
208
|
+
if (!existsSync(SETTINGS)) return out;
|
|
209
|
+
let inPerTier = false;
|
|
210
|
+
for (const line of readFileSync(SETTINGS, 'utf-8').split('\n')) {
|
|
211
|
+
if (/^ {4}per_tier:/.test(line)) { inPerTier = true; continue; }
|
|
212
|
+
if (inPerTier) {
|
|
213
|
+
const m = line.match(/^ {6}(cheap|medium|strong):\s*([0-9.]+|null)\s*(#.*)?$/);
|
|
214
|
+
if (m) {
|
|
215
|
+
out[m[1]] = m[2] === 'null' ? null : Number(m[2]);
|
|
216
|
+
continue;
|
|
217
|
+
}
|
|
218
|
+
if (/^ {0,5}\S/.test(line)) inPerTier = false;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
return out;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function cmdTier(rest) {
|
|
225
|
+
const tier = rest[0];
|
|
226
|
+
if (!['cheap', 'medium', 'strong'].includes(tier)) {
|
|
227
|
+
console.error('usage: budget.mjs tier {cheap|medium|strong}');
|
|
228
|
+
process.exit(2);
|
|
229
|
+
}
|
|
230
|
+
const dayMs = 24 * 3600 * 1000;
|
|
231
|
+
const now = Date.now();
|
|
232
|
+
const inWindow = (ts) => ts && (now - new Date(ts).getTime()) < dayMs;
|
|
233
|
+
const spent = loadSessions()
|
|
234
|
+
.filter((s) => s.tier === tier && inWindow(s.capturedAt || s.endedAt || s.ts))
|
|
235
|
+
.reduce((acc, s) => acc + (Number(s.total_cost_usd ?? s.cost_usd ?? 0) || 0), 0);
|
|
236
|
+
let reserved = 0;
|
|
237
|
+
const reservePath = join(dirname(STORE), 'tier-reserves.jsonl');
|
|
238
|
+
if (existsSync(reservePath)) {
|
|
239
|
+
for (const line of readFileSync(reservePath, 'utf-8').split('\n')) {
|
|
240
|
+
if (!line.trim()) continue;
|
|
241
|
+
try {
|
|
242
|
+
const e = JSON.parse(line);
|
|
243
|
+
if (e.tier === tier && e.status === 'pending' && (now - e.ts_ms) < dayMs) {
|
|
244
|
+
reserved += Number(e.est_usd || 0) || 0;
|
|
245
|
+
}
|
|
246
|
+
} catch { /* skip malformed line */ }
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
const ceiling = loadPerTierCeilings()[tier];
|
|
250
|
+
const available = ceiling === null ? true : spent + reserved < ceiling;
|
|
251
|
+
console.log(JSON.stringify({
|
|
252
|
+
tier,
|
|
253
|
+
window: 'rolling-24h',
|
|
254
|
+
ceiling_usd: ceiling,
|
|
255
|
+
spent_usd: spent,
|
|
256
|
+
reserved_usd: reserved,
|
|
257
|
+
available,
|
|
258
|
+
}));
|
|
259
|
+
}
|
|
260
|
+
|
|
201
261
|
function main() {
|
|
202
262
|
const [cmd, ...rest] = process.argv.slice(2);
|
|
203
263
|
switch (cmd) {
|
|
204
264
|
case 'set': return cmdSet(rest);
|
|
205
265
|
case 'get': return cmdGet();
|
|
206
266
|
case 'check': return cmdCheck();
|
|
267
|
+
case 'tier': return cmdTier(rest);
|
|
207
268
|
default:
|
|
208
|
-
console.error('usage: budget.mjs {set <usd>|get|check}');
|
|
269
|
+
console.error('usage: budget.mjs {set <usd>|get|check|tier <cheap|medium|strong>}');
|
|
209
270
|
process.exit(2);
|
|
210
271
|
}
|
|
211
272
|
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
#!/usr/bin/env tsx
|
|
2
|
+
/**
|
|
3
|
+
* Emit the scoped-projection skill count — the number `docs/CLAIMS.md`
|
|
4
|
+
* publishes for the default install
|
|
5
|
+
* (road-to-reproducible-artefact-counts).
|
|
6
|
+
*
|
|
7
|
+
* The claim used to read "217 of 288 skills … with the counting method
|
|
8
|
+
* pinned in the benchmark doc", while that benchmark doc said 215 of 286.
|
|
9
|
+
* Both numbers were hand-typed, only one of them tracked the catalog, and
|
|
10
|
+
* the gap grew by one on every skill added because no gate could compare
|
|
11
|
+
* them. This script is the single derivation both now come from.
|
|
12
|
+
*
|
|
13
|
+
* It adds no counting mechanism of its own — that would be a second path
|
|
14
|
+
* free to disagree with the first, which is the defect, not the fix:
|
|
15
|
+
*
|
|
16
|
+
* - the file set is `update_counts.iter_skills()`, the exact walk behind
|
|
17
|
+
* the canonical `count('skills')`, so `projected + pruned === total`
|
|
18
|
+
* holds by construction;
|
|
19
|
+
* - the in/out predicate is `_lib/scoped_projection.is_pruned_under_scoped`,
|
|
20
|
+
* the exact function `install.ts` applies when it prunes a real user's
|
|
21
|
+
* tree, so the published number cannot describe a projection the
|
|
22
|
+
* installer does not perform.
|
|
23
|
+
*
|
|
24
|
+
* `runtime.active_packs` is deliberately empty here: the claim is about the
|
|
25
|
+
* DEFAULT for a new install, and a fresh machine has no overlay. An operator
|
|
26
|
+
* who activates extra packs gets more skills than the claim states — the
|
|
27
|
+
* claim is a floor, and `--json` exposes `active_packs` so a reader can see
|
|
28
|
+
* the assumption rather than infer it.
|
|
29
|
+
*
|
|
30
|
+
* Usage:
|
|
31
|
+
* ./scripts-run src/scripts/count_scoped_projection # "217 of 288"
|
|
32
|
+
* ./scripts-run src/scripts/count_scoped_projection --json
|
|
33
|
+
* ./scripts-run src/scripts/count_scoped_projection --projected
|
|
34
|
+
*
|
|
35
|
+
* Exit codes: 0 ok · 1 the pack registry could not be read.
|
|
36
|
+
*/
|
|
37
|
+
|
|
38
|
+
import * as fs from 'node:fs';
|
|
39
|
+
import * as path from 'node:path';
|
|
40
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
41
|
+
|
|
42
|
+
import {
|
|
43
|
+
scoped_projection_stats,
|
|
44
|
+
type ScopedProjectionStats,
|
|
45
|
+
} from './_lib/scoped_projection.js';
|
|
46
|
+
import { iter_skills, REPO_ROOT } from './update_counts.js';
|
|
47
|
+
|
|
48
|
+
const _HERE = fileURLToPath(import.meta.url);
|
|
49
|
+
|
|
50
|
+
export type { ScopedProjectionStats };
|
|
51
|
+
|
|
52
|
+
/** Partition the canonical skill catalog with the installer's own predicate. */
|
|
53
|
+
export function stats(package_root: string = REPO_ROOT): ScopedProjectionStats {
|
|
54
|
+
return scoped_projection_stats(package_root, iter_skills());
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function main(argv: readonly string[] = process.argv.slice(2)): number {
|
|
58
|
+
let s: ScopedProjectionStats;
|
|
59
|
+
try {
|
|
60
|
+
s = stats();
|
|
61
|
+
} catch (err) {
|
|
62
|
+
process.stderr.write(
|
|
63
|
+
`❌ scoped-projection count unavailable: ${(err as Error).message}\n` +
|
|
64
|
+
' (src/config/discovery/packs.yml must exist and parse to a list)\n',
|
|
65
|
+
);
|
|
66
|
+
return 1;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (argv.includes('--json')) {
|
|
70
|
+
process.stdout.write(`${JSON.stringify(s, null, 2)}\n`);
|
|
71
|
+
return 0;
|
|
72
|
+
}
|
|
73
|
+
if (argv.includes('--projected')) {
|
|
74
|
+
process.stdout.write(`${s.projected}\n`);
|
|
75
|
+
return 0;
|
|
76
|
+
}
|
|
77
|
+
if (argv.includes('--total')) {
|
|
78
|
+
process.stdout.write(`${s.total}\n`);
|
|
79
|
+
return 0;
|
|
80
|
+
}
|
|
81
|
+
process.stdout.write(`${s.projected} of ${s.total}\n`);
|
|
82
|
+
return 0;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function _isCliEntry(): boolean {
|
|
86
|
+
if (process.argv[1] === undefined) {
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
const argvUrl = pathToFileURL(path.resolve(process.argv[1])).href;
|
|
90
|
+
if (import.meta.url === argvUrl) {
|
|
91
|
+
return true;
|
|
92
|
+
}
|
|
93
|
+
try {
|
|
94
|
+
const here = fs.realpathSync(fileURLToPath(import.meta.url));
|
|
95
|
+
const argv = fs.realpathSync(path.resolve(process.argv[1]));
|
|
96
|
+
return here === argv;
|
|
97
|
+
} catch {
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (_isCliEntry() || process.argv[1] === _HERE) {
|
|
103
|
+
process.exit(main(process.argv.slice(2)));
|
|
104
|
+
}
|
|
@@ -23,8 +23,12 @@
|
|
|
23
23
|
# 9. npx smoke first-touch paths (--version, --help, mcp initialize)
|
|
24
24
|
# 6-8 feed src/scripts/check_evaluator_budgets.ts against
|
|
25
25
|
# src/config/evaluator-budgets.json (owner + review date lint-enforced).
|
|
26
|
+
# 8b. record publish with UMBRELLA_RECORD=1, writes the measurement set to
|
|
27
|
+
# agents/evidence/metrics/evaluator-measurements.json so
|
|
28
|
+
# drift is visible in a diff the day it lands
|
|
26
29
|
#
|
|
27
30
|
# Usage: bash src/scripts/evaluator_umbrella.sh [--skip-pack <tarball>]
|
|
31
|
+
# UMBRELLA_RECORD=1 bash src/scripts/evaluator_umbrella.sh # nightly
|
|
28
32
|
set -euo pipefail
|
|
29
33
|
|
|
30
34
|
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
|
@@ -166,9 +170,38 @@ console.log(JSON.stringify(m, null, 1));' \
|
|
|
166
170
|
"$MEASURE_TMP" "$UNPACKED_BYTES" "$NM_MB" "$DEP_COUNT" "$CLI_MS" "$MCP_MS" "$CLI_COUNT" "$MCP_TOOLS" \
|
|
167
171
|
"$COLD_START_MS" "$COLD_START_CONDITIONS"
|
|
168
172
|
|
|
169
|
-
|
|
173
|
+
# The budget gate runs BEFORE the record is refreshed, so the comparison is
|
|
174
|
+
# fresh-vs-committed. Refreshing first would make every drift check trivially
|
|
175
|
+
# green (road-to-gates-that-can-fail Phase 5).
|
|
176
|
+
step "budget gate (absolute + >regression_pct creep + drift vs the committed record)"
|
|
170
177
|
./scripts-run src/scripts/check_evaluator_budgets --measurements "$MEASURE_TMP"
|
|
171
178
|
|
|
179
|
+
# ------------------------------------------------- 8b. publish the record
|
|
180
|
+
# With UMBRELLA_RECORD=1 (the nightly sets it) the measurement set is written
|
|
181
|
+
# to a COMMITTED artifact, so budget drift is visible the day it lands instead
|
|
182
|
+
# of at the next release — `cli_help_command_count` drifted 74 → 80 against a
|
|
183
|
+
# value frozen at 79 with nobody seeing it. It lives under agents/evidence/,
|
|
184
|
+
# the repo's committed-evidence tree, and NOT under src/config/, which ships:
|
|
185
|
+
# this is maintainer evidence with nightly churn, not consumer runtime config,
|
|
186
|
+
# and keeping it out of the tarball also keeps evaluator-budgets.json (the
|
|
187
|
+
# policy) reviewable on its own. Sorted keys + fixed indent so a moved number
|
|
188
|
+
# is a one-line diff.
|
|
189
|
+
if [[ "${UMBRELLA_RECORD:-0}" == "1" ]]; then
|
|
190
|
+
step "publish the measurement record (agents/evidence/metrics/)"
|
|
191
|
+
RECORD_PATH="$ROOT/agents/evidence/metrics/evaluator-measurements.json"
|
|
192
|
+
mkdir -p "$(dirname "$RECORD_PATH")"
|
|
193
|
+
RUN_URL=""
|
|
194
|
+
if [[ -n "${GITHUB_REPOSITORY:-}" && -n "${GITHUB_RUN_ID:-}" ]]; then
|
|
195
|
+
RUN_URL="${GITHUB_SERVER_URL:-https://github.com}/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID"
|
|
196
|
+
fi
|
|
197
|
+
node src/scripts/record_evaluator_measurements.mjs \
|
|
198
|
+
--measurements "$MEASURE_TMP" \
|
|
199
|
+
--out "$RECORD_PATH" \
|
|
200
|
+
--sha "$(git rev-parse HEAD 2>/dev/null || echo '')" \
|
|
201
|
+
--run-url "$RUN_URL" \
|
|
202
|
+
--conditions "$COLD_START_CONDITIONS"
|
|
203
|
+
fi
|
|
204
|
+
|
|
172
205
|
# ------------------------------------------------------------- 9. npx smoke
|
|
173
206
|
step "first-touch smoke (installed bin)"
|
|
174
207
|
"$CLI_BIN" --version >/dev/null
|
|
@@ -109,6 +109,14 @@ const _FN_SPEC: Record<string, FnSpec> = {
|
|
|
109
109
|
artifact: 'commands',
|
|
110
110
|
cells: { 'claude-code': 'native', augment: 'native' },
|
|
111
111
|
},
|
|
112
|
+
// The colon form (`/cluster:sub`) in the project tree. Same artifact and
|
|
113
|
+
// the same native cell as the hyphen wrapper above — the two split the
|
|
114
|
+
// command population between them (nested here, flat there); this is not
|
|
115
|
+
// an added surface.
|
|
116
|
+
generate_claude_project_commands: {
|
|
117
|
+
artifact: 'commands',
|
|
118
|
+
cells: { 'claude-code': 'native' },
|
|
119
|
+
},
|
|
112
120
|
generate_claude_subagents: {
|
|
113
121
|
artifact: 'subagents',
|
|
114
122
|
cells: { 'claude-code': 'native' },
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
#!/usr/bin/env tsx
|
|
2
|
+
/**
|
|
3
|
+
* Derive the routing-matrix router-coverage corpus (road-to-tested-routing
|
|
4
|
+
* Phase 2, PREREG P2 corpus expansion).
|
|
5
|
+
*
|
|
6
|
+
* Reads every per-rule routing matrix under `tests/eval/routing-matrix/`
|
|
7
|
+
* and emits ONE generated corpus file,
|
|
8
|
+
* `internal/bench/corpora/router-coverage/routing-matrix-derived.yaml`,
|
|
9
|
+
* with one `router-coverage` prompt per matrix POSITIVE, labelled
|
|
10
|
+
* `intended_triggers: [<rule>]`. Near-misses are deliberately NOT emitted —
|
|
11
|
+
* the corpus measures intended activation; the matrices' negative half is
|
|
12
|
+
* enforced by `tests/scripts/routing_matrix.test.ts`.
|
|
13
|
+
*
|
|
14
|
+
* Ownership (two-gate pattern): this generator is the only writer;
|
|
15
|
+
* `--check` re-derives in memory and exits 1 on drift, so the derived file
|
|
16
|
+
* can never be hand-edited into disagreement with its source matrices.
|
|
17
|
+
*/
|
|
18
|
+
import * as fs from "node:fs";
|
|
19
|
+
import * as path from "node:path";
|
|
20
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
21
|
+
import { parse as parseYaml } from "yaml";
|
|
22
|
+
|
|
23
|
+
const REPO_ROOT = path.resolve(
|
|
24
|
+
path.dirname(fileURLToPath(import.meta.url)),
|
|
25
|
+
"..",
|
|
26
|
+
"..",
|
|
27
|
+
);
|
|
28
|
+
const MATRIX_DIR = path.join(REPO_ROOT, "tests", "eval", "routing-matrix");
|
|
29
|
+
export const OUT_FILE = path.join(
|
|
30
|
+
REPO_ROOT,
|
|
31
|
+
"internal",
|
|
32
|
+
"bench",
|
|
33
|
+
"corpora",
|
|
34
|
+
"router-coverage",
|
|
35
|
+
"routing-matrix-derived.yaml",
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
interface MatrixCase {
|
|
39
|
+
prompt: string;
|
|
40
|
+
open_files?: string[];
|
|
41
|
+
command?: string;
|
|
42
|
+
}
|
|
43
|
+
interface MatrixFile {
|
|
44
|
+
rule: string;
|
|
45
|
+
positives: MatrixCase[];
|
|
46
|
+
near_misses: MatrixCase[];
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** Cheap language tag for the corpus `language:` field. */
|
|
50
|
+
export function detect_language(prompt: string): "de" | "en" {
|
|
51
|
+
if (/[äöüßÄÖÜ]/.test(prompt)) {
|
|
52
|
+
return "de";
|
|
53
|
+
}
|
|
54
|
+
return /(^|\s)(der|die|das|und|nicht|bitte|keine?|mach|prüfe|erstelle|für)(\s|$)/i.test(prompt)
|
|
55
|
+
? "de"
|
|
56
|
+
: "en";
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function yamlQuote(s: string): string {
|
|
60
|
+
return JSON.stringify(s);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function derive(): string {
|
|
64
|
+
const files = fs
|
|
65
|
+
.readdirSync(MATRIX_DIR)
|
|
66
|
+
.filter((f) => f.endsWith(".yaml"))
|
|
67
|
+
.sort();
|
|
68
|
+
const lines: string[] = [];
|
|
69
|
+
lines.push("# Router-coverage corpus — DERIVED FROM the per-rule routing matrices.");
|
|
70
|
+
lines.push("#");
|
|
71
|
+
lines.push("# GENERATED FILE — do not hand-edit. Source of truth:");
|
|
72
|
+
lines.push("# tests/eval/routing-matrix/*.yaml");
|
|
73
|
+
lines.push("# Regenerate: npx tsx src/scripts/generate_router_coverage_from_matrix.ts");
|
|
74
|
+
lines.push("# Drift gate: npx tsx src/scripts/generate_router_coverage_from_matrix.ts --check");
|
|
75
|
+
lines.push("");
|
|
76
|
+
lines.push("version: 1");
|
|
77
|
+
lines.push("corpus_id: router-coverage-routing-matrix-derived");
|
|
78
|
+
lines.push("selection_accuracy_target: 0.0");
|
|
79
|
+
lines.push("");
|
|
80
|
+
lines.push("prompts:");
|
|
81
|
+
for (const f of files) {
|
|
82
|
+
const parsed = parseYaml(fs.readFileSync(path.join(MATRIX_DIR, f), "utf-8")) as MatrixFile;
|
|
83
|
+
parsed.positives.forEach((c, i) => {
|
|
84
|
+
const id = `rm-${parsed.rule}-${String(i + 1).padStart(2, "0")}`;
|
|
85
|
+
lines.push(` - id: ${id}`);
|
|
86
|
+
lines.push(" category: router-coverage");
|
|
87
|
+
lines.push(` language: ${detect_language(c.prompt)}`);
|
|
88
|
+
lines.push(` prompt: ${yamlQuote(c.prompt)}`);
|
|
89
|
+
lines.push(" expected_skills: []");
|
|
90
|
+
lines.push(` intended_triggers: [${parsed.rule}]`);
|
|
91
|
+
if (c.open_files && c.open_files.length > 0) {
|
|
92
|
+
lines.push(` open_files: [${c.open_files.map(yamlQuote).join(", ")}]`);
|
|
93
|
+
}
|
|
94
|
+
lines.push("");
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
return `${lines.join("\n").trimEnd()}\n`;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export function main(argv?: string[]): number {
|
|
101
|
+
const args = argv ?? process.argv.slice(2);
|
|
102
|
+
const check = args.includes("--check");
|
|
103
|
+
const derived = derive();
|
|
104
|
+
if (check) {
|
|
105
|
+
let current = "";
|
|
106
|
+
try {
|
|
107
|
+
current = fs.readFileSync(OUT_FILE, "utf-8");
|
|
108
|
+
} catch {
|
|
109
|
+
process.stderr.write(`missing derived corpus: ${OUT_FILE} — run the generator\n`);
|
|
110
|
+
return 1;
|
|
111
|
+
}
|
|
112
|
+
if (current !== derived) {
|
|
113
|
+
process.stderr.write(
|
|
114
|
+
"routing-matrix-derived.yaml is stale — regenerate via generate_router_coverage_from_matrix\n",
|
|
115
|
+
);
|
|
116
|
+
return 1;
|
|
117
|
+
}
|
|
118
|
+
process.stdout.write("routing-matrix-derived corpus: fresh\n");
|
|
119
|
+
return 0;
|
|
120
|
+
}
|
|
121
|
+
fs.writeFileSync(OUT_FILE, derived, "utf-8");
|
|
122
|
+
const promptCount = (derived.match(/^ {2}- id: /gm) ?? []).length;
|
|
123
|
+
const ruleCount = new Set(
|
|
124
|
+
[...derived.matchAll(/intended_triggers: \[([^\]]+)\]/g)].map((m) => m[1]),
|
|
125
|
+
).size;
|
|
126
|
+
process.stdout.write(
|
|
127
|
+
`wrote ${path.relative(REPO_ROOT, OUT_FILE)} — ${promptCount} prompts, ${ruleCount} distinct rule ids\n`,
|
|
128
|
+
);
|
|
129
|
+
return 0;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
declare const __AGENT_CONFIG_BUNDLE__: boolean | undefined;
|
|
133
|
+
function _isCliEntry(): boolean {
|
|
134
|
+
if (typeof __AGENT_CONFIG_BUNDLE__ !== "undefined" && __AGENT_CONFIG_BUNDLE__) {
|
|
135
|
+
return false;
|
|
136
|
+
}
|
|
137
|
+
if (process.argv[1] === undefined) {
|
|
138
|
+
return false;
|
|
139
|
+
}
|
|
140
|
+
const argvUrl = pathToFileURL(path.resolve(process.argv[1])).href;
|
|
141
|
+
if (import.meta.url === argvUrl) {
|
|
142
|
+
return true;
|
|
143
|
+
}
|
|
144
|
+
try {
|
|
145
|
+
const here = fs.realpathSync(fileURLToPath(import.meta.url));
|
|
146
|
+
const argv1 = fs.realpathSync(path.resolve(process.argv[1]));
|
|
147
|
+
return here === argv1;
|
|
148
|
+
} catch {
|
|
149
|
+
return false;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if (_isCliEntry()) {
|
|
154
|
+
process.exit(main());
|
|
155
|
+
}
|