oh-my-opencode 4.18.1 → 4.18.2
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/.agents/skills/codex-qa/scripts/lsp-e2e.sh +39 -16
- package/.agents/skills/opencode-qa/scripts/lsp-e2e.sh +111 -28
- package/bin/AGENTS.md +33 -0
- package/dist/cli/index.js +57 -18
- package/dist/cli-node/index.js +57 -18
- package/dist/hooks/anthropic-context-window-limit-recovery/empty-content-recovery-sdk.d.ts +6 -0
- package/dist/hooks/category-skill-reminder/hook.d.ts +9 -1
- package/dist/hooks/comment-checker/hook.d.ts +8 -1
- package/dist/hooks/todo-continuation-enforcer/types.d.ts +3 -0
- package/dist/index.js +560 -333
- package/dist/plugin/messages-transform.d.ts +1 -0
- package/dist/plugin-handlers/prometheus-agent-config-builder.d.ts +2 -0
- package/dist/tui.js +43 -4
- package/package.json +13 -13
- package/packages/git-bash-mcp/dist/cli.js +81 -19
- package/packages/lsp-core/src/lsp/client-diagnostics-freshness.integration.test.ts +4 -4
- package/packages/lsp-core/src/lsp/directory-diagnostics.test.ts +118 -1
- package/packages/lsp-core/src/lsp/directory-diagnostics.ts +1 -1
- package/packages/lsp-daemon/dist/cli.js +262 -63
- package/packages/lsp-daemon/dist/client.js +194 -50
- package/packages/lsp-daemon/dist/daemon-client.d.ts +2 -2
- package/packages/lsp-daemon/dist/daemon-client.js +26 -2
- package/packages/lsp-daemon/dist/ensure-daemon.d.ts +5 -4
- package/packages/lsp-daemon/dist/ensure-daemon.js +79 -18
- package/packages/lsp-daemon/dist/index.js +262 -63
- package/packages/lsp-daemon/dist/proxy.d.ts +2 -0
- package/packages/lsp-daemon/dist/proxy.js +79 -23
- package/packages/lsp-tools-mcp/dist/cli.js +82 -20
- package/packages/lsp-tools-mcp/dist/mcp.js +82 -20
- package/packages/lsp-tools-mcp/dist/tools.js +1 -1
- package/packages/omo-codex/plugin/.codex-plugin/plugin.json +1 -1
- package/packages/omo-codex/plugin/.mcp.json +2 -1
- package/packages/omo-codex/plugin/components/bootstrap/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/bootstrap/package.json +1 -1
- package/packages/omo-codex/plugin/components/codegraph/dist/cli.js +100 -28
- package/packages/omo-codex/plugin/components/codegraph/dist/serve.js +100 -28
- package/packages/omo-codex/plugin/components/codegraph/package.json +1 -1
- package/packages/omo-codex/plugin/components/codegraph/src/mcp-bridge.ts +21 -9
- package/packages/omo-codex/plugin/components/codegraph/test/mcp-bridge-fixtures.ts +35 -0
- package/packages/omo-codex/plugin/components/codegraph/test/serve-mcp-bridge-lifecycle.test.ts +69 -0
- package/packages/omo-codex/plugin/components/codegraph/test/serve-mcp-bridge.test.ts +57 -1
- package/packages/omo-codex/plugin/components/comment-checker/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/comment-checker/package.json +1 -1
- package/packages/omo-codex/plugin/components/git-bash/hooks/hooks.json +2 -2
- package/packages/omo-codex/plugin/components/git-bash/package.json +1 -1
- package/packages/omo-codex/plugin/components/lazycodex-executor-verify/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/lazycodex-executor-verify/package.json +1 -1
- package/packages/omo-codex/plugin/components/lsp/.mcp.json +2 -1
- package/packages/omo-codex/plugin/components/lsp/dist/.omo-runtime-manifest.json +3 -3
- package/packages/omo-codex/plugin/components/lsp/dist/cli.js +113 -31
- package/packages/omo-codex/plugin/components/lsp/hooks/hooks.json +2 -2
- package/packages/omo-codex/plugin/components/lsp/package.json +1 -1
- package/packages/omo-codex/plugin/components/lsp/test/package-smoke.test.ts +1 -0
- package/packages/omo-codex/plugin/components/rules/bundled-rules/hephaestus/gpt-5.5.md +1 -1
- package/packages/omo-codex/plugin/components/rules/bundled-rules/hephaestus/gpt-5.6.md +5 -3
- package/packages/omo-codex/plugin/components/rules/hooks/hooks.json +4 -4
- package/packages/omo-codex/plugin/components/rules/package.json +1 -1
- package/packages/omo-codex/plugin/components/start-work-continuation/README.md +2 -2
- package/packages/omo-codex/plugin/components/start-work-continuation/directive.md +3 -3
- package/packages/omo-codex/plugin/components/start-work-continuation/dist/cli.js +2 -2
- package/packages/omo-codex/plugin/components/start-work-continuation/hooks/hooks.json +2 -2
- package/packages/omo-codex/plugin/components/start-work-continuation/package.json +1 -1
- package/packages/omo-codex/plugin/components/start-work-continuation/src/boulder-reader.ts +1 -1
- package/packages/omo-codex/plugin/components/start-work-continuation/src/codex-hook.ts +1 -1
- package/packages/omo-codex/plugin/components/start-work-continuation/test/boulder-reader.test.ts +15 -1
- package/packages/omo-codex/plugin/components/start-work-continuation/test/codex-hook.test.ts +20 -0
- package/packages/omo-codex/plugin/components/teammode/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/teammode/package.json +1 -1
- package/packages/omo-codex/plugin/components/telemetry/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/telemetry/package.json +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/directive.md +50 -33
- package/packages/omo-codex/plugin/components/ultrawork/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/package.json +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/skills/ultrawork/SKILL.md +50 -33
- package/packages/omo-codex/plugin/components/ulw-loop/directive.md +50 -33
- package/packages/omo-codex/plugin/components/ulw-loop/dist/cli.js +3 -3
- package/packages/omo-codex/plugin/components/ulw-loop/dist/stop-resume-hook.js +5 -6
- package/packages/omo-codex/plugin/components/ulw-loop/hooks/hooks.json +4 -4
- package/packages/omo-codex/plugin/components/ulw-loop/package.json +1 -1
- package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/references/full-workflow.md +1 -1
- package/packages/omo-codex/plugin/components/ulw-loop/src/stop-resume-hook.ts +5 -6
- package/packages/omo-codex/plugin/components/ulw-loop/test/stop-resume-hook.test.ts +2 -2
- package/packages/omo-codex/plugin/hooks/post-compact-resetting-git-bash-mcp-reminder.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-compact-resetting-lsp-diagnostics-cache.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-compact-resetting-project-rule-cache.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-codegraph-init-guidance.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-comments.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-lsp-diagnostics.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-thread-title-hygiene.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-matching-project-rules.json +1 -1
- package/packages/omo-codex/plugin/hooks/pre-tool-use-enforcing-unlimited-goal-budget.json +1 -1
- package/packages/omo-codex/plugin/hooks/pre-tool-use-guarding-ulw-loop-spawns.json +1 -1
- package/packages/omo-codex/plugin/hooks/pre-tool-use-recommending-git-bash-mcp.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-checking-auto-update.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-checking-bootstrap-provisioning.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-checking-codegraph-bootstrap.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-loading-project-rules.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-recording-session-telemetry.json +1 -1
- package/packages/omo-codex/plugin/hooks/stop-checking-start-work-continuation.json +1 -1
- package/packages/omo-codex/plugin/hooks/stop-checking-ulw-loop-resume.json +1 -1
- package/packages/omo-codex/plugin/hooks/subagent-stop-checking-start-work-continuation.json +1 -1
- package/packages/omo-codex/plugin/hooks/subagent-stop-verifying-lazycodex-executor-evidence.json +1 -1
- package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ultrawork-trigger.json +1 -1
- package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ulw-loop-steering.json +1 -1
- package/packages/omo-codex/plugin/hooks/user-prompt-submit-loading-project-rules.json +1 -1
- package/packages/omo-codex/plugin/package-lock.json +13 -13
- package/packages/omo-codex/plugin/package.json +1 -1
- package/packages/omo-codex/plugin/scripts/sync-skills.mjs +10 -2
- package/packages/omo-codex/plugin/skills/review-work/SKILL.md +7 -0
- package/packages/omo-codex/plugin/skills/start-work/SKILL.md +3 -2
- package/packages/omo-codex/plugin/skills/ultrawork/SKILL.md +50 -33
- package/packages/omo-codex/plugin/skills/ulw-loop/references/full-workflow.md +1 -1
- package/packages/omo-codex/plugin/test/mcp-research-servers.test.mjs +1 -0
- package/packages/omo-codex/plugin/test/sync-skills-orchestration.test.mjs +7 -0
- package/packages/omo-codex/plugin/test/sync-skills-test-support.mjs +35 -4
- package/packages/omo-codex/scripts/install-dist/install-local.mjs +1 -1
|
@@ -13,13 +13,13 @@ import { homedir as homedir3 } from "node:os";
|
|
|
13
13
|
import { join as join11, resolve as resolve11 } from "node:path";
|
|
14
14
|
|
|
15
15
|
// ../../../../lsp-daemon/dist/client.js
|
|
16
|
-
import { connect
|
|
16
|
+
import { connect } from "node:net";
|
|
17
17
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
18
18
|
import { existsSync, realpathSync, statSync } from "node:fs";
|
|
19
19
|
import { basename, delimiter, dirname, isAbsolute, join, relative, resolve } from "node:path";
|
|
20
20
|
import { spawn } from "node:child_process";
|
|
21
21
|
import { closeSync as closeSync2, mkdirSync as mkdirSync2, openSync as openSync2 } from "node:fs";
|
|
22
|
-
import {
|
|
22
|
+
import { Socket } from "node:net";
|
|
23
23
|
import { dirname as dirname3 } from "node:path";
|
|
24
24
|
import { execPath } from "node:process";
|
|
25
25
|
import {
|
|
@@ -426,42 +426,48 @@ class DaemonUnreachableError extends Error {
|
|
|
426
426
|
async function ensureDaemonRunning(paths, deps = defaultEnsureDaemonDeps(), options = {}) {
|
|
427
427
|
const readyTimeoutMs = options.readyTimeoutMs ?? DEFAULT_READY_TIMEOUT_MS;
|
|
428
428
|
const pollIntervalMs = options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
|
|
429
|
-
|
|
429
|
+
const signal = options.signal;
|
|
430
|
+
throwIfAborted(signal);
|
|
431
|
+
if (await awaitWithSignal(deps.probe(paths, signal), signal))
|
|
430
432
|
return;
|
|
433
|
+
throwIfAborted(signal);
|
|
431
434
|
deps.spawnDaemon(paths);
|
|
432
|
-
await waitUntilReachable(paths, deps, readyTimeoutMs, pollIntervalMs);
|
|
435
|
+
await waitUntilReachable(paths, deps, readyTimeoutMs, pollIntervalMs, signal);
|
|
433
436
|
}
|
|
434
|
-
async function waitUntilReachable(paths, deps, readyTimeoutMs, pollIntervalMs) {
|
|
437
|
+
async function waitUntilReachable(paths, deps, readyTimeoutMs, pollIntervalMs, signal) {
|
|
435
438
|
const deadline = deps.now() + readyTimeoutMs;
|
|
436
439
|
for (;; ) {
|
|
437
|
-
|
|
440
|
+
throwIfAborted(signal);
|
|
441
|
+
if (await awaitWithSignal(deps.probe(paths, signal), signal))
|
|
438
442
|
return;
|
|
439
443
|
if (deps.now() >= deadline)
|
|
440
444
|
throw new DaemonUnreachableError(paths.socket);
|
|
441
|
-
await deps.sleep(pollIntervalMs);
|
|
445
|
+
await awaitWithSignal(deps.sleep(pollIntervalMs, signal), signal);
|
|
442
446
|
}
|
|
443
447
|
}
|
|
444
|
-
async function probeDaemon(paths, timeoutMs = PROBE_TIMEOUT_MS) {
|
|
448
|
+
async function probeDaemon(paths, timeoutMs = PROBE_TIMEOUT_MS, signal) {
|
|
445
449
|
const token = readAuthToken(paths);
|
|
446
450
|
if (!token)
|
|
447
451
|
return false;
|
|
448
|
-
return await pingDaemon(paths, token, timeoutMs) !== null;
|
|
452
|
+
return await pingDaemon(paths, token, timeoutMs, signal) !== null;
|
|
449
453
|
}
|
|
450
|
-
function pingDaemon(paths, token, timeoutMs = PROBE_TIMEOUT_MS) {
|
|
454
|
+
function pingDaemon(paths, token, timeoutMs = PROBE_TIMEOUT_MS, signal) {
|
|
451
455
|
return new Promise((resolve22) => {
|
|
452
|
-
const socket =
|
|
456
|
+
const socket = new Socket;
|
|
453
457
|
let settled = false;
|
|
458
|
+
let timer;
|
|
454
459
|
const finish = (value) => {
|
|
455
460
|
if (settled)
|
|
456
461
|
return;
|
|
457
462
|
settled = true;
|
|
463
|
+
if (timer !== undefined)
|
|
464
|
+
clearTimeout(timer);
|
|
465
|
+
signal?.removeEventListener("abort", onAbort);
|
|
458
466
|
socket.destroy();
|
|
459
467
|
resolve22(value);
|
|
460
468
|
};
|
|
461
|
-
const
|
|
462
|
-
timer.unref?.();
|
|
469
|
+
const onAbort = () => finish(null);
|
|
463
470
|
const decoder = createLineDecoder((message) => {
|
|
464
|
-
clearTimeout(timer);
|
|
465
471
|
finish(parsePingResponse(message));
|
|
466
472
|
});
|
|
467
473
|
socket.once("connect", () => {
|
|
@@ -469,9 +475,16 @@ function pingDaemon(paths, token, timeoutMs = PROBE_TIMEOUT_MS) {
|
|
|
469
475
|
});
|
|
470
476
|
socket.on("data", (chunk) => decoder.push(chunk));
|
|
471
477
|
socket.once("error", () => {
|
|
472
|
-
clearTimeout(timer);
|
|
473
478
|
finish(null);
|
|
474
479
|
});
|
|
480
|
+
timer = setTimeout(() => finish(null), timeoutMs);
|
|
481
|
+
timer.unref?.();
|
|
482
|
+
if (signal?.aborted) {
|
|
483
|
+
onAbort();
|
|
484
|
+
return;
|
|
485
|
+
}
|
|
486
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
487
|
+
socket.connect(paths.socket);
|
|
475
488
|
});
|
|
476
489
|
}
|
|
477
490
|
function spawnDaemonProcess(paths) {
|
|
@@ -489,14 +502,62 @@ function spawnDaemonProcess(paths) {
|
|
|
489
502
|
}
|
|
490
503
|
function defaultEnsureDaemonDeps() {
|
|
491
504
|
return {
|
|
492
|
-
probe: (paths) => probeDaemon(paths),
|
|
505
|
+
probe: (paths, signal) => probeDaemon(paths, PROBE_TIMEOUT_MS, signal),
|
|
493
506
|
spawnDaemon: (paths) => spawnDaemonProcess(paths),
|
|
494
|
-
sleep: (ms) =>
|
|
495
|
-
setTimeout(resolve22, ms);
|
|
496
|
-
}),
|
|
507
|
+
sleep: (ms, signal) => sleepWithSignal(ms, signal),
|
|
497
508
|
now: () => Date.now()
|
|
498
509
|
};
|
|
499
510
|
}
|
|
511
|
+
function sleepWithSignal(ms, signal) {
|
|
512
|
+
return new Promise((resolve22) => {
|
|
513
|
+
let settled = false;
|
|
514
|
+
const finish = () => {
|
|
515
|
+
if (settled)
|
|
516
|
+
return;
|
|
517
|
+
settled = true;
|
|
518
|
+
clearTimeout(timer);
|
|
519
|
+
signal?.removeEventListener("abort", finish);
|
|
520
|
+
resolve22();
|
|
521
|
+
};
|
|
522
|
+
const timer = setTimeout(finish, ms);
|
|
523
|
+
if (signal?.aborted) {
|
|
524
|
+
finish();
|
|
525
|
+
return;
|
|
526
|
+
}
|
|
527
|
+
signal?.addEventListener("abort", finish, { once: true });
|
|
528
|
+
});
|
|
529
|
+
}
|
|
530
|
+
function awaitWithSignal(promise, signal) {
|
|
531
|
+
if (!signal)
|
|
532
|
+
return promise;
|
|
533
|
+
if (signal.aborted)
|
|
534
|
+
return Promise.reject(abortError(signal));
|
|
535
|
+
return new Promise((resolve22, reject) => {
|
|
536
|
+
let settled = false;
|
|
537
|
+
const finish = (run) => {
|
|
538
|
+
if (settled)
|
|
539
|
+
return;
|
|
540
|
+
settled = true;
|
|
541
|
+
signal.removeEventListener("abort", onAbort);
|
|
542
|
+
run();
|
|
543
|
+
};
|
|
544
|
+
const onAbort = () => finish(() => reject(abortError(signal)));
|
|
545
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
546
|
+
promise.then((value) => finish(() => resolve22(value)), (error) => finish(() => reject(error)));
|
|
547
|
+
});
|
|
548
|
+
}
|
|
549
|
+
function throwIfAborted(signal) {
|
|
550
|
+
if (signal?.aborted)
|
|
551
|
+
throw abortError(signal);
|
|
552
|
+
}
|
|
553
|
+
function abortError(signal) {
|
|
554
|
+
const reason = signal.reason;
|
|
555
|
+
if (reason instanceof Error)
|
|
556
|
+
return reason;
|
|
557
|
+
const error = new Error(typeof reason === "string" ? reason : "daemon startup cancelled");
|
|
558
|
+
error.name = "AbortError";
|
|
559
|
+
return error;
|
|
560
|
+
}
|
|
500
561
|
function parsePingResponse(message) {
|
|
501
562
|
if (!message || typeof message !== "object" || Array.isArray(message))
|
|
502
563
|
return null;
|
|
@@ -692,7 +753,7 @@ class JsonRpcConnection {
|
|
|
692
753
|
settled = true;
|
|
693
754
|
this.pendingRequests.delete(key);
|
|
694
755
|
cleanup();
|
|
695
|
-
const rejectCancelled = () => reject(
|
|
756
|
+
const rejectCancelled = () => reject(abortError2(options.signal));
|
|
696
757
|
if (!requestWritten) {
|
|
697
758
|
cancelAfterWrite = true;
|
|
698
759
|
rejectCancelled();
|
|
@@ -896,7 +957,7 @@ ${body}`;
|
|
|
896
957
|
}
|
|
897
958
|
}
|
|
898
959
|
}
|
|
899
|
-
function
|
|
960
|
+
function abortError2(signal) {
|
|
900
961
|
const reason = signal?.reason;
|
|
901
962
|
if (reason instanceof Error)
|
|
902
963
|
return reason;
|
|
@@ -3118,11 +3179,11 @@ function waitForDiagnosticsActivity(wait, signal) {
|
|
|
3118
3179
|
if (!signal)
|
|
3119
3180
|
return wait;
|
|
3120
3181
|
if (signal.aborted)
|
|
3121
|
-
return Promise.reject(
|
|
3182
|
+
return Promise.reject(abortError3(signal));
|
|
3122
3183
|
return new Promise((resolve72, reject) => {
|
|
3123
3184
|
const onAbort = () => {
|
|
3124
3185
|
signal.removeEventListener("abort", onAbort);
|
|
3125
|
-
reject(
|
|
3186
|
+
reject(abortError3(signal));
|
|
3126
3187
|
};
|
|
3127
3188
|
signal.addEventListener("abort", onAbort, { once: true });
|
|
3128
3189
|
wait.then(() => {
|
|
@@ -3157,7 +3218,7 @@ function preCommitAbortReason(source) {
|
|
|
3157
3218
|
return reason;
|
|
3158
3219
|
return new Error("LSP request cancelled before workspace edit commit");
|
|
3159
3220
|
}
|
|
3160
|
-
function
|
|
3221
|
+
function abortError3(signal) {
|
|
3161
3222
|
const reason = signal.reason;
|
|
3162
3223
|
if (reason instanceof Error)
|
|
3163
3224
|
return reason;
|
|
@@ -3188,7 +3249,7 @@ async function stopClientBestEffort(client) {
|
|
|
3188
3249
|
reportBestEffortCleanupError("client stop", error);
|
|
3189
3250
|
}
|
|
3190
3251
|
}
|
|
3191
|
-
function
|
|
3252
|
+
function awaitWithSignal2(promise, signal) {
|
|
3192
3253
|
if (!signal)
|
|
3193
3254
|
return promise;
|
|
3194
3255
|
return new Promise((resolve72, reject) => {
|
|
@@ -3291,7 +3352,7 @@ class LspManager {
|
|
|
3291
3352
|
if (managed.initPromise) {
|
|
3292
3353
|
managed.pendingWaiters++;
|
|
3293
3354
|
try {
|
|
3294
|
-
await
|
|
3355
|
+
await awaitWithSignal2(managed.initPromise, signal);
|
|
3295
3356
|
} catch (err) {
|
|
3296
3357
|
managed.pendingWaiters--;
|
|
3297
3358
|
await this.tryDeleteIfOrphaned(key, managed);
|
|
@@ -3329,7 +3390,7 @@ class LspManager {
|
|
|
3329
3390
|
};
|
|
3330
3391
|
this.clients.set(key, newManaged);
|
|
3331
3392
|
try {
|
|
3332
|
-
await
|
|
3393
|
+
await awaitWithSignal2(initPromise, signal);
|
|
3333
3394
|
} catch (err) {
|
|
3334
3395
|
newManaged.pendingWaiters--;
|
|
3335
3396
|
if (this.clients.get(key) === newManaged) {
|
|
@@ -4280,7 +4341,7 @@ async function aggregateDiagnosticsForDirectory(directory, extension, severity,
|
|
|
4280
4341
|
const fileErrors = [];
|
|
4281
4342
|
const maxConcurrency = Math.max(1, options.maxConcurrency ?? DIRECTORY_DIAGNOSTICS_MAX_CONCURRENCY);
|
|
4282
4343
|
options.signal?.throwIfAborted();
|
|
4283
|
-
const client = await manager.getClient(root, server2);
|
|
4344
|
+
const client = await manager.getClient(root, server2, options.signal);
|
|
4284
4345
|
try {
|
|
4285
4346
|
let nextIndex = 0;
|
|
4286
4347
|
const workers = Array.from({ length: Math.min(maxConcurrency, filesToProcess.length) }, async () => {
|
|
@@ -4960,14 +5021,14 @@ class DaemonRequestCancelledError extends DaemonRequestError {
|
|
|
4960
5021
|
async function callToolViaDaemon(name, args, options) {
|
|
4961
5022
|
const context = requireContext(options.context);
|
|
4962
5023
|
const paths = options.paths ?? daemonPaths();
|
|
4963
|
-
const ensure = options.ensure ?? ensureDaemonRunning;
|
|
5024
|
+
const ensure = options.ensure ?? ((ensurePaths, signal) => ensureDaemonRunning(ensurePaths, undefined, signal === undefined ? {} : { signal }));
|
|
4964
5025
|
const timeoutMs = options.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
|
|
4965
5026
|
const requestArgs = withContext(args, context);
|
|
4966
5027
|
let lastError;
|
|
4967
5028
|
let authRefreshUsed = false;
|
|
4968
5029
|
for (let attempt = 0;attempt < 3; attempt += 1) {
|
|
4969
5030
|
try {
|
|
4970
|
-
await
|
|
5031
|
+
await ensureDaemonAvailable(paths, ensure, options.signal);
|
|
4971
5032
|
const token = readAuthToken(paths);
|
|
4972
5033
|
if (!token)
|
|
4973
5034
|
throw new DaemonRequestError("daemon auth token missing", false);
|
|
@@ -4998,6 +5059,27 @@ function requireContext(context) {
|
|
|
4998
5059
|
function withContext(args, context) {
|
|
4999
5060
|
return { ...args, [CONTEXT_KEY]: context };
|
|
5000
5061
|
}
|
|
5062
|
+
function ensureDaemonAvailable(paths, ensure, signal) {
|
|
5063
|
+
if (!signal)
|
|
5064
|
+
return ensure(paths);
|
|
5065
|
+
return new Promise((resolve9, reject) => {
|
|
5066
|
+
let settled = false;
|
|
5067
|
+
const finish = (run) => {
|
|
5068
|
+
if (settled)
|
|
5069
|
+
return;
|
|
5070
|
+
settled = true;
|
|
5071
|
+
signal.removeEventListener("abort", onAbort);
|
|
5072
|
+
run();
|
|
5073
|
+
};
|
|
5074
|
+
const onAbort = () => finish(() => reject(new DaemonRequestCancelledError(false)));
|
|
5075
|
+
if (signal.aborted) {
|
|
5076
|
+
onAbort();
|
|
5077
|
+
return;
|
|
5078
|
+
}
|
|
5079
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
5080
|
+
Promise.resolve().then(() => ensure(paths, signal)).then(() => finish(() => resolve9()), (error) => finish(() => reject(signal.aborted ? new DaemonRequestCancelledError(false) : error)));
|
|
5081
|
+
});
|
|
5082
|
+
}
|
|
5001
5083
|
function daemonUnreachableResult(paths, error) {
|
|
5002
5084
|
const text2 = [
|
|
5003
5085
|
`LSP daemon unreachable: ${errorText(error)}.`,
|
|
@@ -5011,7 +5093,7 @@ function daemonUnreachableResult(paths, error) {
|
|
|
5011
5093
|
}
|
|
5012
5094
|
function sendToolCall(paths, token, name, args, options) {
|
|
5013
5095
|
return new Promise((resolve9, reject) => {
|
|
5014
|
-
const socket =
|
|
5096
|
+
const socket = connect(paths.socket);
|
|
5015
5097
|
const requestId = allocateProxyRequestId();
|
|
5016
5098
|
let settled = false;
|
|
5017
5099
|
let requestWritten = false;
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
"type": "command",
|
|
9
9
|
"command": "node \"${PLUGIN_ROOT}/dist/cli.js\" hook post-tool-use",
|
|
10
10
|
"timeout": 60,
|
|
11
|
-
"statusMessage": "(OmO 4.18.
|
|
11
|
+
"statusMessage": "(OmO 4.18.2) Checking LSP Diagnostics"
|
|
12
12
|
}
|
|
13
13
|
]
|
|
14
14
|
}
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"type": "command",
|
|
22
22
|
"command": "node \"${PLUGIN_ROOT}/dist/cli.js\" hook post-compact",
|
|
23
23
|
"timeout": 5,
|
|
24
|
-
"statusMessage": "(OmO 4.18.
|
|
24
|
+
"statusMessage": "(OmO 4.18.2) Resetting LSP Diagnostics Cache"
|
|
25
25
|
}
|
|
26
26
|
]
|
|
27
27
|
}
|
|
@@ -44,6 +44,7 @@ describe("plugin package metadata", () => {
|
|
|
44
44
|
expect(postCompactCommand).toBe(`node "${pluginRoot}/dist/cli.js" hook post-compact`);
|
|
45
45
|
expect(lspServer?.command).toBe("node");
|
|
46
46
|
expect(lspServer?.args).toEqual(["../../../../lsp-daemon/dist/cli.js", "mcp"]);
|
|
47
|
+
expect(lspServer?.startup_timeout_sec).toBe(10);
|
|
47
48
|
expect(cliSource).not.toContain("./lazy-lsp-mcp.js");
|
|
48
49
|
expect(cliSource).toContain("resolveLspDaemonCliPath");
|
|
49
50
|
expect(daemonCliPathSource).toContain("@code-yeongyu/lsp-daemon/cli");
|
|
@@ -74,7 +74,7 @@ Diagnostics catch type errors, not logic bugs; tests cover only what their autho
|
|
|
74
74
|
|
|
75
75
|
# Global Review and Debugging Gate
|
|
76
76
|
|
|
77
|
-
Run `review-work` plus a `debugging` runtime audit only before a PR handoff or when the user asks for a review; lane pass/fail semantics live in those skills.
|
|
77
|
+
Run `review-work` plus a `debugging` runtime audit only before a PR handoff or when the user asks for a review; lane pass/fail semantics live in those skills. Each passing review lane and debugging audit binds to the exact full commit SHA it reviewed. Immediately append a durable task-evidence/ledger record with its name, full SHA, verdict, and report artifact/source. Before reuse after continuation or compaction, re-read the record and require the exact lane/SHA pair; memory or an unstamped report is not coverage. Every missing pair at the current SHA still runs, and new commits require fresh applicable coverage. For everything else, the gate above is the whole gate: once you have personally observed the artifact working, report your evidence. Redact secrets, tokens, and PII from ledgers, PR bodies, and handoffs.
|
|
78
78
|
|
|
79
79
|
# Failure Recovery
|
|
80
80
|
|
|
@@ -19,13 +19,15 @@ Status requests are not stop signals: give the update, keep working. Honor every
|
|
|
19
19
|
|
|
20
20
|
# Discovery
|
|
21
21
|
|
|
22
|
-
Never speculate about code you have not read: verify with tools and re-read on every hand-off.
|
|
22
|
+
Never speculate about code you have not read: verify with tools and re-read on every hand-off. **USE CODE MODE AGGRESSIVELY FOR BOUNDED DISCOVERY.** When multiple independent tool calls produce results that can be materially filtered, joined, deduplicated, or reduced, make ONE `exec` / eval JavaScript program that calls eligible tools concurrently with `Promise.all` and returns only decision-relevant evidence. For shell-native repo work without programmatic tool access, use ONE Python script with `concurrent.futures`, `subprocess`, and utility functions to batch commands and reduce output. **USE LSP FOR SYMBOLS** - definitions, references, rename impact, workspace symbols, and diagnostics - instead of reconstructing semantics with text search. Use direct calls when one result chooses the next action, outputs are already small, semantic judgment is required between calls, approval or side effects are involved, or native artifacts must be preserved. Retrieve again only for a missing required fact or a second-order question that changes the design. Stop when you can act; prefer the root fix over the symptom.
|
|
23
23
|
|
|
24
24
|
# Operating Loop
|
|
25
25
|
|
|
26
26
|
Explore -> Plan (`update_plan`, per Task Tracking) -> Implement -> Verify -> Manually QA.
|
|
27
27
|
|
|
28
|
-
Implement surgically, matching codebase style (naming, indentation, imports, error handling) even when you would write it differently. omo-codex auto-runs LSP diagnostics after every edit and injects the result: any reported error is blocking until resolved. Verify with targeted tests and builds for changed behavior; if validation cannot run, say why and name the next best check.
|
|
28
|
+
Implement surgically, matching codebase style (naming, indentation, imports, error handling) even when you would write it differently. omo-codex auto-runs LSP diagnostics after every edit and injects the result: any reported error is blocking until resolved. Verify with targeted tests and builds for changed behavior; if validation cannot run, say why and name the next best check. Re-run a validation command only when its inputs changed since its last green run; one full pass right before the final message replaces repeated identical reruns.
|
|
29
|
+
|
|
30
|
+
Waiting is not free: a status poll replays the whole accumulated context through the model. Run a long command (install, build, suite, container, CI watch) to completion in ONE `exec` call with a timeout sized to the expected wait - or send output to a log file read once on a completion signal - never re-poll the same surface with empty reads or sub-minute waits. If two consecutive checks show no state change, double the wait or switch to a completion signal.
|
|
29
31
|
|
|
30
32
|
# Subagents
|
|
31
33
|
|
|
@@ -52,7 +54,7 @@ Diagnostics catch type errors, not logic bugs; tests cover only what their autho
|
|
|
52
54
|
|
|
53
55
|
"This should work" from reading source does not pass. A defect found in usage is yours to fix this turn.
|
|
54
56
|
|
|
55
|
-
Run `review-work` plus a `debugging` runtime audit only before a PR handoff or when the user asks for a review; lane pass/fail semantics live in those skills.
|
|
57
|
+
Run `review-work` plus a `debugging` runtime audit only before a PR handoff or when the user asks for a review; lane pass/fail semantics live in those skills. Each passing review lane and debugging audit binds to the exact full commit SHA it reviewed. Immediately append a durable task-evidence/ledger record with its name, full SHA, verdict, and report artifact/source. Before reuse after continuation or compaction, re-read the record and require the exact lane/SHA pair; memory or an unstamped report is not coverage. Every missing pair at the current SHA still runs, and new commits require fresh applicable coverage. For everything else, the gate above is the whole gate: once you have personally observed the artifact working, report your evidence. Redact secrets, tokens, and PII from ledgers, PR bodies, and handoffs.
|
|
56
58
|
|
|
57
59
|
# Failure Recovery
|
|
58
60
|
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
"type": "command",
|
|
8
8
|
"command": "node \"${PLUGIN_ROOT}/dist/cli.js\" hook session-start",
|
|
9
9
|
"timeout": 10,
|
|
10
|
-
"statusMessage": "(OmO 4.18.
|
|
10
|
+
"statusMessage": "(OmO 4.18.2) Loading Project Rules"
|
|
11
11
|
}
|
|
12
12
|
]
|
|
13
13
|
}
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"type": "command",
|
|
20
20
|
"command": "node \"${PLUGIN_ROOT}/dist/cli.js\" hook user-prompt-submit",
|
|
21
21
|
"timeout": 10,
|
|
22
|
-
"statusMessage": "(OmO 4.18.
|
|
22
|
+
"statusMessage": "(OmO 4.18.2) Loading Project Rules"
|
|
23
23
|
}
|
|
24
24
|
]
|
|
25
25
|
}
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"type": "command",
|
|
33
33
|
"command": "node \"${PLUGIN_ROOT}/dist/cli.js\" hook post-tool-use",
|
|
34
34
|
"timeout": 10,
|
|
35
|
-
"statusMessage": "(OmO 4.18.
|
|
35
|
+
"statusMessage": "(OmO 4.18.2) Matching Project Rules"
|
|
36
36
|
}
|
|
37
37
|
]
|
|
38
38
|
}
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"type": "command",
|
|
46
46
|
"command": "node \"${PLUGIN_ROOT}/dist/cli.js\" hook post-compact",
|
|
47
47
|
"timeout": 10,
|
|
48
|
-
"statusMessage": "(OmO 4.18.
|
|
48
|
+
"statusMessage": "(OmO 4.18.2) Resetting Project Rule Cache"
|
|
49
49
|
}
|
|
50
50
|
]
|
|
51
51
|
}
|
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
Codex Stop-hook continuation injector for the omo-codex `start-work` skill.
|
|
4
4
|
|
|
5
|
-
It reads `.omo/boulder.json` in the hook payload `cwd`, resolves the active work, inspects the active plan
|
|
5
|
+
It reads `.omo/boulder.json` in the hook payload `cwd`, resolves the active work, inspects the active plan's top-level checklist, and emits Codex Stop-hook JSON while the plan still has unchecked tasks or its final review/debugging gate remains pending:
|
|
6
6
|
|
|
7
7
|
```json
|
|
8
8
|
{"decision":"block","reason":"<directive>"}
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
The `reason` is loaded from `directive.md` on every invocation and filled with current plan state. The hook returns no output when `stop_hook_active` is `true`, when no active Boulder work exists, when the work is completed, when the active work is not tied to `codex:<session_id>`, or when
|
|
11
|
+
The `reason` is loaded from `directive.md` on every invocation and filled with current plan state. The hook returns no output when `stop_hook_active` is `true`, when no active Boulder work exists, when the work is completed, when the active work is not tied to `codex:<session_id>`, or when the plan has no readable top-level checklist. An active plan whose checklist is fully checked still blocks Stop until the final gate runs and Boulder is marked completed.
|
|
12
12
|
|
|
13
13
|
This pairs with the `start-work` skill at `plugin/skills/start-work/SKILL.md`. That skill writes `.omo/boulder.json` with Codex session ids prefixed as `codex:` so the hook can continue only its own active Codex session.
|
|
14
14
|
|
|
@@ -16,7 +16,7 @@ You are mid-flight on a Prometheus work plan; this turn is an automatic continua
|
|
|
16
16
|
# What to do this turn
|
|
17
17
|
|
|
18
18
|
1. Read `{{PLAN_PATH}}` AND `{{LEDGER_PATH}}` first — they are the only sources of truth for what remains and what evidence exists; do not trust your memory of prior turns.
|
|
19
|
-
2.
|
|
19
|
+
2. When the remaining count is `0`, skip checkbox execution and perform the Final gate now. Otherwise, pick the FIRST unchecked top-level checkbox in `## TODOs` or `## Final Verification Wave`. Ignore nested checkboxes under Acceptance Criteria / Evidence / Definition of Done.
|
|
20
20
|
3. Follow the `start-work` skill in full. The skill is already loaded from your earlier turn — re-read its file at `packages/omo-codex/plugin/skills/start-work/SKILL.md` if you have lost context.
|
|
21
21
|
4. Apply the checkbox's tier from its ledger entry, or classify it now per the start-work skill: LIGHT (default — a narrow change inside existing layers) needs one real-surface proof of the deliverable, with auxiliary surfaces first-class for CLI- or data-shaped work, and only trigger-mapped adversarial classes; HEAVY (new module/abstraction, auth/security, external integration, schema/migration, concurrency, cross-domain refactor, care signals) takes the full per-criterion regime. When unsure, take HEAVY; never downgrade.
|
|
22
22
|
5. Decompose the checkbox into atomic sub-tasks. Dispatch them in PARALLEL via `multi_agent_v1.spawn_agent` calls in this same response unless a sub-task has a NAMED blocking dependency (input from another sub-task or shared file). Use `fork_context: false` unless full history is truly required. Put role and specialty instructions inside `message`. Flat `spawn_agent` requiring `task_name` instead (`multi_agent_v2`)? Add `"task_name"`, use `fork_turns: "none"`, `wait_agent` takes only `timeout_ms`, finished agents end on their own.
|
|
@@ -40,7 +40,7 @@ You are mid-flight on a Prometheus work plan; this turn is an automatic continua
|
|
|
40
40
|
|
|
41
41
|
# Final gate
|
|
42
42
|
|
|
43
|
-
Before completion,
|
|
43
|
+
Before completion, satisfy all five `review-work` lanes and a `debugging` runtime audit; inconclusive lanes or an inconclusive audit fail. Each pass binds to the exact full commit SHA it reviewed. Immediately append a durable task-evidence record to `{{LEDGER_PATH}}` for every passing lane and the debugging audit with its name, full SHA, verdict, and report artifact/source. Before same-SHA reuse after any continuation or compaction, re-read the ledger record and verify the exact lane/SHA pair; memory, chat history, or an unstamped report is not coverage. New commits require fresh applicable coverage. Do not create a PR, PR handoff, branch handoff, merge, or final completion answer until this gate passes. For PR/branch work, stay in the task-owned worktree: create/update the PR, wait for CI/review/Cubic gates, merge by default unless explicitly opted out, then clean up. After the gate and lifecycle pass, mark the Boulder work completed before the final answer. Redact secrets, tokens, credentials, auth headers, cookies, env dumps, logs, and PII.
|
|
44
44
|
|
|
45
45
|
# Stop conditions for THIS turn
|
|
46
46
|
|
|
@@ -54,6 +54,6 @@ Before completion, run `review-work` and a `debugging` runtime audit; inconclusi
|
|
|
54
54
|
- Surface only state changes: sub-agent dispatched, scenario PASS/FAIL with artifact path, checkbox marked, evidence appended.
|
|
55
55
|
- Do NOT print "Should I continue?", restate the plan, or recap prior turns — the Stop hook continues you; the ledger and plan are the durable record.
|
|
56
56
|
|
|
57
|
-
Begin now.
|
|
57
|
+
Begin now. If no checkbox remains, run the Final gate; otherwise pick the next checkbox, dispatch the parallel sub-agents, verify, mark, continue.
|
|
58
58
|
|
|
59
59
|
</start-work-continuation>
|
|
@@ -21,7 +21,7 @@ function readContinuationState(cwd, sessionId) {
|
|
|
21
21
|
return null;
|
|
22
22
|
const planPath = resolveBoulderPlanPathForWork(cwd, work);
|
|
23
23
|
const checklist = getPlanChecklist(planPath);
|
|
24
|
-
if (checklist.
|
|
24
|
+
if (checklist.total === 0)
|
|
25
25
|
return null;
|
|
26
26
|
return {
|
|
27
27
|
planName: work.planName,
|
|
@@ -239,7 +239,7 @@ function renderDirective(state, sessionId) {
|
|
|
239
239
|
BOULDER_PATH: state.boulderPath,
|
|
240
240
|
REMAINING_COUNT: String(state.checklist.remaining),
|
|
241
241
|
TOTAL_COUNT: String(state.checklist.total),
|
|
242
|
-
NEXT_TASK_LABEL: state.checklist.nextTaskLabel ?? "",
|
|
242
|
+
NEXT_TASK_LABEL: state.checklist.nextTaskLabel ?? "none (final gate pending)",
|
|
243
243
|
WORKTREE_BLOCK: worktreeBlock,
|
|
244
244
|
LEDGER_PATH: state.ledgerPath,
|
|
245
245
|
SESSION_ID: sessionId
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
"type": "command",
|
|
8
8
|
"command": "node \"${PLUGIN_ROOT}/components/start-work-continuation/dist/cli.js\" hook stop",
|
|
9
9
|
"timeout": 10,
|
|
10
|
-
"statusMessage": "(OmO 4.18.
|
|
10
|
+
"statusMessage": "(OmO 4.18.2) Checking Start-Work Continuation"
|
|
11
11
|
}
|
|
12
12
|
]
|
|
13
13
|
}
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"type": "command",
|
|
20
20
|
"command": "node \"${PLUGIN_ROOT}/components/start-work-continuation/dist/cli.js\" hook subagent-stop",
|
|
21
21
|
"timeout": 10,
|
|
22
|
-
"statusMessage": "(OmO 4.18.
|
|
22
|
+
"statusMessage": "(OmO 4.18.2) Checking Start-Work Continuation"
|
|
23
23
|
}
|
|
24
24
|
]
|
|
25
25
|
}
|
|
@@ -50,7 +50,7 @@ export function readContinuationState(cwd: string, sessionId: string): Continuat
|
|
|
50
50
|
|
|
51
51
|
const planPath = resolveBoulderPlanPathForWork(cwd, work);
|
|
52
52
|
const checklist = getPlanChecklist(planPath);
|
|
53
|
-
if (checklist.
|
|
53
|
+
if (checklist.total === 0) return null;
|
|
54
54
|
|
|
55
55
|
return {
|
|
56
56
|
planName: work.planName,
|
|
@@ -27,7 +27,7 @@ function renderDirective(state: ContinuationState, sessionId: string): string {
|
|
|
27
27
|
BOULDER_PATH: state.boulderPath,
|
|
28
28
|
REMAINING_COUNT: String(state.checklist.remaining),
|
|
29
29
|
TOTAL_COUNT: String(state.checklist.total),
|
|
30
|
-
NEXT_TASK_LABEL: state.checklist.nextTaskLabel ?? "",
|
|
30
|
+
NEXT_TASK_LABEL: state.checklist.nextTaskLabel ?? "none (final gate pending)",
|
|
31
31
|
WORKTREE_BLOCK: worktreeBlock,
|
|
32
32
|
LEDGER_PATH: state.ledgerPath,
|
|
33
33
|
SESSION_ID: sessionId,
|
package/packages/omo-codex/plugin/components/start-work-continuation/test/boulder-reader.test.ts
CHANGED
|
@@ -123,7 +123,7 @@ describe("start-work boulder state reader", () => {
|
|
|
123
123
|
expect(state?.checklist).toEqual({ completed: 0, remaining: 1, total: 1, nextTaskLabel: "First" });
|
|
124
124
|
});
|
|
125
125
|
|
|
126
|
-
it("#given no remaining
|
|
126
|
+
it("#given active codex work with no remaining checklist items #when state is read #then final gate continuation remains present", () => {
|
|
127
127
|
// given
|
|
128
128
|
const workspace = createWorkspace({
|
|
129
129
|
boulderJson: createBoulderJson({ status: "active", sessionIds: ["codex:sess_abc"] }),
|
|
@@ -133,6 +133,20 @@ describe("start-work boulder state reader", () => {
|
|
|
133
133
|
// when
|
|
134
134
|
const state = readContinuationState(workspace, "sess_abc");
|
|
135
135
|
|
|
136
|
+
// then
|
|
137
|
+
expect(state?.checklist).toEqual({ completed: 1, remaining: 0, total: 1, nextTaskLabel: null });
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it("#given active codex work with no readable checklist #when state is read #then continuation remains absent", () => {
|
|
141
|
+
// given
|
|
142
|
+
const workspace = createWorkspace({
|
|
143
|
+
boulderJson: createBoulderJson({ status: "active", sessionIds: ["codex:sess_abc"] }),
|
|
144
|
+
planMarkdown: "# Plan\n\nNo checklist yet.\n",
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
// when
|
|
148
|
+
const state = readContinuationState(workspace, "sess_abc");
|
|
149
|
+
|
|
136
150
|
// then
|
|
137
151
|
expect(state).toBeNull();
|
|
138
152
|
});
|
package/packages/omo-codex/plugin/components/start-work-continuation/test/codex-hook.test.ts
CHANGED
|
@@ -69,6 +69,26 @@ describe("start-work Stop hook", () => {
|
|
|
69
69
|
expect(parsed.reason).toContain("- Your session id in boulder.json: `codex:sess_abc`");
|
|
70
70
|
});
|
|
71
71
|
|
|
72
|
+
it("#given active codex work with zero remaining tasks #when hook runs #then blocks for the final gate", () => {
|
|
73
|
+
// given
|
|
74
|
+
const workspace = createWorkspace({
|
|
75
|
+
boulderJson: createBoulderJson({ sessionIds: ["codex:sess_abc"], status: "active" }),
|
|
76
|
+
planMarkdown: ["# Plan", "", "## TODOs", "- [x] Done"].join("\n"),
|
|
77
|
+
});
|
|
78
|
+
const fs = createMemoryFs();
|
|
79
|
+
|
|
80
|
+
// when
|
|
81
|
+
const output = runStopHook(createStopInput(workspace), fs);
|
|
82
|
+
|
|
83
|
+
// then
|
|
84
|
+
const parsed = parseBlockOutput(output);
|
|
85
|
+
expect(parsed.decision).toBe("block");
|
|
86
|
+
expect(parsed.reason).toContain("- Remaining top-level checkboxes: `0` of `1`");
|
|
87
|
+
expect(parsed.reason).toContain("- Next incomplete task: `none (final gate pending)`");
|
|
88
|
+
expect(parsed.reason).toContain("When the remaining count is `0`, skip checkbox execution");
|
|
89
|
+
expect(parsed.reason).toContain("re-read the ledger record and verify the exact lane/SHA pair");
|
|
90
|
+
});
|
|
91
|
+
|
|
72
92
|
it("#given context-window pressure in transcript #when hook runs #then it does not inject continuation text", () => {
|
|
73
93
|
// given
|
|
74
94
|
const transcriptPath = "/repo/transcript.jsonl";
|