@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.
- package/.claude-plugin/marketplace.json +1 -1
- package/AGENTS.md +6 -6
- package/CHANGELOG.md +66 -0
- package/README.md +1 -1
- package/agents/templates/.ai-council.yml.example +55 -24
- package/dist/agent-src/commands/cost/report.md +2 -2
- package/dist/agent-src/commands/team/adversarial.md +8 -7
- package/dist/agent-src/commands/team/delegate.md +13 -12
- package/dist/agent-src/commands/team/review.md +9 -8
- package/dist/agent-src/commands/team/status.md +9 -8
- package/dist/agent-src/commands/team.md +29 -19
- package/dist/agent-src/contexts/execution/auto-dispatch-classification.md +88 -2
- package/dist/agent-src/contexts/execution/auto-orchestration-activation.md +29 -21
- package/dist/agent-src/contexts/execution/host-capability-manifest.md +26 -15
- package/dist/agent-src/contexts/execution/orchestration-telemetry.md +18 -0
- package/dist/agent-src/contexts/execution/subagent-response-contract.md +54 -0
- package/dist/agent-src/contexts/execution/subagent-routing.md +13 -11
- package/dist/agent-src/contexts/execution/subagent-spawn-contract.md +25 -0
- package/dist/agent-src/contexts/execution/subagent-steering.md +6 -3
- package/dist/agent-src/rules/delegation-policy.md +11 -7
- package/dist/agent-src/rules/settings-ask-protocol.md +8 -7
- package/dist/agent-src/rules/token-budget-discipline.md +1 -1
- package/dist/agent-src/skills/forensics-report/SKILL.md +114 -0
- package/dist/agent-src/skills/forensics-report/evals/triggers.json +17 -0
- package/dist/agent-src/skills/premortem/SKILL.md +30 -1
- package/dist/agent-src/skills/reasoning-orchestrator/SKILL.md +2 -1
- package/dist/agent-src/skills/subagent-orchestration/SKILL.md +7 -5
- package/dist/agent-src/skills/subagent-orchestration/schemas/subagent-status.json +61 -3
- package/dist/agent-src/skills/test-case-discovery/SKILL.md +1 -1
- package/dist/agent-src/templates/AGENTS.md +4 -4
- package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
- package/dist/agent-src/templates/roadmaps.md +23 -0
- package/dist/cli-delegate/{chunk-22GCIJ2O.js → chunk-66OVWGB5.js} +1 -1
- package/dist/cli-delegate/{chunk-GB2FSUEE.js → chunk-LCFJIQ4J.js} +32 -0
- package/dist/cli-delegate/{chunk-VSYT7OHO.js → chunk-LVRYBKTI.js} +31 -2
- package/dist/cli-delegate/{chunk-DVRS2KVR.js → chunk-MMXA5GY7.js} +10 -17
- package/dist/cli-delegate/{chunk-JXARCGVN.js → chunk-MP3JT3IU.js} +1 -1
- package/dist/cli-delegate/{chunk-PQSAQKHW.js → chunk-NZ5N2XQL.js} +3 -3
- package/dist/cli-delegate/{chunk-7OMQCH4V.js → chunk-OJM3MBJM.js} +1 -1
- package/dist/cli-delegate/{chunk-2BE5I2MN.js → chunk-RCIA67XN.js} +111 -90
- package/dist/cli-delegate/cmd_analyze_session.js +1 -1
- package/dist/cli-delegate/cmd_brand_status.js +1 -1
- package/dist/cli-delegate/cmd_conformance.js +6 -43
- package/dist/cli-delegate/cmd_doctor.js +9 -5
- package/dist/cli-delegate/cmd_explain.js +2 -2
- package/dist/cli-delegate/cmd_export.js +4 -4
- package/dist/cli-delegate/cmd_handoff.js +3 -3
- package/dist/cli-delegate/cmd_mcp_available.js +1 -1
- package/dist/cli-delegate/cmd_migrate.js +2 -2
- package/dist/cli-delegate/cmd_packs_active.js +2 -2
- package/dist/cli-delegate/cmd_prune.js +4 -4
- package/dist/cli-delegate/cmd_route_audit.js +2 -2
- package/dist/cli-delegate/cmd_route_explain.js +1 -1
- package/dist/cli-delegate/cmd_settings_get.js +1 -7
- package/dist/cli-delegate/cmd_settings_migrate.js +4 -4
- package/dist/cli-delegate/cmd_settings_set.js +1 -1
- package/dist/cli-delegate/cmd_sync.js +4 -4
- package/dist/cli-delegate/cmd_uninstall.js +4 -4
- package/dist/cli-delegate/cmd_update.js +1 -1
- package/dist/cli-delegate/cmd_upgrade.js +1 -1
- package/dist/cli-delegate/cmd_validate.js +4 -4
- package/dist/cli-delegate/cmd_versions.js +1 -1
- package/dist/discovery/deprecation-report.md +1 -1
- package/dist/discovery/discovery-manifest.json +69 -20
- package/dist/discovery/discovery-manifest.json.sha256 +1 -1
- package/dist/discovery/discovery-manifest.summary.md +3 -2
- package/dist/discovery/orphan-report.md +1 -1
- package/dist/discovery/packs.json +39 -2
- package/dist/discovery/trust-report.md +2 -2
- package/dist/discovery/workspaces.json +12 -4
- package/dist/hooks/dispatch.js +373 -35
- package/dist/install/install.mjs +42 -17
- package/dist/mcp/registry-manifest.json +2 -2
- package/dist/mcp/server.json +2 -2
- package/dist/mcp/server.mjs +651 -262
- package/dist/router.json +1 -1
- package/dist/scripts/_lib/agent_settings.js +54 -0
- package/dist/scripts/_lib/agent_settings.js.map +1 -1
- package/dist/scripts/condense.js +147 -2
- package/dist/scripts/condense.js.map +1 -1
- package/dist/server/schemas/settings.js +6 -7
- package/dist/server/schemas/settings.js.map +1 -1
- package/dist/shared/settingsCarveOut.js +0 -9
- package/dist/shared/settingsCarveOut.js.map +1 -1
- package/dist/shared/settingsClasses.js +11 -9
- package/dist/shared/settingsClasses.js.map +1 -1
- package/dist/ui/assets/{index-DmR0mbk4.js → index-JDAF00bC.js} +2 -2
- package/dist/ui/assets/{index-DmR0mbk4.js.map → index-JDAF00bC.js.map} +1 -1
- package/dist/ui/index.html +1 -1
- package/docs/contracts/settings-classes.md +39 -9
- package/package.json +1 -1
- package/src/agent-src/templates/AGENTS.md +4 -4
- package/src/config/agent-settings.template.yml +52 -66
- package/src/config/discovery/packs.yml +11 -0
- package/src/config/discovery/workspaces.yml +1 -1
- package/src/config/gate-violation-baselines.json +3 -3
- package/src/scripts/_cli/cmd_conformance.ts +14 -42
- package/src/scripts/_cli/cmd_doctor.ts +65 -48
- package/src/scripts/_lib/agent_settings.ts +58 -0
- package/src/scripts/_lib/auto_dispatch.corpus.test.ts +1 -1
- package/src/scripts/_lib/auto_dispatch.ts +23 -18
- package/src/scripts/_lib/capsule_trigger.ts +124 -0
- package/src/scripts/_lib/host_capability.ts +63 -6
- package/src/scripts/_lib/judgment_ladder.ts +382 -0
- package/src/scripts/_lib/orchestration_gate.ts +8 -0
- package/src/scripts/_lib/orchestration_record.ts +37 -0
- package/src/scripts/_lib/orchestration_savings.ts +1 -1
- package/src/scripts/_lib/self_repair.ts +160 -13
- package/src/scripts/_lib/self_repair_store.ts +28 -0
- package/src/scripts/_lib/subagent_capsule.ts +182 -0
- package/src/scripts/_lib/subagent_response.ts +19 -0
- package/src/scripts/_lib/subagent_spawn.ts +13 -0
- package/src/scripts/_lib/subagent_steering.ts +9 -4
- package/src/scripts/_lib/tier_budget_routing.ts +6 -1
- package/src/scripts/_lib/worker_budget.ts +37 -2
- package/src/scripts/ai_council/clients.ts +76 -54
- package/src/scripts/ai_council/config.ts +94 -6
- package/src/scripts/ai_council/handoff.ts +113 -0
- package/src/scripts/ai_council/modes.ts +19 -4
- package/src/scripts/ai_council/orchestrator.ts +105 -0
- package/src/scripts/ai_council/quorum.ts +84 -0
- package/src/scripts/ai_council/session.ts +44 -0
- package/src/scripts/ai_council/transport_resolver.ts +127 -1
- package/src/scripts/ai_team/availability.ts +110 -0
- package/src/scripts/ai_team/config.ts +34 -14
- package/src/scripts/ai_team/team_dispatch.ts +71 -29
- package/src/scripts/bench_critic_protocol.ts +396 -0
- package/src/scripts/check_detector_corpus.ts +202 -0
- package/src/scripts/check_hook_bundle_freshness.ts +4 -1
- package/src/scripts/condense.ts +162 -2
- package/src/scripts/council_cli.ts +401 -25
- package/src/scripts/forensics_report.ts +333 -0
- package/src/scripts/hooks/delegation_nudge_hook.ts +69 -35
- package/src/scripts/lint_discovery_vocabulary.ts +2 -0
- package/src/scripts/lint_never_silent.ts +276 -0
- package/src/scripts/lint_no_activation_gates.ts +271 -0
- package/src/scripts/orchestration_record.ts +8 -0
- package/src/scripts/rebuild_hook_bundle.ts +177 -0
- package/src/scripts/release.ts +74 -2
- package/src/scripts/routing_doctor.ts +21 -36
- package/src/scripts/score_skill_selection.ts +19 -5
- package/src/scripts/self_repair_cli.ts +287 -57
- package/src/scripts/team_events_spike.ts +271 -0
- package/src/scripts/team_review_gate_hook.ts +31 -9
- package/src/server/schemas/settings.ts +10 -17
- package/src/shared/settingsCarveOut.ts +0 -10
- 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
|
+
}
|
package/src/scripts/release.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
103
|
-
|
|
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
|
|
145
|
-
*
|
|
146
|
-
*
|
|
147
|
-
*
|
|
148
|
-
* exact reason. Read-only over
|
|
149
|
-
* new state. This closes the same
|
|
150
|
-
* incident exposed (policy present,
|
|
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
|
-
`
|
|
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
|
|
241
|
-
const
|
|
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 =
|
|
239
|
+
const host_manifest = probeHostCapabilities(platform);
|
|
244
240
|
const activationInputs: ActivationInputs = {
|
|
245
|
-
|
|
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 (
|
|
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
|
-
|
|
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
|
|
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:
|
|
520
|
-
`
|
|
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
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
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 =
|
|
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;
|