@event4u/agent-config 14.2.0 → 14.5.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 +33 -252
- package/agents/templates/.ai-council.yml.example +35 -0
- package/dist/agent-src/contexts/execution/roadmap-process-loop.md +83 -12
- package/dist/agent-src/rules/roadmap-progress-sync.md +48 -4
- package/dist/agent-src/scripts/resume_probe.ts +154 -9
- package/dist/agent-src/scripts/roadmap_gates.ts +210 -6
- package/dist/agent-src/skills/roadmap-management/SKILL.md +9 -4
- package/dist/agent-src/templates/agent-settings.md +70 -0
- package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
- package/dist/agent-src/templates/scripts/telemetry/remote.ts +571 -0
- package/dist/agent-src/templates/scripts/telemetry/settings.ts +222 -44
- package/dist/cli/python/workspace_drive.js +8 -3
- package/dist/cli/python/workspace_drive.js.map +1 -1
- package/dist/cli/python/workspace_hosts.js +16 -12
- package/dist/cli/python/workspace_hosts.js.map +1 -1
- package/dist/cli/registry.js +2 -0
- package/dist/cli/registry.js.map +1 -1
- package/dist/cli-delegate/{chunk-F3YUF7SA.js → chunk-3UCJWMMP.js} +1 -1
- package/dist/cli-delegate/chunk-F7DQWYR5.js +69 -0
- package/dist/cli-delegate/{chunk-7PZ4FSZB.js → chunk-FBKL6JWT.js} +61 -2
- package/dist/cli-delegate/{chunk-MNWRDMR5.js → chunk-PKOBQADP.js} +1 -1
- package/dist/cli-delegate/cmd_conformance.js +26 -11
- package/dist/cli-delegate/cmd_doctor.js +2 -2
- package/dist/cli-delegate/cmd_handoff.js +3 -3
- package/dist/cli-delegate/cmd_route_audit.js +2 -2
- package/dist/cli-delegate/cmd_settings_set.js +17 -3
- package/dist/discovery/deprecation-report.md +1 -1
- package/dist/discovery/discovery-manifest.json +4 -4
- package/dist/discovery/discovery-manifest.json.sha256 +1 -1
- package/dist/discovery/discovery-manifest.summary.md +1 -1
- package/dist/discovery/dormancy-report.md +1 -1
- package/dist/discovery/orphan-report.md +1 -1
- package/dist/discovery/packs.json +2 -2
- package/dist/discovery/trust-report.md +1 -1
- package/dist/discovery/workspaces.json +2 -2
- package/dist/hooks/dispatch.js +3754 -1721
- package/dist/mcp/registry-manifest.json +2 -2
- package/dist/mcp/server.json +2 -2
- package/dist/mcp/server.mjs +1070 -408
- package/dist/scripts/_lib/host_launch.js +66 -0
- package/dist/scripts/_lib/host_launch.js.map +1 -0
- package/dist/server/io/yamlIO.js +175 -3
- package/dist/server/io/yamlIO.js.map +1 -1
- package/dist/server/routes/wizard.js +20 -1
- package/dist/server/routes/wizard.js.map +1 -1
- package/dist/shared/settingsConsent.js +25 -0
- package/dist/shared/settingsConsent.js.map +1 -1
- package/docs/contracts/settings-classes.md +11 -0
- package/docs/guidelines/agent-infra/roadmap-progress-mechanics.md +2 -1
- package/package.json +1 -1
- package/src/agent-src/scripts/resume_probe.ts +154 -9
- package/src/agent-src/scripts/roadmap_gates.ts +210 -6
- package/src/agent-src/templates/scripts/telemetry/remote.ts +571 -0
- package/src/agent-src/templates/scripts/telemetry/settings.ts +222 -44
- package/src/config/estate-count-budget.json +127 -0
- package/src/config/evaluator-budgets.json +4 -4
- package/src/config/gate-coverage.yml +32 -4
- package/src/config/gate-violation-baselines.json +63 -63
- package/src/config/gitignore-block.txt +5 -0
- package/src/config/hook-latency-budget.json +40 -1
- package/src/config/hook-token-budget.json +16 -0
- package/src/config/rule-stub-ceilings.json +11 -3
- package/src/scripts/_dispatch.bash +42 -1
- package/src/scripts/_lib/council_fallback_posture.ts +81 -0
- package/src/scripts/_lib/council_fallback_wiring.ts +126 -0
- package/src/scripts/_lib/council_settings_block.ts +152 -0
- package/src/scripts/_lib/headless_invocation.ts +285 -0
- package/src/scripts/_lib/host_launch.ts +72 -0
- package/src/scripts/_lib/map_to_object.ts +16 -0
- package/src/scripts/_lib/roadmap_checkboxes.ts +88 -0
- package/src/scripts/_lib/run_checkpoint.ts +409 -0
- package/src/scripts/_lib/session_register.ts +32 -1
- package/src/scripts/_lib/stdin.ts +35 -1
- package/src/scripts/_lib/turn_end_refusals.ts +12 -4
- package/src/scripts/_lib/unattended_guard.ts +371 -0
- package/src/scripts/ai_council/config.ts +27 -1
- package/src/scripts/ai_council/events_log.ts +12 -1
- package/src/scripts/ai_council/fallback_config.ts +114 -0
- package/src/scripts/ai_council/mid_flight_fallback.ts +295 -0
- package/src/scripts/ai_council/orchestrator.ts +251 -292
- package/src/scripts/ai_council/py_parity.ts +139 -0
- package/src/scripts/ai_council/response_render.ts +99 -0
- package/src/scripts/ai_council/spend_gate.ts +123 -0
- package/src/scripts/ai_council/transport_resolver.ts +81 -13
- package/src/scripts/bench_hook_injection.ts +118 -7
- package/src/scripts/bench_hook_latency.ts +442 -16
- package/src/scripts/check_estate_count.ts +924 -0
- package/src/scripts/compile_hook_manifest.ts +67 -0
- package/src/scripts/council_cli.ts +100 -125
- package/src/scripts/decision_memo.ts +325 -0
- package/src/scripts/hook_manifest.json +1 -0
- package/src/scripts/hook_manifest.yaml +172 -25
- package/src/scripts/hooks/concern_registry.ts +6 -0
- package/src/scripts/hooks/dispatch_hook.ts +183 -10
- package/src/scripts/hooks/dispatch_issues.ts +26 -0
- package/src/scripts/hooks/end_review_nudge_hook.ts +97 -24
- package/src/scripts/hooks/hook_stdin.ts +111 -5
- package/src/scripts/hooks/host_semantics.ts +28 -0
- package/src/scripts/hooks/injection_budget.ts +731 -0
- package/src/scripts/hooks/payload_stub.ts +446 -0
- package/src/scripts/hooks/run_continuation_hook.ts +683 -0
- package/src/scripts/hooks/session_eol_hook.ts +52 -0
- package/src/scripts/hooks/telemetry_usage_hook.ts +339 -0
- package/src/scripts/hooks/tool_result_bytes_hook.ts +27 -0
- package/src/scripts/hooks/turn_end_gate_hook.ts +45 -7
- package/src/scripts/hooks_doctor.ts +16 -2
- package/src/scripts/interruption_report.ts +330 -13
- package/src/scripts/lint_hook_manifest.ts +167 -0
- package/src/scripts/roadmap_progress_hook.ts +254 -46
- package/src/scripts/run_supervise.ts +623 -0
- package/src/scripts/session_register_hook.ts +146 -12
- package/src/scripts/sessions_cli.ts +18 -6
- package/src/scripts/telemetry_disclosure_hook.ts +244 -0
- package/src/server/io/yamlIO.ts +167 -3
- package/src/shared/settingsConsent.ts +49 -2
- package/dist/cli-delegate/chunk-MMJL4TIX.js +0 -18
|
@@ -34,6 +34,7 @@
|
|
|
34
34
|
*/
|
|
35
35
|
|
|
36
36
|
import { spawnSync } from "node:child_process";
|
|
37
|
+
import * as crypto from "node:crypto";
|
|
37
38
|
import fs from "node:fs";
|
|
38
39
|
import path from "node:path";
|
|
39
40
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
@@ -41,9 +42,146 @@ import { fileURLToPath, pathToFileURL } from "node:url";
|
|
|
41
42
|
import { hardenedSpawnEnv } from "./_lib/spawn_env.js";
|
|
42
43
|
import { log_dispatch_issue } from "./hooks/dispatch_issues.js";
|
|
43
44
|
import { readHookStdin } from "./hooks/hook_stdin.js";
|
|
45
|
+
import { atomic_write_json } from "./hooks/state_io.js";
|
|
44
46
|
|
|
45
47
|
export const REPLAY_ENV_VAR = "AGENT_CONFIG_REPLAY";
|
|
46
48
|
|
|
49
|
+
/**
|
|
50
|
+
* Debounce ledger (road-to-per-turn-hook-economy step 3.1 / D-3).
|
|
51
|
+
*
|
|
52
|
+
* The hook used to re-shell the regenerator through tsx on EVERY roadmap write.
|
|
53
|
+
* A cold tsx start per write, in the roadmap-heavy workflow this repo runs, is
|
|
54
|
+
* the second of the two spawns that escape the in-process concern table.
|
|
55
|
+
*
|
|
56
|
+
* It now records the touched repo roots and regenerates ONCE at turn end.
|
|
57
|
+
* Two properties make that safe rather than merely cheaper:
|
|
58
|
+
*
|
|
59
|
+
* - **The ledger lives under `consumer_root`, not under the target root.** A
|
|
60
|
+
* touched root may be a sibling worktree, and at `stop` the payload carries no
|
|
61
|
+
* roadmap path — so a marker written beside the edited file would be
|
|
62
|
+
* unfindable at flush time. `--project-dir` is the same value on both events,
|
|
63
|
+
* which makes one ledger per consumer the only shape that closes.
|
|
64
|
+
* - **A ledger write that fails regenerates immediately instead.** Losing the
|
|
65
|
+
* dashboard-stays-in-sync guarantee silently is worse than paying the spawn,
|
|
66
|
+
* so the debounce degrades to the old behaviour rather than to a no-op.
|
|
67
|
+
*
|
|
68
|
+
* Flushed by `stop` AND `session_end`, so the worst case is a regeneration
|
|
69
|
+
* deferred to turn end (risk 4) and never a dropped one. The per-write guarantee
|
|
70
|
+
* was never a stated contract; session granularity is.
|
|
71
|
+
*/
|
|
72
|
+
export const DIRTY_LEDGER_REL = path.join(
|
|
73
|
+
"agents",
|
|
74
|
+
"runtime",
|
|
75
|
+
"state",
|
|
76
|
+
"roadmap-progress",
|
|
77
|
+
"dirty",
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
/** Events that flush the ledger rather than appending to it. */
|
|
81
|
+
export const FLUSH_EVENTS: ReadonlySet<string> = new Set([
|
|
82
|
+
"stop",
|
|
83
|
+
"session_end",
|
|
84
|
+
// The dispatcher envelope carries the agent-config event id; a bare host
|
|
85
|
+
// payload carries the native name, and the hook accepts either shape on the
|
|
86
|
+
// write path already.
|
|
87
|
+
"Stop",
|
|
88
|
+
"SessionEnd",
|
|
89
|
+
"sessionEnd",
|
|
90
|
+
// Native aliases `hook_manifest.yaml` maps onto those two events. Missing them
|
|
91
|
+
// was a real gap rather than a completeness nit: the write path keys on
|
|
92
|
+
// `tool_name`, not on the event, so a host whose turn end arrives under one of
|
|
93
|
+
// these names accumulated dirty roots that nothing ever flushed. Found by the
|
|
94
|
+
// R2 review of the commit that introduced this ledger.
|
|
95
|
+
"TaskComplete",
|
|
96
|
+
"TaskCancel",
|
|
97
|
+
"AfterAgent",
|
|
98
|
+
]);
|
|
99
|
+
|
|
100
|
+
function _ledger_dir(consumer_root: string): string {
|
|
101
|
+
return path.join(consumer_root, DIRTY_LEDGER_REL);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* One entry file per root, named from a hash of the root path.
|
|
106
|
+
*
|
|
107
|
+
* **This shape is the concurrency fix, not a style choice.** The first version
|
|
108
|
+
* kept a single JSON array and did a read-modify-write on it, which is a
|
|
109
|
+
* lost-update: two `post_tool_use` dispatches from parallel tool calls in one
|
|
110
|
+
* turn both read `[]`, the second write drops the first root, and `mark_dirty`
|
|
111
|
+
* still returns `true` — so the inline-regeneration fallback never fires and the
|
|
112
|
+
* dashboard goes silently stale, which is exactly what the debounce exists to
|
|
113
|
+
* prevent. Found by the R2 review of the commit that introduced it.
|
|
114
|
+
*
|
|
115
|
+
* Independent per-root files remove the race by construction rather than by
|
|
116
|
+
* holding a lock across a read and a write: concurrent dispatches touch
|
|
117
|
+
* different paths, and the same root twice is the same path — so N writes to one
|
|
118
|
+
* repo still leave ONE entry.
|
|
119
|
+
*/
|
|
120
|
+
function _entry_path(consumer_root: string, root: string): string {
|
|
121
|
+
const digest = crypto.createHash("sha256").update(root).digest("hex").slice(0, 16);
|
|
122
|
+
return path.join(_ledger_dir(consumer_root), `${digest}.json`);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/** Read the ledger; a missing directory or an unparseable entry reads as empty. */
|
|
126
|
+
export function read_dirty_roots(consumer_root: string): string[] {
|
|
127
|
+
let names: string[];
|
|
128
|
+
try {
|
|
129
|
+
names = fs.readdirSync(_ledger_dir(consumer_root));
|
|
130
|
+
} catch {
|
|
131
|
+
return [];
|
|
132
|
+
}
|
|
133
|
+
const roots: string[] = [];
|
|
134
|
+
for (const name of names.sort()) {
|
|
135
|
+
if (!name.endsWith(".json")) continue;
|
|
136
|
+
try {
|
|
137
|
+
const raw = fs.readFileSync(path.join(_ledger_dir(consumer_root), name), "utf8");
|
|
138
|
+
const parsed = JSON.parse(raw) as unknown;
|
|
139
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) continue;
|
|
140
|
+
const root = (parsed as { root?: unknown }).root;
|
|
141
|
+
if (typeof root === "string" && root.trim() !== "") roots.push(root);
|
|
142
|
+
} catch {
|
|
143
|
+
// A torn or hand-edited entry is dropped, never fatal.
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return Array.from(new Set(roots)).sort();
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Record each root as its own entry. Returns false when any write failed — the
|
|
151
|
+
* caller then regenerates inline, so a broken state directory costs latency
|
|
152
|
+
* rather than a silently stale dashboard.
|
|
153
|
+
*
|
|
154
|
+
* Writes go through `atomic_write_json` (tmp → rename under the dispatcher
|
|
155
|
+
* lock), which `hook-architecture-v1` requires for everything written under
|
|
156
|
+
* `agents/runtime/state/`. That call is a no-op under `AGENT_CONFIG_REPLAY=1`,
|
|
157
|
+
* which is why the replay guard stays in the caller rather than relying on it.
|
|
158
|
+
*/
|
|
159
|
+
export function mark_dirty(consumer_root: string, roots: readonly string[]): boolean {
|
|
160
|
+
if (roots.length === 0) return true;
|
|
161
|
+
let wrote = 0;
|
|
162
|
+
for (const root of roots) {
|
|
163
|
+
try {
|
|
164
|
+
atomic_write_json(_entry_path(consumer_root, root), {
|
|
165
|
+
root,
|
|
166
|
+
marked_at: new Date().toISOString(),
|
|
167
|
+
});
|
|
168
|
+
wrote += 1;
|
|
169
|
+
} catch {
|
|
170
|
+
// A partial write still needs the caller's inline fallback.
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
return wrote === roots.length;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/** Drop every entry. A missing directory is success — flush is idempotent. */
|
|
177
|
+
export function clear_dirty(consumer_root: string): void {
|
|
178
|
+
try {
|
|
179
|
+
fs.rmSync(_ledger_dir(consumer_root), { recursive: true, force: true });
|
|
180
|
+
} catch {
|
|
181
|
+
// observability never breaks the hook
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
47
185
|
// Tools whose successful execution can write to a roadmap file. We keep
|
|
48
186
|
// the list explicit so an unknown tool name (e.g. a new MCP tool that
|
|
49
187
|
// happens to mention a roadmap path in its input) does not trigger a
|
|
@@ -322,6 +460,59 @@ function _resolve_tsx_invocation(scriptPath: string): { command: string; args: s
|
|
|
322
460
|
return { command: "npx", args: ["tsx", scriptPath] };
|
|
323
461
|
}
|
|
324
462
|
|
|
463
|
+
/**
|
|
464
|
+
* Run the regenerator once per root. Extracted by step 3.1 so the write path
|
|
465
|
+
* and the flush path share ONE implementation — a debounce whose flush
|
|
466
|
+
* regenerated differently from the per-write path would be a second code path
|
|
467
|
+
* nobody compares.
|
|
468
|
+
*/
|
|
469
|
+
function _regenerate(roots: Iterable<string>, verbose: boolean): void {
|
|
470
|
+
for (const root of roots) {
|
|
471
|
+
const script = _resolve_regenerator(root);
|
|
472
|
+
if (script === null) {
|
|
473
|
+
// Phase 1 of road-to-hooks-actually-fire-in-consumers: log dispatch
|
|
474
|
+
// issue directly (this hook runs as a subprocess from the universal
|
|
475
|
+
// dispatcher; routing through the dispatcher would add latency for
|
|
476
|
+
// no benefit).
|
|
477
|
+
try {
|
|
478
|
+
log_dispatch_issue(
|
|
479
|
+
root,
|
|
480
|
+
"roadmap-progress",
|
|
481
|
+
"prerequisite_missing",
|
|
482
|
+
"update_roadmap_progress.ts not found at any of: " +
|
|
483
|
+
".augment/scripts/, dist/agent-src/scripts/, " +
|
|
484
|
+
"src/agent-src/scripts/",
|
|
485
|
+
"./agent-config hooks:install --regen " + "(or ./agent-config init)",
|
|
486
|
+
);
|
|
487
|
+
} catch {
|
|
488
|
+
// observability never breaks the hook
|
|
489
|
+
}
|
|
490
|
+
if (verbose) {
|
|
491
|
+
process.stderr.write(
|
|
492
|
+
`roadmap-progress-hook: regenerator not found for ${root}, skipping\n`,
|
|
493
|
+
);
|
|
494
|
+
}
|
|
495
|
+
continue;
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
try {
|
|
499
|
+
// The regenerator is a `.ts` script; run it through tsx (resolved from
|
|
500
|
+
// a node_modules/.bin found by walking up from the regenerator's dir,
|
|
501
|
+
// falling back to `npx tsx`), mirroring the dispatcher's `.ts` concerns.
|
|
502
|
+
// `--repo-root` + cwd pin the regeneration to the edited file's own repo.
|
|
503
|
+
const inv = _resolve_tsx_invocation(script);
|
|
504
|
+
spawnSync(inv.command, [...inv.args, "--repo-root", root], {
|
|
505
|
+
cwd: root,
|
|
506
|
+
stdio: ["ignore", "ignore", "ignore"],
|
|
507
|
+
timeout: 30000,
|
|
508
|
+
env: hardenedSpawnEnv(),
|
|
509
|
+
});
|
|
510
|
+
} catch {
|
|
511
|
+
// never propagate regenerator failures into the agent loop
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
|
|
325
516
|
export function run(
|
|
326
517
|
stdin_text: string,
|
|
327
518
|
options: { consumer_root: string; verbose?: boolean },
|
|
@@ -341,8 +532,18 @@ export function run(
|
|
|
341
532
|
}
|
|
342
533
|
}
|
|
343
534
|
|
|
344
|
-
// Unwrap dispatcher envelope (Phase 7.3, hook-architecture-v1.md).
|
|
535
|
+
// Unwrap dispatcher envelope (Phase 7.3, hook-architecture-v1.md). The
|
|
536
|
+
// envelope's `event` is read BEFORE the unwrap and kept: step 3.1 needs it to
|
|
537
|
+
// tell a write (append to the ledger) from a turn end (flush it), and the
|
|
538
|
+
// inner payload does not carry it.
|
|
539
|
+
let envelope_event = "";
|
|
540
|
+
const raw_native = payload["hook_event_name"];
|
|
541
|
+
if (typeof raw_native === "string") {
|
|
542
|
+
envelope_event = raw_native;
|
|
543
|
+
}
|
|
345
544
|
if (["schema_version", "platform", "event", "payload"].every((k) => k in payload)) {
|
|
545
|
+
const raw_event = payload["event"];
|
|
546
|
+
envelope_event = typeof raw_event === "string" ? raw_event : envelope_event;
|
|
346
547
|
const inner = payload["payload"];
|
|
347
548
|
payload =
|
|
348
549
|
typeof inner === "object" && inner !== null && !Array.isArray(inner)
|
|
@@ -350,6 +551,38 @@ export function run(
|
|
|
350
551
|
: {};
|
|
351
552
|
}
|
|
352
553
|
|
|
554
|
+
const replay = (process.env[REPLAY_ENV_VAR] ?? "").trim() === "1";
|
|
555
|
+
|
|
556
|
+
// Flush path (step 3.1): `stop` / `session_end` carry no roadmap path, so the
|
|
557
|
+
// roots come from the ledger the write path appended to. Nothing dirty → a
|
|
558
|
+
// read of one small file and exit, which is what makes this cheap enough to
|
|
559
|
+
// bind on a slot that already carries ten concerns.
|
|
560
|
+
if (FLUSH_EVENTS.has(envelope_event)) {
|
|
561
|
+
if (replay) {
|
|
562
|
+
if (verbose) {
|
|
563
|
+
process.stderr.write("roadmap-progress-hook: replay mode, skipping flush\n");
|
|
564
|
+
}
|
|
565
|
+
return 0;
|
|
566
|
+
}
|
|
567
|
+
const dirty = read_dirty_roots(consumer_root);
|
|
568
|
+
if (dirty.length === 0) {
|
|
569
|
+
return 0;
|
|
570
|
+
}
|
|
571
|
+
// Clear FIRST: a regenerator that hangs or throws must not leave a ledger
|
|
572
|
+
// that replays the same roots on every subsequent turn end. `_regenerate`
|
|
573
|
+
// already swallows its own failures, so a cleared ledger plus a failed
|
|
574
|
+
// regeneration degrades to "stale until the next roadmap write", which is
|
|
575
|
+
// the same worst case the debounce already accepts.
|
|
576
|
+
clear_dirty(consumer_root);
|
|
577
|
+
_regenerate(dirty, verbose);
|
|
578
|
+
if (verbose) {
|
|
579
|
+
process.stderr.write(
|
|
580
|
+
`roadmap-progress-hook: flushed ${dirty.length} dirty root(s) at ${envelope_event}\n`,
|
|
581
|
+
);
|
|
582
|
+
}
|
|
583
|
+
return 0;
|
|
584
|
+
}
|
|
585
|
+
|
|
353
586
|
const tool = payload["tool_name"] || payload["toolName"] || payload["tool"];
|
|
354
587
|
if (!(typeof tool === "string" && WRITE_TOOLS.has(tool))) {
|
|
355
588
|
return 0;
|
|
@@ -369,59 +602,34 @@ export function run(
|
|
|
369
602
|
return 0;
|
|
370
603
|
}
|
|
371
604
|
|
|
372
|
-
// Replay mode (`AGENT_CONFIG_REPLAY=1`) skips the
|
|
373
|
-
// so fixture dispatches
|
|
374
|
-
|
|
605
|
+
// Replay mode (`AGENT_CONFIG_REPLAY=1`) skips both the ledger write and the
|
|
606
|
+
// regenerator subprocess, so fixture dispatches leave no state and never
|
|
607
|
+
// rewrite agents/roadmaps-progress.md.
|
|
608
|
+
if (replay) {
|
|
375
609
|
if (verbose) {
|
|
376
610
|
process.stderr.write("roadmap-progress-hook: replay mode, skipping regenerator\n");
|
|
377
611
|
}
|
|
378
612
|
return 0;
|
|
379
613
|
}
|
|
380
614
|
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
root,
|
|
391
|
-
"roadmap-progress",
|
|
392
|
-
"prerequisite_missing",
|
|
393
|
-
"update_roadmap_progress.ts not found at any of: " +
|
|
394
|
-
".augment/scripts/, dist/agent-src/scripts/, " +
|
|
395
|
-
"src/agent-src/scripts/",
|
|
396
|
-
"./agent-config hooks:install --regen " + "(or ./agent-config init)",
|
|
397
|
-
);
|
|
398
|
-
} catch {
|
|
399
|
-
// observability never breaks the hook
|
|
400
|
-
}
|
|
401
|
-
if (verbose) {
|
|
402
|
-
process.stderr.write(
|
|
403
|
-
`roadmap-progress-hook: regenerator not found for ${root}, skipping\n`,
|
|
404
|
-
);
|
|
405
|
-
}
|
|
406
|
-
continue;
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
try {
|
|
410
|
-
// The regenerator is a `.ts` script; run it through tsx (resolved from
|
|
411
|
-
// a node_modules/.bin found by walking up from the regenerator's dir,
|
|
412
|
-
// falling back to `npx tsx`), mirroring the dispatcher's `.ts` concerns.
|
|
413
|
-
// `--repo-root` + cwd pin the regeneration to the edited file's own repo.
|
|
414
|
-
const inv = _resolve_tsx_invocation(script);
|
|
415
|
-
spawnSync(inv.command, [...inv.args, "--repo-root", root], {
|
|
416
|
-
cwd: root,
|
|
417
|
-
stdio: ["ignore", "ignore", "ignore"],
|
|
418
|
-
timeout: 30000,
|
|
419
|
-
env: hardenedSpawnEnv(),
|
|
420
|
-
});
|
|
421
|
-
} catch {
|
|
422
|
-
// never propagate regenerator failures into the agent loop
|
|
615
|
+
// Write path (step 3.1): record and return. The spawn this used to pay per
|
|
616
|
+
// write now happens once, at turn end. A ledger that cannot be written falls
|
|
617
|
+
// back to regenerating inline — the debounce is an optimisation and must never
|
|
618
|
+
// be the reason a dashboard silently stops updating.
|
|
619
|
+
if (mark_dirty(consumer_root, Array.from(targetRoots))) {
|
|
620
|
+
if (verbose) {
|
|
621
|
+
process.stderr.write(
|
|
622
|
+
`roadmap-progress-hook: marked ${targetRoots.size} root(s) dirty for tool=${tool}\n`,
|
|
623
|
+
);
|
|
423
624
|
}
|
|
625
|
+
return 0;
|
|
626
|
+
}
|
|
627
|
+
if (verbose) {
|
|
628
|
+
process.stderr.write(
|
|
629
|
+
"roadmap-progress-hook: ledger unwritable, regenerating inline\n",
|
|
630
|
+
);
|
|
424
631
|
}
|
|
632
|
+
_regenerate(targetRoots, verbose);
|
|
425
633
|
|
|
426
634
|
if (verbose) {
|
|
427
635
|
process.stderr.write(`roadmap-progress-hook: regenerated for tool=${tool}\n`);
|