@event4u/agent-config 9.28.0 → 9.29.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.
Files changed (147) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/AGENTS.md +6 -6
  3. package/CHANGELOG.md +66 -0
  4. package/README.md +1 -1
  5. package/agents/templates/.ai-council.yml.example +55 -24
  6. package/dist/agent-src/commands/cost/report.md +2 -2
  7. package/dist/agent-src/commands/team/adversarial.md +8 -7
  8. package/dist/agent-src/commands/team/delegate.md +13 -12
  9. package/dist/agent-src/commands/team/review.md +9 -8
  10. package/dist/agent-src/commands/team/status.md +9 -8
  11. package/dist/agent-src/commands/team.md +29 -19
  12. package/dist/agent-src/contexts/execution/auto-dispatch-classification.md +88 -2
  13. package/dist/agent-src/contexts/execution/auto-orchestration-activation.md +29 -21
  14. package/dist/agent-src/contexts/execution/host-capability-manifest.md +26 -15
  15. package/dist/agent-src/contexts/execution/orchestration-telemetry.md +18 -0
  16. package/dist/agent-src/contexts/execution/subagent-response-contract.md +54 -0
  17. package/dist/agent-src/contexts/execution/subagent-routing.md +13 -11
  18. package/dist/agent-src/contexts/execution/subagent-spawn-contract.md +25 -0
  19. package/dist/agent-src/contexts/execution/subagent-steering.md +6 -3
  20. package/dist/agent-src/rules/delegation-policy.md +11 -7
  21. package/dist/agent-src/rules/settings-ask-protocol.md +8 -7
  22. package/dist/agent-src/rules/token-budget-discipline.md +1 -1
  23. package/dist/agent-src/skills/forensics-report/SKILL.md +114 -0
  24. package/dist/agent-src/skills/forensics-report/evals/triggers.json +17 -0
  25. package/dist/agent-src/skills/premortem/SKILL.md +30 -1
  26. package/dist/agent-src/skills/reasoning-orchestrator/SKILL.md +2 -1
  27. package/dist/agent-src/skills/subagent-orchestration/SKILL.md +7 -5
  28. package/dist/agent-src/skills/subagent-orchestration/schemas/subagent-status.json +61 -3
  29. package/dist/agent-src/skills/test-case-discovery/SKILL.md +1 -1
  30. package/dist/agent-src/templates/AGENTS.md +4 -4
  31. package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
  32. package/dist/agent-src/templates/roadmaps.md +23 -0
  33. package/dist/cli-delegate/{chunk-22GCIJ2O.js → chunk-66OVWGB5.js} +1 -1
  34. package/dist/cli-delegate/{chunk-GB2FSUEE.js → chunk-LCFJIQ4J.js} +32 -0
  35. package/dist/cli-delegate/{chunk-VSYT7OHO.js → chunk-LVRYBKTI.js} +31 -2
  36. package/dist/cli-delegate/{chunk-DVRS2KVR.js → chunk-MMXA5GY7.js} +10 -17
  37. package/dist/cli-delegate/{chunk-JXARCGVN.js → chunk-MP3JT3IU.js} +1 -1
  38. package/dist/cli-delegate/{chunk-PQSAQKHW.js → chunk-NZ5N2XQL.js} +3 -3
  39. package/dist/cli-delegate/{chunk-7OMQCH4V.js → chunk-OJM3MBJM.js} +1 -1
  40. package/dist/cli-delegate/{chunk-2BE5I2MN.js → chunk-RCIA67XN.js} +111 -90
  41. package/dist/cli-delegate/cmd_analyze_session.js +1 -1
  42. package/dist/cli-delegate/cmd_brand_status.js +1 -1
  43. package/dist/cli-delegate/cmd_conformance.js +6 -43
  44. package/dist/cli-delegate/cmd_doctor.js +9 -5
  45. package/dist/cli-delegate/cmd_explain.js +2 -2
  46. package/dist/cli-delegate/cmd_export.js +4 -4
  47. package/dist/cli-delegate/cmd_handoff.js +3 -3
  48. package/dist/cli-delegate/cmd_mcp_available.js +1 -1
  49. package/dist/cli-delegate/cmd_migrate.js +2 -2
  50. package/dist/cli-delegate/cmd_packs_active.js +2 -2
  51. package/dist/cli-delegate/cmd_prune.js +4 -4
  52. package/dist/cli-delegate/cmd_route_audit.js +2 -2
  53. package/dist/cli-delegate/cmd_route_explain.js +1 -1
  54. package/dist/cli-delegate/cmd_settings_get.js +1 -7
  55. package/dist/cli-delegate/cmd_settings_migrate.js +4 -4
  56. package/dist/cli-delegate/cmd_settings_set.js +1 -1
  57. package/dist/cli-delegate/cmd_sync.js +4 -4
  58. package/dist/cli-delegate/cmd_uninstall.js +4 -4
  59. package/dist/cli-delegate/cmd_update.js +1 -1
  60. package/dist/cli-delegate/cmd_upgrade.js +1 -1
  61. package/dist/cli-delegate/cmd_validate.js +4 -4
  62. package/dist/cli-delegate/cmd_versions.js +1 -1
  63. package/dist/discovery/deprecation-report.md +1 -1
  64. package/dist/discovery/discovery-manifest.json +69 -20
  65. package/dist/discovery/discovery-manifest.json.sha256 +1 -1
  66. package/dist/discovery/discovery-manifest.summary.md +3 -2
  67. package/dist/discovery/orphan-report.md +1 -1
  68. package/dist/discovery/packs.json +39 -2
  69. package/dist/discovery/trust-report.md +2 -2
  70. package/dist/discovery/workspaces.json +12 -4
  71. package/dist/hooks/dispatch.js +373 -35
  72. package/dist/install/install.mjs +42 -17
  73. package/dist/mcp/registry-manifest.json +2 -2
  74. package/dist/mcp/server.json +2 -2
  75. package/dist/mcp/server.mjs +651 -262
  76. package/dist/router.json +1 -1
  77. package/dist/scripts/_lib/agent_settings.js +54 -0
  78. package/dist/scripts/_lib/agent_settings.js.map +1 -1
  79. package/dist/scripts/condense.js +147 -2
  80. package/dist/scripts/condense.js.map +1 -1
  81. package/dist/server/schemas/settings.js +6 -7
  82. package/dist/server/schemas/settings.js.map +1 -1
  83. package/dist/shared/settingsCarveOut.js +0 -9
  84. package/dist/shared/settingsCarveOut.js.map +1 -1
  85. package/dist/shared/settingsClasses.js +11 -9
  86. package/dist/shared/settingsClasses.js.map +1 -1
  87. package/dist/ui/assets/{index-DmR0mbk4.js → index-JDAF00bC.js} +2 -2
  88. package/dist/ui/assets/{index-DmR0mbk4.js.map → index-JDAF00bC.js.map} +1 -1
  89. package/dist/ui/index.html +1 -1
  90. package/docs/contracts/settings-classes.md +39 -9
  91. package/package.json +1 -1
  92. package/src/agent-src/templates/AGENTS.md +4 -4
  93. package/src/config/agent-settings.template.yml +52 -66
  94. package/src/config/discovery/packs.yml +11 -0
  95. package/src/config/discovery/workspaces.yml +1 -1
  96. package/src/config/gate-violation-baselines.json +3 -3
  97. package/src/scripts/_cli/cmd_conformance.ts +14 -42
  98. package/src/scripts/_cli/cmd_doctor.ts +65 -48
  99. package/src/scripts/_lib/agent_settings.ts +58 -0
  100. package/src/scripts/_lib/auto_dispatch.corpus.test.ts +1 -1
  101. package/src/scripts/_lib/auto_dispatch.ts +23 -18
  102. package/src/scripts/_lib/capsule_trigger.ts +124 -0
  103. package/src/scripts/_lib/host_capability.ts +63 -6
  104. package/src/scripts/_lib/judgment_ladder.ts +382 -0
  105. package/src/scripts/_lib/orchestration_gate.ts +8 -0
  106. package/src/scripts/_lib/orchestration_record.ts +37 -0
  107. package/src/scripts/_lib/orchestration_savings.ts +1 -1
  108. package/src/scripts/_lib/self_repair.ts +160 -13
  109. package/src/scripts/_lib/self_repair_store.ts +28 -0
  110. package/src/scripts/_lib/subagent_capsule.ts +182 -0
  111. package/src/scripts/_lib/subagent_response.ts +19 -0
  112. package/src/scripts/_lib/subagent_spawn.ts +13 -0
  113. package/src/scripts/_lib/subagent_steering.ts +9 -4
  114. package/src/scripts/_lib/tier_budget_routing.ts +6 -1
  115. package/src/scripts/_lib/worker_budget.ts +37 -2
  116. package/src/scripts/ai_council/clients.ts +76 -54
  117. package/src/scripts/ai_council/config.ts +94 -6
  118. package/src/scripts/ai_council/handoff.ts +113 -0
  119. package/src/scripts/ai_council/modes.ts +19 -4
  120. package/src/scripts/ai_council/orchestrator.ts +105 -0
  121. package/src/scripts/ai_council/quorum.ts +84 -0
  122. package/src/scripts/ai_council/session.ts +44 -0
  123. package/src/scripts/ai_council/transport_resolver.ts +127 -1
  124. package/src/scripts/ai_team/availability.ts +110 -0
  125. package/src/scripts/ai_team/config.ts +34 -14
  126. package/src/scripts/ai_team/team_dispatch.ts +71 -29
  127. package/src/scripts/bench_critic_protocol.ts +396 -0
  128. package/src/scripts/check_detector_corpus.ts +202 -0
  129. package/src/scripts/check_hook_bundle_freshness.ts +4 -1
  130. package/src/scripts/condense.ts +162 -2
  131. package/src/scripts/council_cli.ts +401 -25
  132. package/src/scripts/forensics_report.ts +333 -0
  133. package/src/scripts/hooks/delegation_nudge_hook.ts +69 -35
  134. package/src/scripts/lint_discovery_vocabulary.ts +2 -0
  135. package/src/scripts/lint_never_silent.ts +276 -0
  136. package/src/scripts/lint_no_activation_gates.ts +271 -0
  137. package/src/scripts/orchestration_record.ts +8 -0
  138. package/src/scripts/rebuild_hook_bundle.ts +177 -0
  139. package/src/scripts/release.ts +74 -2
  140. package/src/scripts/routing_doctor.ts +21 -36
  141. package/src/scripts/score_skill_selection.ts +19 -5
  142. package/src/scripts/self_repair_cli.ts +287 -57
  143. package/src/scripts/team_events_spike.ts +271 -0
  144. package/src/scripts/team_review_gate_hook.ts +31 -9
  145. package/src/server/schemas/settings.ts +10 -17
  146. package/src/shared/settingsCarveOut.ts +0 -10
  147. package/src/shared/settingsClasses.ts +11 -9
