@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
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*
|
|
6
6
|
* Ported from the retired Python `src/scripts/check_iron_law_prominence.py` (ADR-200,
|
|
7
7
|
* Phase 4 / Wave 4c). The CLI contract is pinned — positional
|
|
8
|
-
* paths (default
|
|
8
|
+
* paths (default `src/rules`), `--format` / `--quiet`
|
|
9
9
|
* flags, exit codes (0 clean, 1 violations, 3 internal error),
|
|
10
10
|
* stdout/stderr split, byte-identical finding messages, and the same
|
|
11
11
|
* heading parse / code-fence skipping. No behaviour changes — latent bugs
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* historical quirks preserved (consumers pin the exact behaviour).
|
|
12
12
|
*
|
|
13
13
|
* Fails when a single PR (or commit range) modifies more than one
|
|
14
|
-
* kernel rule under
|
|
14
|
+
* kernel rule under `src/rules/`. Override via the
|
|
15
15
|
* PR label `bundled-always-rules-acknowledged`.
|
|
16
16
|
*
|
|
17
17
|
* Kernel set is the locked 9-rule list in
|
|
@@ -45,12 +45,20 @@ import * as fs from 'node:fs';
|
|
|
45
45
|
import * as path from 'node:path';
|
|
46
46
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
47
47
|
|
|
48
|
+
import { checkRatchet } from './_lib/gate_baseline.js';
|
|
49
|
+
|
|
48
50
|
const LEGACY = '.agent-src.uncondensed/';
|
|
49
51
|
const EXEMPT: ReadonlySet<string> = new Set([
|
|
50
52
|
'src/scripts/_lib/agent_src.py',
|
|
51
53
|
'src/scripts/check_references.py',
|
|
52
54
|
'src/scripts/check_condensed_paths.py',
|
|
53
55
|
'src/scripts/check_no_new_legacy_path.py', // this file documents the literal
|
|
56
|
+
// The `.ts` twin needs its own entry now: the docstring above says the
|
|
57
|
+
// faithful-twin rule covers it, but that rule reads a `.py` sibling from
|
|
58
|
+
// disk and the py2ts final deletion removed every sibling — so since then
|
|
59
|
+
// ANY edit to this guard's own message strings flags the guard. Surfaced
|
|
60
|
+
// by the Phase-5 edit that added the existing-literal pass below.
|
|
61
|
+
'src/scripts/check_no_new_legacy_path.ts',
|
|
54
62
|
'src/scripts/check_source_pointer_freshness.ts', // forbidden-substring detector for the legacy tree
|
|
55
63
|
]);
|
|
56
64
|
|
|
@@ -131,6 +139,154 @@ function find_offenders(diffText: string, twinCheck: TwinCheck = _is_faithful_tw
|
|
|
131
139
|
return offenders;
|
|
132
140
|
}
|
|
133
141
|
|
|
142
|
+
// ---------------------------------------------------------------------------
|
|
143
|
+
// Pass 2 — EXISTING hardcoded legacy scan roots (ratcheted).
|
|
144
|
+
//
|
|
145
|
+
// The diff pass above stops the debt from growing. It is structurally blind to
|
|
146
|
+
// the debt that is already there — and every one of the 14 dead gates found in
|
|
147
|
+
// 2026-07 was pre-existing, so a new-violations-only check could never have
|
|
148
|
+
// seen them (road-to-gates-that-can-fail Phase 5).
|
|
149
|
+
//
|
|
150
|
+
// WHY THIS IS NOT THE FULL-TREE LINT THAT WAS ALREADY REJECTED. The note in
|
|
151
|
+
// `find_offenders` records a measured rejection: a full-tree lint over `tests/`
|
|
152
|
+
// hits 44 files / 213 lines, most of them legitimate (the legacy detectors'
|
|
153
|
+
// own test data, `validator_ignore` substrings, synthetic tmpdir fixtures), so
|
|
154
|
+
// the signal drowns. That measurement stands and this pass does not repeat it.
|
|
155
|
+
// Three things make this scope different:
|
|
156
|
+
//
|
|
157
|
+
// 1. It reads EXECUTABLE code only — `src/scripts/**/*.{ts,mts,mjs}`. No
|
|
158
|
+
// prose, no docs, no tests, no fixtures.
|
|
159
|
+
// 2. It counts only lines that CONSTRUCT A PATH — the literal must reach
|
|
160
|
+
// `path.join(...)` inside a string. A detector holding the literal as
|
|
161
|
+
// data to match, strip, or print is not a scan root and is not counted.
|
|
162
|
+
// Measured on 2026-08-02: 236 raw mentions in src/scripts → 147 string
|
|
163
|
+
// literals → 70 path-constructing lines. The narrowing is the point.
|
|
164
|
+
// 3. It is a RATCHET, not a hard fail. The current count is committed to
|
|
165
|
+
// `src/config/gate-violation-baselines.json`; the gate goes red only when
|
|
166
|
+
// the count RISES. Every Phase-1 repair lowers it, and the baseline's
|
|
167
|
+
// 56-day expiry (see `_lib/gate_baseline.ts`) stops a stalled number from
|
|
168
|
+
// hardening into configuration.
|
|
169
|
+
// ---------------------------------------------------------------------------
|
|
170
|
+
|
|
171
|
+
/** Gate key in `src/config/gate-violation-baselines.json`. */
|
|
172
|
+
export const HARDCODED_ROOT_GATE = 'check_no_new_legacy_path:hardcoded-scan-roots';
|
|
173
|
+
|
|
174
|
+
/** The executable-code root this pass reads. */
|
|
175
|
+
export const HARDCODED_ROOT_SCAN_DIR = 'src/scripts';
|
|
176
|
+
|
|
177
|
+
/** Bare directory name — the form that appears inside a `path.join(...)` call. */
|
|
178
|
+
const LEGACY_DIRNAME = LEGACY.replace(/\/+$/, '');
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Files allowed to construct a legacy path forever.
|
|
182
|
+
*
|
|
183
|
+
* `_lib/agent_src.ts` is the shared resolver — owning the constant is its whole
|
|
184
|
+
* job, and every gate that routes through it survived the ADR-051 move. Exempting
|
|
185
|
+
* it is what makes the count mean "gates that bypassed the resolver".
|
|
186
|
+
*/
|
|
187
|
+
const HARDCODED_ROOT_EXEMPT: ReadonlySet<string> = new Set(['src/scripts/_lib/agent_src.ts']);
|
|
188
|
+
|
|
189
|
+
const _STRING_LITERAL_LEGACY = new RegExp(
|
|
190
|
+
`['"\`][^'"\`]*${LEGACY_DIRNAME.replace(/\./g, '\\.')}`,
|
|
191
|
+
);
|
|
192
|
+
|
|
193
|
+
function _isCommentLine(trimmed: string): boolean {
|
|
194
|
+
return (
|
|
195
|
+
trimmed.startsWith('//') ||
|
|
196
|
+
trimmed.startsWith('*') ||
|
|
197
|
+
trimmed.startsWith('/*') ||
|
|
198
|
+
trimmed.startsWith('#')
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function* _walkScripts(dir: string): Generator<string> {
|
|
203
|
+
let entries: fs.Dirent[];
|
|
204
|
+
try {
|
|
205
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
206
|
+
} catch {
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
for (const e of entries) {
|
|
210
|
+
const full = path.join(dir, e.name);
|
|
211
|
+
if (e.isDirectory()) {
|
|
212
|
+
yield* _walkScripts(full);
|
|
213
|
+
} else if (/\.(ts|mts|mjs)$/.test(e.name) && !/\.d\.m?ts$/.test(e.name)) {
|
|
214
|
+
yield full;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Lines under `src/scripts/` that build a filesystem path against the retired
|
|
221
|
+
* tree. Returns `file:line: <source>` strings, sorted by path.
|
|
222
|
+
*
|
|
223
|
+
* `repoRoot` defaults to cwd (CI runs the guard at the repo root) and is
|
|
224
|
+
* injectable so tests can point at a fixture tree.
|
|
225
|
+
*/
|
|
226
|
+
export function find_hardcoded_scan_roots(
|
|
227
|
+
repoRoot: string = process.cwd(),
|
|
228
|
+
twinCheck: TwinCheck = _is_faithful_twin,
|
|
229
|
+
): string[] {
|
|
230
|
+
const root = path.join(repoRoot, HARDCODED_ROOT_SCAN_DIR);
|
|
231
|
+
const findings: string[] = [];
|
|
232
|
+
for (const abs of _walkScripts(root)) {
|
|
233
|
+
const rel = path.relative(repoRoot, abs).split(path.sep).join('/');
|
|
234
|
+
if (EXEMPT.has(rel) || HARDCODED_ROOT_EXEMPT.has(rel) || twinCheck(rel)) {
|
|
235
|
+
continue;
|
|
236
|
+
}
|
|
237
|
+
let text: string;
|
|
238
|
+
try {
|
|
239
|
+
text = fs.readFileSync(abs, 'utf-8');
|
|
240
|
+
} catch {
|
|
241
|
+
continue;
|
|
242
|
+
}
|
|
243
|
+
if (!text.includes(LEGACY_DIRNAME)) {
|
|
244
|
+
continue;
|
|
245
|
+
}
|
|
246
|
+
text.split('\n').forEach((line, i) => {
|
|
247
|
+
if (!line.includes(LEGACY_DIRNAME)) return;
|
|
248
|
+
const trimmed = line.trim();
|
|
249
|
+
if (_isCommentLine(trimmed)) return;
|
|
250
|
+
if (!/path\.join\s*\(/.test(line)) return;
|
|
251
|
+
if (!_STRING_LITERAL_LEGACY.test(line)) return;
|
|
252
|
+
findings.push(`${rel}:${i + 1}: ${trimmed.slice(0, 100)}`);
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
findings.sort();
|
|
256
|
+
return findings;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Run the existing-literal pass and judge it against the committed baseline.
|
|
261
|
+
* Returns 0 when the count is at or below the baseline (and the baseline is
|
|
262
|
+
* fresh), 1 otherwise. Prints either way — a passing ratchet still reports the
|
|
263
|
+
* live number, which is the whole point of committing it.
|
|
264
|
+
*/
|
|
265
|
+
export function checkHardcodedScanRoots(repoRoot: string = process.cwd()): number {
|
|
266
|
+
const findings = find_hardcoded_scan_roots(repoRoot);
|
|
267
|
+
const verdict = checkRatchet({
|
|
268
|
+
gate: HARDCODED_ROOT_GATE,
|
|
269
|
+
actual: findings.length,
|
|
270
|
+
repoRoot,
|
|
271
|
+
});
|
|
272
|
+
if (verdict.ok) {
|
|
273
|
+
process.stdout.write(`✅ ${verdict.message}\n`);
|
|
274
|
+
return 0;
|
|
275
|
+
}
|
|
276
|
+
process.stdout.write(
|
|
277
|
+
`❌ Hardcoded \`${LEGACY}\` scan root(s) under ${HARDCODED_ROOT_SCAN_DIR}/ ` +
|
|
278
|
+
'above the recorded baseline:\n',
|
|
279
|
+
);
|
|
280
|
+
for (const f of findings.slice(0, 20)) {
|
|
281
|
+
process.stdout.write(` 🔴 ${f}\n`);
|
|
282
|
+
}
|
|
283
|
+
if (findings.length > 20) {
|
|
284
|
+
process.stdout.write(` … and ${findings.length - 20} more\n`);
|
|
285
|
+
}
|
|
286
|
+
process.stdout.write(`\n${verdict.message}\n`);
|
|
287
|
+
return 1;
|
|
288
|
+
}
|
|
289
|
+
|
|
134
290
|
function main(): number {
|
|
135
291
|
// --stdin: read a unified diff from stdin (CI pipes `gh pr diff` — auth-safe,
|
|
136
292
|
// no `git fetch <base>` extraheader race on shallow PR-merge checkouts, the
|
|
@@ -170,6 +326,7 @@ function main(): number {
|
|
|
170
326
|
offenders = find_offenders((proc.stdout as string | null) ?? '');
|
|
171
327
|
}
|
|
172
328
|
|
|
329
|
+
let rc = 0;
|
|
173
330
|
if (offenders.length) {
|
|
174
331
|
process.stdout.write(
|
|
175
332
|
'❌ New `.agent-src.uncondensed/` reference(s) added under src/ ' +
|
|
@@ -182,11 +339,18 @@ function main(): number {
|
|
|
182
339
|
'\nFix: reference the real `src/` target. Existing stale prose is ' +
|
|
183
340
|
'migrated opportunistically; do not ADD new dead-path references.\n',
|
|
184
341
|
);
|
|
185
|
-
|
|
342
|
+
rc = 1;
|
|
343
|
+
} else {
|
|
344
|
+
process.stdout.write('✅ No new `.agent-src.uncondensed/` references added under src/.\n');
|
|
186
345
|
}
|
|
187
346
|
|
|
188
|
-
|
|
189
|
-
|
|
347
|
+
// Pass 2 — the pre-existing hardcoded scan roots the diff pass cannot see.
|
|
348
|
+
// Skippable for the golden-parity harness, which compares this guard's
|
|
349
|
+
// diff behaviour against a pinned reference implementation.
|
|
350
|
+
if (!process.argv.includes('--no-existing')) {
|
|
351
|
+
rc = Math.max(rc, checkHardcodedScanRoots());
|
|
352
|
+
}
|
|
353
|
+
return rc;
|
|
190
354
|
}
|
|
191
355
|
|
|
192
356
|
const _HERE = fileURLToPath(import.meta.url);
|
|
@@ -55,12 +55,26 @@ export interface PackSizeBudget {
|
|
|
55
55
|
/**
|
|
56
56
|
* Parse `npm pack --json` stdout. npm runs the `prepare` lifecycle script even
|
|
57
57
|
* under `--ignore-scripts` on some versions, and this repo's `prepare` prints a
|
|
58
|
-
* banner — straight into the stream we parse. Slice from
|
|
59
|
-
*
|
|
58
|
+
* banner — straight into the stream we parse. Slice from a `[` so a lifecycle
|
|
59
|
+
* banner is tolerated instead of becoming a SyntaxError.
|
|
60
|
+
*
|
|
61
|
+
* Not the FIRST `[`: npm echoes the lifecycle command line, and this repo's own
|
|
62
|
+
* `prepare` is `[ -d .git ] && bash src/scripts/install-hooks.sh || true` — its
|
|
63
|
+
* `[` precedes the payload's. Try each candidate offset in order and keep the
|
|
64
|
+
* first that actually parses (road-to-gates-that-can-fail Phase 6.2).
|
|
60
65
|
*/
|
|
61
66
|
export function parsePackJson(stdout: string): PackResult {
|
|
62
|
-
|
|
63
|
-
|
|
67
|
+
let parsed: PackResult[] | null = null;
|
|
68
|
+
for (let i = stdout.indexOf('['); i >= 0; i = stdout.indexOf('[', i + 1)) {
|
|
69
|
+
try {
|
|
70
|
+
parsed = JSON.parse(stdout.slice(i)) as PackResult[];
|
|
71
|
+
break;
|
|
72
|
+
} catch {
|
|
73
|
+
// Not the payload — a banner bracket. Keep scanning.
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
// No candidate parsed: reproduce the original SyntaxError from the raw text.
|
|
77
|
+
parsed ??= JSON.parse(stdout) as PackResult[];
|
|
64
78
|
const first = parsed[0];
|
|
65
79
|
if (first === undefined) throw new Error('npm pack --json returned an empty array');
|
|
66
80
|
return first;
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* same finding text, same scan scope and file-walk order. No behaviour
|
|
9
9
|
* changes; latent bugs are replicated and flagged as divergence candidates.
|
|
10
10
|
*
|
|
11
|
-
* Scans
|
|
11
|
+
* Scans src/ and dist/agent-src/ for project-specific
|
|
12
12
|
* references that violate package portability (the package must work in ANY
|
|
13
13
|
* project).
|
|
14
14
|
*
|
|
@@ -271,10 +271,25 @@ const ALLOWLIST: string[] = [
|
|
|
271
271
|
'package repository', // "package repository" concept
|
|
272
272
|
'src/scripts/mcp_server/', // MCP server module path (road-to-mcp-server.md Phase 1)
|
|
273
273
|
String.raw`scripts\.mcp_server`, // MCP server Python module entrypoint
|
|
274
|
+
// ── Revealed 2026-08-02 when `src/` entered SCAN_DIRS (dead-root repair,
|
|
275
|
+
// road-to-renewal-foundation Phase 1). Both live under `src/templates/`,
|
|
276
|
+
// which is never projected into `dist/agent-src/`, so this gate had never
|
|
277
|
+
// seen them. Neither is a CONSUMER-project leak — they are the package's
|
|
278
|
+
// own published identifiers, which a consumer must type verbatim:
|
|
279
|
+
String.raw`plugin install agent-conf@`, // real marketplace install command
|
|
280
|
+
String.raw`\.event4u-bridge\.yml`, // real shipped anchor filename (src/scripts/install.ts, lint_agents_layout.ts)
|
|
274
281
|
];
|
|
275
282
|
|
|
276
|
-
// Directories to scan (only package files, not project-specific agents/)
|
|
277
|
-
|
|
283
|
+
// Directories to scan (only package files, not project-specific agents/).
|
|
284
|
+
//
|
|
285
|
+
// DEAD-ROOT REPAIR (road-to-renewal-foundation Phase 1): the second entry was
|
|
286
|
+
// `.agent-src.uncondensed`, deleted by ADR-051. The loops below `continue` on a
|
|
287
|
+
// missing directory, so half this gate's declared corpus — the whole authoring
|
|
288
|
+
// tree — was silently skipped. A project identifier authored in `src/` was only
|
|
289
|
+
// ever caught once a condense run projected it into `dist/agent-src/`, i.e. one
|
|
290
|
+
// step later than the gate claims to catch it. `src/` is the source of truth
|
|
291
|
+
// now and is scanned directly.
|
|
292
|
+
const SCAN_DIRS = ['src', 'dist/agent-src'];
|
|
278
293
|
|
|
279
294
|
// Additional root-level files shipped by the package that must also stay
|
|
280
295
|
// portable. These are read by agents working on the package itself and —
|
|
@@ -608,11 +623,20 @@ function check_cli_invocations(filepath: string): Violation[] {
|
|
|
608
623
|
return violations;
|
|
609
624
|
}
|
|
610
625
|
|
|
611
|
-
function scan_all(root: string): {
|
|
626
|
+
function scan_all(root: string): {
|
|
627
|
+
violations: Violation[];
|
|
628
|
+
detected: string[];
|
|
629
|
+
scanned: number;
|
|
630
|
+
} {
|
|
612
631
|
const { patterns, detected } = _compile_patterns(root);
|
|
613
632
|
const forbidden = _compile_forbidden_patterns();
|
|
614
633
|
const allowlist = _compile_allowlist();
|
|
615
634
|
const violations: Violation[] = [];
|
|
635
|
+
// Distinct files actually read, for the gate-coverage contract
|
|
636
|
+
// (src/config/gate-coverage.yml): a gate that inspected nothing has not
|
|
637
|
+
// passed. Counted as a set because the layer-3/4 passes revisit files the
|
|
638
|
+
// layer-1 pass already read.
|
|
639
|
+
const scannedFiles = new Set<string>();
|
|
616
640
|
|
|
617
641
|
// Layer 1 + 2: full package content
|
|
618
642
|
for (const scanDir of SCAN_DIRS) {
|
|
@@ -621,6 +645,7 @@ function scan_all(root: string): { violations: Violation[]; detected: string[] }
|
|
|
621
645
|
continue;
|
|
622
646
|
}
|
|
623
647
|
for (const f of _rglobMdSorted(d)) {
|
|
648
|
+
scannedFiles.add(f);
|
|
624
649
|
violations.push(...check_file(f, [...patterns, ...forbidden], allowlist));
|
|
625
650
|
}
|
|
626
651
|
}
|
|
@@ -629,6 +654,7 @@ function scan_all(root: string): { violations: Violation[]; detected: string[] }
|
|
|
629
654
|
for (const rel of SCAN_ROOT_FILES) {
|
|
630
655
|
const f = path.join(root, rel);
|
|
631
656
|
if (_isFile(f)) {
|
|
657
|
+
scannedFiles.add(f);
|
|
632
658
|
violations.push(...check_file(f, forbidden, allowlist));
|
|
633
659
|
}
|
|
634
660
|
}
|
|
@@ -664,7 +690,7 @@ function scan_all(root: string): { violations: Violation[]; detected: string[] }
|
|
|
664
690
|
}
|
|
665
691
|
}
|
|
666
692
|
|
|
667
|
-
return { violations, detected };
|
|
693
|
+
return { violations, detected, scanned: scannedFiles.size };
|
|
668
694
|
}
|
|
669
695
|
|
|
670
696
|
function format_text(violations: Violation[], detected: string[]): string {
|
|
@@ -816,7 +842,7 @@ function parseArgs(argv: readonly string[]): ParsedArgs {
|
|
|
816
842
|
function main(): number {
|
|
817
843
|
const args = parseArgs(process.argv.slice(2));
|
|
818
844
|
|
|
819
|
-
let result: { violations: Violation[]; detected: string[] };
|
|
845
|
+
let result: { violations: Violation[]; detected: string[]; scanned: number };
|
|
820
846
|
try {
|
|
821
847
|
result = scan_all(args.root);
|
|
822
848
|
} catch (e) {
|
|
@@ -825,7 +851,11 @@ function main(): number {
|
|
|
825
851
|
return 3;
|
|
826
852
|
}
|
|
827
853
|
|
|
828
|
-
const { violations, detected } = result;
|
|
854
|
+
const { violations, detected, scanned } = result;
|
|
855
|
+
// gate-coverage contract (src/config/gate-coverage.yml): one machine-readable
|
|
856
|
+
// count on stderr so `check_gate_coverage` can assert this gate read a real
|
|
857
|
+
// corpus. It scanned a deleted root for months and still exited 0.
|
|
858
|
+
process.stderr.write(`scanned: ${String(scanned)}\n`);
|
|
829
859
|
if (args.format === 'json') {
|
|
830
860
|
const payload = { detected, violations };
|
|
831
861
|
process.stdout.write(`${JSON.stringify(payload, null, 2)}\n`);
|
|
@@ -452,9 +452,12 @@ function _extract_personas_frontmatter(text: string): Array<[number, string]> {
|
|
|
452
452
|
|
|
453
453
|
function _find_suggestion(p: string, root: string): string {
|
|
454
454
|
const name = path.basename(p);
|
|
455
|
+
// DEAD-ROOT REPAIR (road-to-renewal-foundation Phase 1): the middle entry
|
|
456
|
+
// was `.agent-src.uncondensed`, deleted by ADR-051. `src/` is the source of
|
|
457
|
+
// truth, so a file that exists only there produced no suggestion at all.
|
|
455
458
|
for (const d of [
|
|
456
459
|
path.join(root, 'dist/agent-src'),
|
|
457
|
-
path.join(root, '
|
|
460
|
+
path.join(root, 'src'),
|
|
458
461
|
path.join(root, 'agents'),
|
|
459
462
|
]) {
|
|
460
463
|
if (_exists(d)) {
|
|
@@ -586,10 +589,15 @@ function check_file(filepath: string, artifacts: Artifacts, root: string): Broke
|
|
|
586
589
|
} else {
|
|
587
590
|
// Strip leading ./ and try with prefixes
|
|
588
591
|
const ref = rawRef.replace(/^[./]+/, '');
|
|
592
|
+
// DEAD-ROOT REPAIR: `.agent-src.uncondensed` (ADR-051) never
|
|
593
|
+
// resolved, so a reference that only exists under the source of
|
|
594
|
+
// truth was reported BROKEN. `src/` and its category roots
|
|
595
|
+
// replace it.
|
|
589
596
|
for (const prefix of [
|
|
590
597
|
root,
|
|
591
598
|
path.join(root, 'dist/agent-src'),
|
|
592
|
-
path.join(root, '
|
|
599
|
+
path.join(root, 'src'),
|
|
600
|
+
path.join(root, 'src/agent-src'),
|
|
593
601
|
]) {
|
|
594
602
|
if (_exists(path.join(prefix, ref))) {
|
|
595
603
|
resolved = true;
|
|
@@ -605,7 +613,7 @@ function check_file(filepath: string, artifacts: Artifacts, root: string): Broke
|
|
|
605
613
|
const rel = rawRef.slice('.augment/'.length);
|
|
606
614
|
for (const prefix of [
|
|
607
615
|
path.join(root, 'dist/agent-src'),
|
|
608
|
-
path.join(root, '
|
|
616
|
+
path.join(root, 'src/agent-src'),
|
|
609
617
|
]) {
|
|
610
618
|
if (_exists(path.join(prefix, rel))) {
|
|
611
619
|
resolved = true;
|
|
@@ -774,6 +782,10 @@ function check_memory_yaml(filepath: string, artifacts: Artifacts, root: string)
|
|
|
774
782
|
function scan_all(root: string): BrokenRef[] {
|
|
775
783
|
const artifacts = collect_artifacts(root);
|
|
776
784
|
const broken: BrokenRef[] = [];
|
|
785
|
+
// gate-coverage contract (src/config/gate-coverage.yml): a machine-readable
|
|
786
|
+
// count of files actually read, so `check_gate_coverage` can assert this
|
|
787
|
+
// gate saw a real corpus rather than a moved root.
|
|
788
|
+
let scannedFiles = 0;
|
|
777
789
|
for (const scanDir of SCAN_DIRS) {
|
|
778
790
|
const d = path.join(root, scanDir);
|
|
779
791
|
if (!_exists(d)) {
|
|
@@ -785,18 +797,22 @@ function scan_all(root: string): BrokenRef[] {
|
|
|
785
797
|
if (SKIP_DIRS.some((skip) => f.startsWith(path.join(root, skip)))) {
|
|
786
798
|
continue;
|
|
787
799
|
}
|
|
800
|
+
scannedFiles += 1;
|
|
788
801
|
broken.push(...check_file(f, artifacts, root));
|
|
789
802
|
}
|
|
790
803
|
}
|
|
791
804
|
const memoryDir = path.join(root, MEMORY_YAML_ROOT);
|
|
792
805
|
if (_isDir(memoryDir)) {
|
|
793
806
|
for (const f of _rglob(memoryDir, '.yml').sort()) {
|
|
807
|
+
scannedFiles += 1;
|
|
794
808
|
broken.push(...check_memory_yaml(f, artifacts, root));
|
|
795
809
|
}
|
|
796
810
|
for (const f of _rglob(memoryDir, '.yaml').sort()) {
|
|
811
|
+
scannedFiles += 1;
|
|
797
812
|
broken.push(...check_memory_yaml(f, artifacts, root));
|
|
798
813
|
}
|
|
799
814
|
}
|
|
815
|
+
process.stderr.write(`scanned: ${String(scannedFiles)}\n`);
|
|
800
816
|
return broken;
|
|
801
817
|
}
|
|
802
818
|
|
|
@@ -24,6 +24,9 @@ const _HERE = fileURLToPath(import.meta.url);
|
|
|
24
24
|
|
|
25
25
|
const ALLOWLIST_GLOBS = [
|
|
26
26
|
'package.json',
|
|
27
|
+
// Lockfile version fields — bumped by release.ts set_lockfile_version in
|
|
28
|
+
// lockstep with package.json (road-to-gates-that-can-fail Phase 5).
|
|
29
|
+
'package-lock.json',
|
|
27
30
|
'CHANGELOG.md',
|
|
28
31
|
'.claude-plugin/marketplace.json',
|
|
29
32
|
'src/packs/*/pack.yaml',
|
|
@@ -131,6 +134,16 @@ function check(files: readonly string[]): number {
|
|
|
131
134
|
for (const f of bad) {
|
|
132
135
|
process.stdout.write(`OUT-OF-SHAPE: ${f}\n`);
|
|
133
136
|
}
|
|
137
|
+
process.stdout.write(
|
|
138
|
+
'\n' +
|
|
139
|
+
'A release PR may only contain the version-bump allowlist — the heavy\n' +
|
|
140
|
+
'test matrix skips on release/* heads, so code must not ride a release PR\n' +
|
|
141
|
+
'(docs/contracts/release-pr-gating.md § Mid-release fixes).\n' +
|
|
142
|
+
'Fix: land the files above on main via their own PR, then\n' +
|
|
143
|
+
' git checkout release/X.Y.Z && git merge origin/main && git push\n' +
|
|
144
|
+
'— their release-PR diff becomes empty and this check goes green.\n' +
|
|
145
|
+
'Then resume with: task release -- --resume --yes\n',
|
|
146
|
+
);
|
|
134
147
|
return 1;
|
|
135
148
|
}
|
|
136
149
|
process.stdout.write(
|
|
@@ -17,7 +17,9 @@
|
|
|
17
17
|
* - thin_rule_load — thin_projection.thin_gpt
|
|
18
18
|
* - skill_descriptions — description_catalog.skills_core_source.tokens_gpt
|
|
19
19
|
* - command_descriptions — description_catalog.commands_core_source.tokens_gpt
|
|
20
|
-
* - mcp_schemas —
|
|
20
|
+
* - mcp_schemas — max mcp_tool_schemas[*].tokens_gpt (per-transport;
|
|
21
|
+
* a client connects over ONE transport, so Σ would
|
|
22
|
+
* double-bill the tools both transports serve)
|
|
21
23
|
*
|
|
22
24
|
* A baseline captured under a different `token_method` (e.g. proxy → exact)
|
|
23
25
|
* is a legitimate reset, not a regression — the gate WARNS and asks for a
|
|
@@ -74,10 +76,15 @@ function _num(obj: unknown, ...keys: string[]): number {
|
|
|
74
76
|
|
|
75
77
|
/** Extract the flat gated-metric map from a projection-cost report. */
|
|
76
78
|
function extract_metrics(report: Json): Metrics {
|
|
79
|
+
// MAX, not Σ. The report keys one entry per SERVER-TRANSPORT pair, and a
|
|
80
|
+
// client connects over exactly one transport of a given server — summing
|
|
81
|
+
// `agent-config (stdio)` and `agent-config (worker)` would bill the 19
|
|
82
|
+
// shared tools twice and read as a regression the moment the transports were
|
|
83
|
+
// split apart. The gated metric is what the worst-case single client loads.
|
|
77
84
|
const mcp = (report['mcp_tool_schemas'] as Json) ?? {};
|
|
78
85
|
let mcp_total = 0;
|
|
79
86
|
for (const server of Object.values(mcp)) {
|
|
80
|
-
mcp_total
|
|
87
|
+
mcp_total = Math.max(mcp_total, _num(server, 'tokens_gpt'));
|
|
81
88
|
}
|
|
82
89
|
return {
|
|
83
90
|
token_method: String(report['token_method'] ?? 'unknown'),
|