@@ -0,0 +1,177 @@
1
+ #!/usr/bin/env tsx
2
+ /**
3
+ * Safe self-heal for the self-hosted hook bundle — the builder that
4
+ * `check_hook_bundle_freshness` deliberately is not.
5
+ *
6
+ * WHY THIS EXISTS — measured, not hypothetical.
7
+ *
8
+ * The freshness gate reds after ANY hook-source merge from main, because
9
+ * `dist/hooks/dispatch.js` is untracked and nothing in the local chain
10
+ * rebuilds it. On 2026-08-09 that turned `task release` (9.28.0) into a
11
+ * blocked push with a remedy the operator had to run by hand — a routine
12
+ * pre-push chore promoted to a release incident. The gate is right to never
13
+ * build (its documented hazard: esbuild overwriting the dispatcher a running
14
+ * hook is executing can wedge the tool loop). The hazard is in HOW a naive
15
+ * rebuild writes, not in rebuilding: this script builds to a sibling `.new`
16
+ * file, proves the result actually dispatches, and only then renames it into
17
+ * place — an atomic same-directory rename that a running hook holding an open
18
+ * file descriptor never observes mid-write.
19
+ *
20
+ * The `.new` file MUST live in `dist/hooks/` (not a temp dir): the bundle
21
+ * resolves `src/scripts/hook_manifest.yaml` relative to its OWN location via
22
+ * the `__AGENT_CONFIG_BUNDLE__` path-depth switch. Built anywhere else, every
23
+ * probe answers "manifest missing" with exit 0 and the smoke test proves
24
+ * nothing (measured 2026-08-08).
25
+ *
26
+ * The esbuild invocation is NOT duplicated here — it is read from
27
+ * `package.json`'s `build:hooks` script and only the `--outfile=` is
28
+ * rewritten, so the flag set (banner, defines, target) has exactly one home.
29
+ *
30
+ * Behaviour:
31
+ * no self-hosted bundle → no-op (fresh checkout / CI — nothing is running
32
+ * stale code, and materialising a bundle here would
33
+ * change what other gates see)
34
+ * bundle fresh → no-op
35
+ * bundle stale → rebuild to `.new`, probe, rename into place
36
+ * build or probe fails → the `.new` file is removed and the OLD bundle
37
+ * keeps running (old-but-working beats broken);
38
+ * exit 1 so the freshness gate behind this still
39
+ * reds and the failure is loud.
40
+ *
41
+ * Exit codes: 0 = fresh / healed / nothing to heal · 1 = heal failed.
42
+ */
43
+ import { spawnSync } from "node:child_process";
44
+ import * as fs from "node:fs";
45
+ import * as path from "node:path";
46
+ import { fileURLToPath } from "node:url";
47
+
48
+ import { check } from "./check_hook_bundle_freshness.js";
49
+
50
+ const HERE = path.dirname(fileURLToPath(import.meta.url));
51
+ const REPO_ROOT = path.resolve(HERE, "..", "..");
52
+ const LIVE_REL = path.join("dist", "hooks", "dispatch.js");
53
+ const NEW_REL = path.join("dist", "hooks", "dispatch.new.js");
54
+
55
+ /**
56
+ * Rewrite the `--outfile=` of the canonical `build:hooks` script to the
57
+ * `.new` sibling. Returns null when the expected outfile token is absent —
58
+ * a changed `package.json` must fail loudly, never build to a guessed path.
59
+ */
60
+ export function rewriteOutfile(script: string, newOutfile: string): string | null {
61
+ const token = `--outfile=${LIVE_REL}`;
62
+ if (!script.includes(token)) return null;
63
+ return script.replace(token, `--outfile=${newOutfile}`);
64
+ }
65
+
66
+ /**
67
+ * Verdict on a dispatcher probe. The probe contract is pinned against the
68
+ * real dry-run output: exit 0 AND parseable JSON AND a non-empty `concerns`
69
+ * array. Exit 0 alone is NOT enough — a bundle that cannot find its manifest
70
+ * answers "manifest missing" with exit 0 (measured 2026-08-08), which is
71
+ * exactly the broken dispatcher this probe exists to keep out of the live
72
+ * path. Returns null on pass, a human-readable reason on failure.
73
+ */
74
+ export function probeVerdict(exitCode: number, stdout: string): string | null {
75
+ if (exitCode !== 0) return `probe exited ${String(exitCode)}`;
76
+ let parsed: unknown;
77
+ try {
78
+ parsed = JSON.parse(stdout);
79
+ } catch {
80
+ return `probe output is not JSON: ${stdout.slice(0, 120)}`;
81
+ }
82
+ const concerns = (parsed as { concerns?: unknown }).concerns;
83
+ if (!Array.isArray(concerns) || concerns.length === 0) {
84
+ return "probe JSON carries no concerns — the bundle dispatched nothing";
85
+ }
86
+ return null;
87
+ }
88
+
89
+ function cleanup(newAbs: string): void {
90
+ try {
91
+ fs.rmSync(newAbs, { force: true });
92
+ } catch {
93
+ // Best-effort: a leftover .new file is inert (nothing executes it).
94
+ }
95
+ }
96
+
97
+ export function main(): number {
98
+ const r = check(REPO_ROOT);
99
+
100
+ if (r.skipped) {
101
+ process.stdout.write(
102
+ `✅ OK rebuild_hook_bundle: no self-hosted ${LIVE_REL} — nothing runs stale code, nothing to heal\n`,
103
+ );
104
+ return 0;
105
+ }
106
+ if (r.stale.length === 0) {
107
+ process.stdout.write("✅ OK rebuild_hook_bundle: bundle already fresh — no rebuild needed\n");
108
+ return 0;
109
+ }
110
+
111
+ process.stdout.write(
112
+ `🔧 hook bundle stale (${String(r.stale.length)} newer source(s)) — rebuilding safely (build .new → probe → atomic rename)\n`,
113
+ );
114
+
115
+ const pkg = JSON.parse(fs.readFileSync(path.join(REPO_ROOT, "package.json"), "utf-8")) as {
116
+ scripts?: Record<string, string>;
117
+ };
118
+ const buildScript = pkg.scripts?.["build:hooks"];
119
+ if (buildScript === undefined) {
120
+ console.error("❌ rebuild_hook_bundle: package.json has no build:hooks script");
121
+ return 1;
122
+ }
123
+ const cmd = rewriteOutfile(buildScript, NEW_REL);
124
+ if (cmd === null) {
125
+ console.error(
126
+ `❌ rebuild_hook_bundle: build:hooks no longer targets --outfile=${LIVE_REL} — ` +
127
+ "refusing to build to a guessed path; update this script alongside package.json",
128
+ );
129
+ return 1;
130
+ }
131
+
132
+ const newAbs = path.join(REPO_ROOT, NEW_REL);
133
+ const build = spawnSync("sh", ["-c", cmd], {
134
+ cwd: REPO_ROOT,
135
+ encoding: "utf-8",
136
+ env: { ...process.env, PATH: `${path.join(REPO_ROOT, "node_modules", ".bin")}:${process.env["PATH"] ?? ""}` },
137
+ });
138
+ if (build.status !== 0) {
139
+ cleanup(newAbs);
140
+ console.error(`❌ rebuild_hook_bundle: esbuild failed (${String(build.status)}):`);
141
+ console.error((build.stderr || build.stdout || "").trim());
142
+ return 1;
143
+ }
144
+
145
+ const probe = spawnSync(
146
+ "node",
147
+ [newAbs, "--platform", "claude", "--event", "pre_tool_use", "--dry-run"],
148
+ { cwd: REPO_ROOT, encoding: "utf-8", input: "{}" },
149
+ );
150
+ const verdict = probeVerdict(probe.status ?? 1, probe.stdout ?? "");
151
+ if (verdict !== null) {
152
+ cleanup(newAbs);
153
+ console.error(`❌ rebuild_hook_bundle: new bundle failed its probe — ${verdict}`);
154
+ console.error(" The old bundle stays in place (old-but-working beats broken).");
155
+ return 1;
156
+ }
157
+
158
+ fs.renameSync(newAbs, path.join(REPO_ROOT, LIVE_REL));
159
+ process.stdout.write(
160
+ `✅ OK rebuild_hook_bundle: rebuilt + probed + renamed into place (${String(r.checked)} bundled source(s))\n`,
161
+ );
162
+ return 0;
163
+ }
164
+
165
+ const invokedDirectly = ((): boolean => {
166
+ const entry = process.argv[1];
167
+ if (entry === undefined) return false;
168
+ try {
169
+ return fs.realpathSync(path.resolve(entry)) === fs.realpathSync(fileURLToPath(import.meta.url));
170
+ } catch {
171
+ return false;
172
+ }
173
+ })();
174
+
175
+ if (invokedDirectly) {
176
+ process.exit(main());
177
+ }
@@ -1073,6 +1073,78 @@ function _tag_exists_remote(tag: string): boolean {
1073
1073
  return r.returncode === 0;
1074
1074
  }
1075
1075
 
1076
+ /**
1077
+ * True when git rejected a tag push because the ref already exists on the
1078
+ * remote. Wording pinned from the real failure (2026-08-09, 9.28.0 — two
1079
+ * concurrent resume runs, the loser crashed):
1080
+ *
1081
+ * ! [remote rejected] 9.28.0 -> 9.28.0 (cannot lock ref 'refs/tags/9.28.0': reference already exists)
1082
+ *
1083
+ * plus the `[rejected] … (already exists)` variant git emits when the local
1084
+ * and remote tag objects differ.
1085
+ */
1086
+ export function _is_tag_already_exists(stderr: string, stdout: string): boolean {
1087
+ const text = `${stderr}\n${stdout}`;
1088
+ return /\[(?:remote )?rejected\]/i.test(text) && /already exists/i.test(text);
1089
+ }
1090
+
1091
+ /** Peeled commit the remote tag points at, or null when the tag is absent. */
1092
+ function _remote_tag_commit(tag: string): string | null {
1093
+ const r = run(['git', 'ls-remote', REMOTE, `refs/tags/${tag}`, `refs/tags/${tag}^{}`], {
1094
+ check: false,
1095
+ capture: true,
1096
+ });
1097
+ if (r.returncode !== 0) return null;
1098
+ const lines = r.stdout.trim().split('\n').filter(Boolean);
1099
+ if (lines.length === 0) return null;
1100
+ // An annotated tag lists the tag object AND the peeled `^{}` commit — the
1101
+ // peeled line wins so the comparison is commit-vs-commit regardless of
1102
+ // whether the two runs minted distinct tag objects for the same commit.
1103
+ const peeled = lines.find((l) => l.trimEnd().endsWith('^{}'));
1104
+ return (peeled ?? (lines[0] as string)).split('\t')[0] ?? null;
1105
+ }
1106
+
1107
+ /**
1108
+ * Push a tag, tolerating exactly ONE failure: the concurrent-release race.
1109
+ * `_tag_exists_remote` in step 8 is a live check, but between it and this
1110
+ * push a parallel `task release` run can land the same tag (measured
1111
+ * 2026-08-09, 9.28.0). When the remote tag already points at the same commit
1112
+ * as the local one, the repository IS in the desired state — continue. Any
1113
+ * other rejection, and a same-name tag on a DIFFERENT commit, stays fatal
1114
+ * with the push output as the error.
1115
+ */
1116
+ function _push_tag(tag: string): void {
1117
+ const first = run(['git', 'push', REMOTE, tag], { check: false, capture: true });
1118
+ if (first.returncode === 0) {
1119
+ process.stdout.write(first.stdout);
1120
+ process.stderr.write(first.stderr);
1121
+ return;
1122
+ }
1123
+ if (_is_tag_already_exists(first.stderr, first.stdout)) {
1124
+ const remote = _remote_tag_commit(tag);
1125
+ const local = git(['rev-list', '-n', '1', tag], { capture: true });
1126
+ if (remote !== null && remote === local) {
1127
+ process.stdout.write(
1128
+ `↻ tag ${tag} already on ${REMOTE} at the same commit — a parallel run pushed it; continuing\n`,
1129
+ );
1130
+ return;
1131
+ }
1132
+ process.stdout.write(first.stdout);
1133
+ process.stderr.write(first.stderr);
1134
+ die(
1135
+ `tag ${tag} exists on ${REMOTE} pointing at ${remote ?? '<unreadable>'} while the local tag ` +
1136
+ `points at ${local} — refusing to overwrite a published tag; ` +
1137
+ 'delete the wrong one deliberately and re-run',
1138
+ );
1139
+ }
1140
+ process.stdout.write(first.stdout);
1141
+ process.stderr.write(first.stderr);
1142
+ die(
1143
+ `push of tag ${tag} failed (exit ${first.returncode}) — ` +
1144
+ 'the push output above is the real error (a pre-push gate, credentials, or protection)',
1145
+ );
1146
+ }
1147
+
1076
1148
  /** Most recent PR (any state) with `release/X.Y.Z` as head, or null. */
1077
1149
  function _pr_for_branch(branch: string): Record<string, unknown> | null {
1078
1150
  const r = run(
@@ -2029,7 +2101,7 @@ function execute(
2029
2101
  _step(8, total, `Tag ${plan.target} already on ${REMOTE} — skip`);
2030
2102
  } else {
2031
2103
  _step(8, total, `Tag ${plan.target} exists locally — push only`);
2032
- run(['git', 'push', REMOTE, plan.target]);
2104
+ _push_tag(plan.target);
2033
2105
  }
2034
2106
  } else {
2035
2107
  // Sequencing is load-bearing (release-truth Phase 1, council
@@ -2047,7 +2119,7 @@ function execute(
2047
2119
  );
2048
2120
  }
2049
2121
  run(['git', 'tag', '-a', plan.target, '-m', tag_message_from_section(merged!.body, plan.target)]);
2050
- run(['git', 'push', REMOTE, plan.target]);
2122
+ _push_tag(plan.target);
2051
2123
  }
2052
2124
 
2053
2125
  // ─── 9. GitHub Release ──────────────────────────────────────────────────
@@ -53,7 +53,7 @@ import {
53
53
  import * as hooks_status from "./hooks_status.js";
54
54
  import { load_agent_settings } from "./_lib/agent_settings.js";
55
55
  import {
56
- resolveHostCapabilities,
56
+ probeHostCapabilities,
57
57
  type HostCapabilityManifest,
58
58
  } from "./_lib/host_capability.js";
59
59
  import {
@@ -99,10 +99,9 @@ export interface FreshnessReport {
99
99
  }
100
100
 
101
101
  export interface OrchestrationReport {
102
- enabled: boolean;
103
- auto: string;
102
+ /** `emergency.orchestration_halt` — the one audited incident switch. */
103
+ halted: boolean;
104
104
  downshift: boolean;
105
- budget_routing: string;
106
105
  /** cost.budgets.per_tier ceilings + live cool-down state per tier. */
107
106
  tier_budgets: Record<string, { ceiling_usd: number | null; cooldown_until_ms: number }>;
108
107
  host_manifest: HostCapabilityManifest;
@@ -141,22 +140,19 @@ export interface OrchestrationReport {
141
140
  }
142
141
 
143
142
  /**
144
- * Adherence ≠ delivery: budget routing binds via policy prose, so the
145
- * doctor checks the DELIVERY evidence — orchestration audit lines that
146
- * carry a tier decision. Eligible dispatches recorded while
147
- * `budget_routing != off` but ZERO tier-carrying lines → WARN with the
148
- * exact reason. Read-only over agents/runtime/state/audit/*.jsonl; no
149
- * new state. This closes the same blind-spot class the session-canary
150
- * incident exposed (policy present, delivery silently absent).
143
+ * Adherence ≠ delivery: budget-aware tier routing has no on/off setting any
144
+ * more (always-on orchestration removed `subagents.budget_routing`), so the
145
+ * doctor always checks the DELIVERY evidence — orchestration audit lines
146
+ * that carry a tier decision. Dispatches recorded with ZERO tier-carrying
147
+ * lines → WARN with the exact reason. Read-only over
148
+ * agents/runtime/state/audit/*.jsonl; no new state. This closes the same
149
+ * blind-spot class the session-canary incident exposed (policy present,
150
+ * delivery silently absent).
151
151
  */
152
152
  export function check_budget_delivery(
153
153
  workspace_root: string,
154
- budget_routing: string,
155
154
  ): OrchestrationReport["delivery"] {
156
155
  const out = { eligible_dispatches: 0, budget_evidence_lines: 0, warning: "" };
157
- if (budget_routing === "off") {
158
- return out;
159
- }
160
156
  const auditDir = path.join(workspace_root, "agents", "runtime", "state", "audit");
161
157
  let files: string[] = [];
162
158
  try {
@@ -188,7 +184,7 @@ export function check_budget_delivery(
188
184
  }
189
185
  if (out.eligible_dispatches > 0 && out.budget_evidence_lines === 0) {
190
186
  out.warning =
191
- `budget_routing=${budget_routing} is policy-bound but shows no delivery evidence: ` +
187
+ `budget-aware tier routing shows no delivery evidence: ` +
192
188
  `${out.eligible_dispatches} orchestration dispatch(es) recorded, 0 carry a tier decision — ` +
193
189
  `the relation may not be running (adherence != delivery)`;
194
190
  }
@@ -237,17 +233,16 @@ export function collect_orchestration(
237
233
  ): OrchestrationReport {
238
234
  const settings = load_agent_settings({ cwd: workspace_root });
239
235
  const sub = (settings["subagents"] ?? {}) as Record<string, unknown>;
240
- const enabled = sub["enabled"] !== false;
241
- const auto = typeof sub["auto"] === "string" ? (sub["auto"] as string) : "ask";
236
+ const emergency = (settings["emergency"] ?? {}) as Record<string, unknown>;
237
+ const halted = emergency["orchestration_halt"] === true;
242
238
  const downshift = sub["downshift"] !== false;
243
- const host_manifest = resolveHostCapabilities(platform, sub["host_capabilities"]);
239
+ const host_manifest = probeHostCapabilities(platform);
244
240
  const activationInputs: ActivationInputs = {
245
- enabled,
246
- auto: (auto === "on" || auto === "off" ? auto : "ask") as ActivationInputs["auto"],
241
+ halted,
247
242
  subagent_spawn: host_manifest.subagent_spawn,
248
243
  };
249
244
  // Canonical delegable probe: the gate verdict shows which activation
250
- // layer (settings / host primitive) decides on THIS installation.
245
+ // layer (emergency halt / host primitive) decides on THIS installation.
251
246
  const probe = classifyTask(
252
247
  { size_estimate: 5, independent_slices: 3 },
253
248
  activationInputs,
@@ -256,14 +251,6 @@ export function collect_orchestration(
256
251
  const cost_budgets = (cost["budgets"] ?? {}) as Record<string, unknown>;
257
252
  const tracking_dir = path.join(workspace_root, "agents", "cost-tracking");
258
253
  const ledger_present = fs.existsSync(path.join(tracking_dir, "sessions.jsonl"));
259
- // YAML 1.1 trap: an unquoted `off` parses as boolean false — coerce it
260
- // back to the enum string so the check honors the user's intent.
261
- const budget_routing =
262
- sub["budget_routing"] === false
263
- ? "off"
264
- : typeof sub["budget_routing"] === "string"
265
- ? (sub["budget_routing"] as string)
266
- : "ask";
267
254
  const per_tier = (cost_budgets["per_tier"] ?? {}) as Record<string, unknown>;
268
255
  const cooldowns = readCooldowns(tracking_dir);
269
256
  const tier_budgets: OrchestrationReport["tier_budgets"] = {};
@@ -289,10 +276,8 @@ export function collect_orchestration(
289
276
  };
290
277
  }
291
278
  return {
292
- enabled,
293
- auto,
279
+ halted,
294
280
  downshift,
295
- budget_routing,
296
281
  tier_budgets,
297
282
  host_manifest,
298
283
  host_platform: platform,
@@ -301,7 +286,7 @@ export function collect_orchestration(
301
286
  cost_budgets,
302
287
  ledger_present,
303
288
  sample,
304
- delivery: check_budget_delivery(workspace_root, budget_routing),
289
+ delivery: check_budget_delivery(workspace_root),
305
290
  };
306
291
  }
307
292
 
@@ -516,8 +501,8 @@ function _render(report: DoctorReport): string {
516
501
  lines.push("");
517
502
  const o = report.orchestration;
518
503
  lines.push(
519
- `Orchestration: enabled=${o.enabled} · auto=${o.auto} · downshift=${o.downshift} · ` +
520
- `budget_routing=${o.budget_routing} · subagent_spawn=${o.host_manifest.subagent_spawn} ` +
504
+ `Orchestration: halted=${o.halted} · downshift=${o.downshift} · ` +
505
+ `subagent_spawn=${o.host_manifest.subagent_spawn} ` +
521
506
  `(host=${o.host_platform}${o.host_platform_assumed ? ", ASSUMED" : ", observed"}) · ` +
522
507
  `ledger=${o.ledger_present ? "present" : "absent"}`,
523
508
  );
@@ -19,10 +19,22 @@
19
19
  * pass = (a) >= 0.90 OR (b) >= 0.95
20
20
  * fail = (a) < 0.80 AND (b) < 0.80 → cluster needs `routes_to`
21
21
  *
22
- * NOTE: the .py reads the legacy `.agent-src.uncondensed/skills` literal;
23
- * this faithful twin replicates it byte-for-byte. When inputs are missing
24
- * the .py raises an uncaught FileNotFoundError → exit 1; this twin mirrors
25
- * that by throwing.
22
+ * REPOINTED 2026-08-09 (P2.2 of road-to-rule-delivery-integrity). The original
23
+ * read the uncondensed legacy skills tree, faithfully replicating a `.py` twin —
24
+ * but ADR-051 retired that tree as dead and it does not exist in this checkout.
25
+ * A scorer pointed at a missing directory does not fail: its glob yields
26
+ * nothing, every fixture scores against an empty skill set, and the output is a
27
+ * baseline of silent zeros. That is worse than an error, because a baseline is
28
+ * exactly what P2.2's pre-registered criteria compare against — the measurement
29
+ * would have been void and would have looked fine.
30
+ *
31
+ * Now reads `SRC_SKILLS()` from the shared resolver. `dist/agent-src/skills`
32
+ * would be equally valid (ADR-201 makes the projection a byte-exact copy, so
33
+ * the frontmatter is identical); `src/` is chosen because it is the source of
34
+ * truth and is present even before a sync.
35
+ *
36
+ * When inputs are missing the original raised an uncaught FileNotFoundError →
37
+ * exit 1; this twin mirrors that by throwing.
26
38
  */
27
39
 
28
40
  import * as fs from 'node:fs';
@@ -30,12 +42,14 @@ import * as path from 'node:path';
30
42
  import { fileURLToPath, pathToFileURL } from 'node:url';
31
43
  import { parse as parseYaml } from 'yaml';
32
44
 
45
+ import { SRC_SKILLS } from './_lib/agent_src.js';
46
+
33
47
  const _HERE = fileURLToPath(import.meta.url);
34
48
  // src/scripts/score_skill_selection.ts → parents[2] is the repo root.
35
49
  const REPO_ROOT = path.resolve(path.dirname(_HERE), '..', '..');
36
50
  const FIXTURES = path.join(REPO_ROOT, 'tests', 'fixtures', 'skill_selection', 'fixtures.yml');
37
51
  const CLUSTERS = path.join(REPO_ROOT, 'agents', 'reports', 'skill-collision-clusters.json');
38
- const SKILLS_DIR = path.join(REPO_ROOT, '.agent-src.uncondensed', 'skills');
52
+ const SKILLS_DIR = SRC_SKILLS();
39
53
  const DEFAULT_OUT = path.join(REPO_ROOT, 'agents', 'reports', 'skill-selection-accuracy.json');
40
54
 
41
55
  const PASS_A = 0.9;