import assert from "node:assert/strict"; import { execFileSync, spawn, spawnSync } from "node:child_process"; import { existsSync, readFileSync, realpathSync, statSync } from "node:fs"; import { chmod, appendFile, mkdir, mkdtemp, readFile, readdir, rm, symlink, utimes, writeFile, } from "node:fs/promises"; import { tmpdir } from "node:os"; import { dirname, join, resolve } from "node:path"; import { pathToFileURL } from "node:url"; import { afterEach, beforeEach, describe, it } from "node:test"; import { buildManagedCodexHooksConfig } from "../../config/codex-hooks.js"; import { DOCUMENT_REFRESH_EXEMPTION_PREFIX } from "../../document-refresh/enforcer.js"; import { initTeamState, readTeamConfig, readTeamLeaderAttention, readTeamPhase, saveTeamConfig, writeTeamLeaderAttention, writeWorkerIdentity, } from "../../team/state.js"; import { registerTeamNotice } from "../../team/notice-ledger.js"; import { dispatchCodexNativeHook, isSloppyFallbackTranscriptStartUsable, readUnambiguousSessionStartNativeId, resolvePersistedReopenRootContext, isCodexNativeHookMainModule, looksLikeGoalCompletionPrompt, mapCodexHookEventToOmxEvent, resolveSessionOwnerPidFromAncestry, } from "../codex-native-hook.js"; import { closeLaunchSessionBindingOnce, establishLaunchSessionBinding, finalizeBoundOnce, updateDetachedSessionMetadata, writeNativeSessionOwner, writeSessionStart, } from "../../hooks/session.js"; import { neutralizeOwnedRoutingRalplan } from '../../ralplan/documented-leader-preflight.js'; import { resetTriageConfigCache } from "../../hooks/triage-config.js"; import { executeStateOperation } from "../../state/operations.js"; import { HUD_TMUX_HEIGHT_LINES } from "../../hud/constants.js"; import { OMX_TMUX_HUD_OWNER_ENV } from "../../hud/reconcile.js"; import { OMX_TMUX_HUD_LEADER_PANE_ENV } from "../../hud/tmux.js"; import { readAllState } from "../../hud/state.js"; import { renderHud } from "../../hud/render.js"; import { getLegacyWikiDir, serializePage, writePage, } from "../../wiki/storage.js"; import { WIKI_SCHEMA_VERSION } from "../../wiki/types.js"; import { createUltragoalPlan, readUltragoalPlan, } from "../../ultragoal/artifacts.js"; import { getBaseStateDir } from "../../state/paths.js"; import { readSubagentTrackingState, recordNativeSubagentAuthorityObservation } from "../../subagents/tracker.js"; import { maybeNudgeLeaderForAllowedWorkerStop } from "../notify-hook/team-worker-stop.js"; import { MAX_NATIVE_STDIN_JSON_BYTES } from "../hook-payload-guard.js"; import { generateOverlay, writeSessionModelInstructionsFile } from "../../hooks/agents-overlay.js"; import { injectExecFollowup, markExecFollowupsDelivered, readPendingExecFollowups, } from "../../exec/followup.js"; const ARGUMENT_PRODUCING_RUNTIME_DENIAL_COMMANDS = [ ["node-xargs-wrapper-read", `printf x | xargs node -e "require('fs').readFileSync('src/victim.ts','utf8')"`], ] as const; const WGET_REVIEW_MUTATION_COMMANDS = [ ["wget-file-sink-without-hard-cap", "wget --no-config --no-hsts -O .omx/state/inbox/stream https://example.test/file"], ["curl-file-sink-with-timeout-but-no-hard-cap", "curl -q --max-time 1 -o .omx/state/inbox/stream https://example.test/file"], ] as const; const NATIVE_CHILD_MIXED_REFERENCE_STATE_WRITE = [ "native-child-mixed-reference-state-write", `chmod --reference=.omx/state/session.json .omx/state/reference-copy; omx state write --input '{"mode":"ultragoal"}' --json`, ] as const; const NATIVE_CHILD_REFERENCE_UNKNOWN_COMMAND = [ "native-child-reference-plus-unknown-command", "chmod --reference=.omx/state/session.json .omx/state/reference-copy; unknown-mutation-transport", ] as const; const NATIVE_CHILD_RSYNC_AUTHORITY_TARGET = [ "native-child-rsync-authority-target", "rsync .omx/state/conductor-ledger.json .omx/state/session.json", ] as const; const WGET_READ_ONLY_CONTROL_COMMANDS = [ ["wget-spider-no-body", "wget --no-config --no-hsts --spider https://example.test/file"], ["wget-stdout-output-document", "wget --no-config --no-hsts -O - https://example.test/file"], ] as const; const WGET_MAIN_METADATA_MUTATION_COMMANDS = [ ["hardlink-metadata-source-control", "ln .omx/state/conductor-ledger.json .omx/handoffs/run-1/ledger-link"], ["bounded-truncate-metadata-control", "truncate --size=16777216 .omx/state/inbox/truncate"], ] as const; const WGET_INHERITED_POSIX_COMMANDS = [ ["wget-inherited-posix-option-ordering", "wget -O src/posix-inherited-wget-owned.ts https://example.test/file -O -"], ] as const; function nativeHookScriptPath(): string { return join(process.cwd(), "dist", "scripts", "codex-native-hook.js"); } function parseSingleJsonStdout(stdout: string): Record { const trimmed = stdout.trim(); assert.notEqual(trimmed, ""); assert.equal(trimmed.split("\n").length, 1); return JSON.parse(trimmed) as Record; } function runNativeHookCli( payload: Record | string, options: { cwd?: string; env?: NodeJS.ProcessEnv } = {}, ): string { return execFileSync(process.execPath, [nativeHookScriptPath()], { cwd: options.cwd ?? process.cwd(), input: typeof payload === "string" ? payload : JSON.stringify(payload), encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"], env: options.env ?? process.env, }); } function runNativeHookCliResult( payload: Record | string, options: { cwd?: string; env?: NodeJS.ProcessEnv } = {}, ) { return spawnSync(process.execPath, [nativeHookScriptPath()], { cwd: options.cwd ?? process.cwd(), input: typeof payload === "string" ? payload : JSON.stringify(payload), encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"], env: options.env ?? process.env, }); } const OVERSIZED_STDIN_SYSTEM_MESSAGE = "OMX native hook rejected oversized stdin JSON before parsing; maxBytes=1048576."; function buildExactByteHookPayload(eventName: "PreToolUse" | "PostToolUse", byteLength: number): string { const base = JSON.stringify({ hook_event_name: eventName, session_id: `native-${eventName.toLowerCase()}-😀é`, padding: "", }); const paddingBytes = byteLength - Buffer.byteLength(base, "utf8"); assert.ok(paddingBytes >= 0); const payload = JSON.stringify({ hook_event_name: eventName, session_id: `native-${eventName.toLowerCase()}-😀é`, padding: "x".repeat(paddingBytes), }); assert.equal(Buffer.byteLength(payload, "utf8"), byteLength); assert.notEqual(payload.length, Buffer.byteLength(payload, "utf8")); return payload; } async function writeJson(path: string, value: unknown): Promise { await mkdir(dirname(path), { recursive: true }).catch(() => {}); await writeFile(path, JSON.stringify(value, null, 2)); } function readLinuxStartTicks(pid: number): number | null { try { const stat = readFileSync(`/proc/${pid}/stat`, "utf-8"); const commandEnd = stat.lastIndexOf(")"); if (commandEnd === -1) return null; const fields = stat.slice(commandEnd + 1).trim().split(/\s+/); if (fields.length <= 19) return null; const startTicks = Number(fields[19]); return Number.isInteger(startTicks) && startTicks >= 0 ? startTicks : null; } catch { return null; } } function readLinuxCmdline(pid: number): string | null { try { const text = readFileSync(`/proc/${pid}/cmdline`, "utf-8").replace(/\0+/g, " ").trim(); return text.length > 0 ? text : null; } catch { return null; } } const AMBIENT_UNSAFE_NODE_RUNTIME_ENV_NAMES = [ "NODE_OPTIONS", "OPENSSL_CONF", "NODE_V8_COVERAGE", "NODE_COMPILE_CACHE", "NODE_REDIRECT_WARNINGS", "NODE_REPORT_DIRECTORY", "NODE_REPORT_FILENAME", ] as const; async function withCleanAmbientNodeRuntimeEnvironment(run: () => Promise): Promise { const previousRuntimeEnv = Object.fromEntries( AMBIENT_UNSAFE_NODE_RUNTIME_ENV_NAMES.map((name) => [name, process.env[name]]), ); for (const name of AMBIENT_UNSAFE_NODE_RUNTIME_ENV_NAMES) delete process.env[name]; try { return await run(); } finally { for (const name of AMBIENT_UNSAFE_NODE_RUNTIME_ENV_NAMES) { if (previousRuntimeEnv[name] === undefined) delete process.env[name]; else process.env[name] = previousRuntimeEnv[name]; } } } async function writeCanonicalLeaderFixture( stateDir: string, sessionId: string, leaderThreadId: string, cwd: string, ): Promise { await writeJson(join(stateDir, "session.json"), { session_id: sessionId, leader_thread_id: leaderThreadId, cwd, }); await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { [sessionId]: { session_id: sessionId, leader_thread_id: leaderThreadId, threads: { [leaderThreadId]: { thread_id: leaderThreadId, kind: "leader" }, }, }, }, }); } async function withTrustedWorkspaceOmxCli( cwd: string, action: (omxCommand: string, trustedPath: string) => Promise, commandForm: "assignment" | "env" = "assignment", ): Promise { const cliPath = realpathSync(resolve(process.cwd(), "dist", "cli", "omx.js")); const binDir = join(cwd, "node_modules", ".bin"); const shimPath = join(binDir, "omx"); await mkdir(binDir, { recursive: true }); await rm(shimPath, { force: true }); await symlink(cliPath, shimPath); const path = `${binDir}:${dirname(process.execPath)}:/usr/bin:/bin`; return await action(commandForm === "env" ? `env PATH="${path}" omx` : `PATH="${path}" omx`, path); } async function writeNativeMappedSessionState( cwd: string, stateDir: string, sessionId: string, nativeSessionId: string, leaderThreadId?: string, ): Promise { await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId, native_session_id: nativeSessionId, cwd, }); if (leaderThreadId) { await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { [sessionId]: { session_id: sessionId, leader_thread_id: leaderThreadId, threads: { [leaderThreadId]: { thread_id: leaderThreadId, kind: "leader" }, }, }, }, }); } } async function writeLiveNativeMappedSessionState( cwd: string, stateDir: string, sessionId: string, nativeSessionId: string, leaderThreadId?: string, ): Promise { await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); const liveState = await writeSessionStart(cwd, sessionId, { nativeSessionId, }); const liveStatePath = join(cwd, ".omx", "state", "session.json"); const targetStatePath = join(stateDir, "session.json"); if (liveStatePath !== targetStatePath) { await writeFile(targetStatePath, JSON.stringify(liveState, null, 2)); } if (leaderThreadId) { await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { [sessionId]: { session_id: sessionId, leader_thread_id: leaderThreadId, threads: { [leaderThreadId]: { thread_id: leaderThreadId, kind: "leader" }, }, }, }, }); } } async function writeLiveNativeSessionOwnerSidecar( cwd: string, stateDir: string, sessionId: string, ): Promise { const selected = JSON.parse( await readFile(join(stateDir, "session.json"), "utf-8"), ) as Record; await writeJson( join(stateDir, "sessions", sessionId, "session-owner.json"), { ...selected, session_id: sessionId, native_session_id: sessionId, started_at: new Date().toISOString(), cwd, }, ); } async function withIndependentNativeSession( suffix: string, run: (fixture: { cwd: string; stateDir: string; sessionId: string; pointerBefore: string; }) => Promise, ): Promise { const cwd = await mkdtemp(join(tmpdir(), `omx-native-hook-sidecar-${suffix}-`)); try { const stateDir = join(cwd, ".omx", "state"); const selectedSessionId = `native-selected-${suffix}`; const sessionId = `native-independent-${suffix}`; await writeSessionStart(cwd, selectedSessionId, { nativeSessionId: selectedSessionId, pid: process.pid, }); await writeLiveNativeSessionOwnerSidecar(cwd, stateDir, sessionId); await run({ cwd, stateDir, sessionId, pointerBefore: await readFile(join(stateDir, "session.json"), "utf-8"), }); } finally { await rm(cwd, { recursive: true, force: true }); } } async function writeSessionSkillActiveState( stateDir: string, sessionId: string, skill: string, phase: string, ): Promise { await writeJson( join(stateDir, "sessions", sessionId, "skill-active-state.json"), { active: true, skill, phase, session_id: sessionId, active_skills: [{ skill, phase, active: true, session_id: sessionId }], }, ); } async function writeIssue3239ActiveAutopilotDeepInterviewState( cwd: string, sessionId: string, threadId: string, ): Promise { const stateDir = join(cwd, ".omx", "state"); const sessionDir = join(stateDir, "sessions", sessionId); await mkdir(sessionDir, { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId, leader_thread_id: threadId, cwd, }); await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { [sessionId]: { session_id: sessionId, leader_thread_id: threadId, threads: { [threadId]: { thread_id: threadId, kind: "leader" }, }, }, }, }); await writeJson(join(sessionDir, "skill-active-state.json"), { version: 1, active: true, skill: "autopilot", phase: "deep-interview", session_id: sessionId, thread_id: threadId, active_skills: [ { skill: "autopilot", phase: "deep-interview", active: true, session_id: sessionId, thread_id: threadId }, ], }); await writeJson(join(sessionDir, "autopilot-state.json"), { active: true, mode: "autopilot", current_phase: "deep-interview", session_id: sessionId, thread_id: threadId, workingDirectory: cwd, deep_interview_gate: { status: "required" }, }); await writeJson(join(sessionDir, "deep-interview-state.json"), { active: true, mode: "deep-interview", current_phase: "intent-first", session_id: sessionId, thread_id: threadId, workingDirectory: cwd, }); } async function setTeamPaneIds( cwd: string, teamName: string, paneIds: { leaderPaneId: string; workerPaneIds: Record }, ): Promise { for (const fileName of ["config.json", "manifest.v2.json"]) { const filePath = join(cwd, ".omx", "state", "team", teamName, fileName); const parsed = JSON.parse(await readFile(filePath, "utf-8")) as { leader_pane_id?: string | null; workers?: Array<{ name?: string; pane_id?: string | null }>; }; parsed.leader_pane_id = paneIds.leaderPaneId; parsed.workers = (parsed.workers ?? []).map((worker) => ({ ...worker, pane_id: worker.name ? (paneIds.workerPaneIds[worker.name] ?? worker.pane_id ?? null) : (worker.pane_id ?? null), })); await writeJson(filePath, parsed); } } async function configureAuthoritativeTeamWorker( cwd: string, teamName: string, workerPaneId = "%10", workerCwd = cwd, ): Promise { const stateRoot = join(cwd, ".omx", "state"); await initTeamState(teamName, "session pointer ownership regression", "executor", 1, cwd, undefined, { OMX_SESSION_ID: "leader-session", }); await setTeamPaneIds(cwd, teamName, { leaderPaneId: "%42", workerPaneIds: { "worker-1": workerPaneId }, }); for (const fileName of ["config.json", "manifest.v2.json"]) { const filePath = join(stateRoot, "team", teamName, fileName); const state = JSON.parse(await readFile(filePath, "utf-8")) as { team_state_root?: string; leader_cwd?: string; workers?: Array>; }; state.team_state_root = stateRoot; state.leader_cwd = cwd; state.workers = (state.workers ?? []).map((worker) => ({ ...worker, working_dir: workerCwd, worktree_path: workerCwd, team_state_root: stateRoot, })); await writeJson(filePath, state); } await writeJson(join(stateRoot, "team", teamName, "workers", "worker-1", "identity.json"), { name: "worker-1", index: 1, role: "executor", pane_id: workerPaneId, working_dir: workerCwd, worktree_path: workerCwd, team_state_root: stateRoot, }); process.env.TMUX = "1"; process.env.TMUX_PANE = workerPaneId; process.env.OMX_TEAM_INTERNAL_WORKER = `${teamName}/worker-1`; process.env.OMX_TEAM_WORKER = `${teamName}/worker-1`; process.env.OMX_TEAM_STATE_ROOT = stateRoot; process.env.OMX_TEAM_LEADER_CWD = cwd; process.env.OMX_SESSION_ID = "leader-session"; } async function withIsolatedHome( prefix: string, run: (homeDir: string) => Promise, ): Promise { const homeDir = await mkdtemp( join(tmpdir(), `omx-native-hook-home-${prefix}-`), ); const previousHome = process.env.HOME; try { process.env.HOME = homeDir; return await run(homeDir); } finally { if (typeof previousHome === "string") process.env.HOME = previousHome; else delete process.env.HOME; await rm(homeDir, { recursive: true, force: true }); } } async function withLoreGuardConfig( value: string, prefix: string, run: (cwd: string) => Promise, ): Promise { const cwd = await mkdtemp( join(tmpdir(), `omx-native-hook-pretool-git-commit-lore-${prefix}-`), ); const codexHome = await mkdtemp( join(tmpdir(), `omx-native-hook-codex-home-lore-${prefix}-`), ); const defaultHome = await mkdtemp( join(tmpdir(), `omx-native-hook-home-lore-${prefix}-`), ); const originalGuard = process.env.OMX_LORE_COMMIT_GUARD; const originalCodexHome = process.env.CODEX_HOME; const originalHome = process.env.HOME; try { delete process.env.OMX_LORE_COMMIT_GUARD; process.env.CODEX_HOME = codexHome; process.env.HOME = defaultHome; await writeFile( join(codexHome, "config.toml"), `[shell_environment_policy.set]\nOMX_LORE_COMMIT_GUARD = "${value}"\n`, "utf-8", ); return await run(cwd); } finally { if (originalGuard === undefined) delete process.env.OMX_LORE_COMMIT_GUARD; else process.env.OMX_LORE_COMMIT_GUARD = originalGuard; if (originalCodexHome === undefined) delete process.env.CODEX_HOME; else process.env.CODEX_HOME = originalCodexHome; if (originalHome === undefined) delete process.env.HOME; else process.env.HOME = originalHome; await rm(cwd, { recursive: true, force: true }); await rm(codexHome, { recursive: true, force: true }); await rm(defaultHome, { recursive: true, force: true }); } } function buildWorkerStopFakeTmux( tmuxLogPath: string, options: { failSend?: boolean; busyLeader?: boolean; captureText?: string; currentCommand?: string; sendDelayMs?: number; removePathOnSend?: string; removePathOnCapture?: string; } = {}, ): string { const rawCaptureText = options.captureText ?? (options.busyLeader ? "• Working… (esc to interrupt)" : "› ready"); const captureText = `'${rawCaptureText.replace(/'/g, "'\"'\"'")}'`; const currentCommand = `'${(options.currentCommand ?? "codex").replace(/'/g, "'\"'\"'")}'`; const sendDelaySeconds = Math.max(0, options.sendDelayMs ?? 0) / 1000; const removePathOnSend = options.removePathOnSend ? `'${options.removePathOnSend.replace(/'/g, "'\"'\"'")}'` : ""; const removePathOnCapture = options.removePathOnCapture ? `'${options.removePathOnCapture.replace(/'/g, "'\"'\"'")}'` : ""; return `#!/usr/bin/env bash set -eu echo "$@" >> "${tmuxLogPath}" cmd="$1" shift || true if [[ "$cmd" == "show-option" && "\${@: -1}" == "@omx_team_pane_owner_id" ]]; then printf '%s\n' 'team:test' exit 0 fi if [[ "$cmd" == "display-message" ]]; then fmt="" while [[ "$#" -gt 0 ]]; do case "$1" in -p) ;; -t) shift ;; *) fmt="$1" ;; esac shift || true done case "$fmt" in "#{pane_in_mode}") echo "0" ;; "#{pane_id}") echo "%42" ;; "#{pane_current_path}") pwd ;; "#{pane_start_command}") echo "codex" ;; "#{pane_current_command}") printf '%s\\n' ${currentCommand} ;; "#S") echo "omx-team-worker-stop" ;; *) ;; esac exit 0 fi if [[ "$cmd" == "list-panes" ]]; then printf '%%10\t0\t12310\n%%11\t0\t12311\n%%42\t0\t12345\n' exit 0 fi if [[ "$cmd" == "capture-pane" ]]; then ${removePathOnCapture ? `rm -rf ${removePathOnCapture}` : ""} printf '%s\\n' ${captureText} exit 0 fi if [[ "$cmd" == "set-buffer" ]]; then printf '%s' "\${@: -1}" > "${tmuxLogPath}.buffer" exit 0 fi if [[ "$cmd" == "show-buffer" ]]; then if [[ -f "${tmuxLogPath}.buffer" ]]; then cat "${tmuxLogPath}.buffer"; fi exit 0 fi if [[ "$cmd" == "paste-buffer" ]]; then target="" while [[ "$#" -gt 0 ]]; do case "$1" in -t) target="$2"; shift 2 ;; *) shift ;; esac done if [[ -f "${tmuxLogPath}.buffer" ]]; then echo "send-keys -t \${target} -l $(cat "${tmuxLogPath}.buffer")" >> "${tmuxLogPath}" fi exit 0 fi if [[ "$cmd" == "delete-buffer" ]]; then rm -f "${tmuxLogPath}.buffer" exit 0 fi if [[ "$cmd" == "send-keys" ]]; then ${sendDelaySeconds > 0 ? `sleep ${sendDelaySeconds}` : ""} ${removePathOnSend ? `rm -rf ${removePathOnSend}` : ""} ${options.failSend ? "exit 1" : "exit 0"} fi exit 0 `; } function buildSessionOwnerEvidenceTmux(paneInstanceId: string, sessionInstanceId = ""): string { return `#!/usr/bin/env bash set -eu case "\${1:-}" in display-message) printf '%s\n' "omx-owner-evidence" ;; show-option|show-options) case "\${@: -1}" in @omx_pane_instance_id) printf '%s\n' "${paneInstanceId}" ;; @omx_instance_id) printf '%s\n' "${sessionInstanceId}" ;; esac ;; esac `; } async function initTempGitRepo(prefix: string): Promise { const cwd = await mkdtemp(join(tmpdir(), prefix)); execFileSync("git", ["init"], { cwd, stdio: "ignore" }); execFileSync("git", ["config", "user.email", "test@example.com"], { cwd, stdio: "ignore", }); execFileSync("git", ["config", "user.name", "Test User"], { cwd, stdio: "ignore", }); return cwd; } async function writeActiveAutopilotSession( cwd: string, sessionId: string, ): Promise { await writeJson(join(cwd, ".omx", "state", "session.json"), { session_id: sessionId, }); await writeJson( join(cwd, ".omx", "state", "sessions", sessionId, "autopilot-state.json"), { active: true, current_phase: "execution", }, ); } async function writeHookCounterPlugin(cwd: string): Promise { const markerPath = join(cwd, ".omx", "stop-hook-counter.json"); await mkdir(join(cwd, ".omx", "hooks"), { recursive: true }); await writeFile( join(cwd, ".omx", "hooks", "count-stop-hook.mjs"), `import { mkdir, readFile, writeFile } from "node:fs/promises"; import { dirname, join } from "node:path"; export async function onHookEvent(event) { if (event.event !== "stop") return; const outPath = join(process.cwd(), ".omx", "stop-hook-counter.json"); await mkdir(dirname(outPath), { recursive: true }); let count = 0; try { count = JSON.parse(await readFile(outPath, "utf-8")).count || 0; } catch {} await writeFile(outPath, JSON.stringify({ count: count + 1 }, null, 2)); } `, "utf-8", ); return markerPath; } async function writeReleaseReadinessLeaderAttention( teamName: string, sessionId: string, cwd: string, options: { workRemaining: boolean }, ): Promise { await writeTeamLeaderAttention( teamName, { team_name: teamName, updated_at: "2026-04-12T17:20:00.000Z", source: "notify_hook", leader_decision_state: "done_waiting_on_leader", leader_attention_pending: true, leader_attention_reason: "leader_session_stopped", attention_reasons: ["leader_session_stopped"], leader_stale: true, leader_session_active: false, leader_session_id: sessionId, leader_session_stopped_at: "2026-04-12T17:20:00.000Z", unread_leader_message_count: 0, work_remaining: options.workRemaining, stalled_for_ms: null, }, cwd, ); } async function writeReleaseReadinessStateMarker( sessionId: string, teamName: string, cwd: string, ): Promise { await writeJson( join( cwd, ".omx", "state", "sessions", sessionId, "release-readiness-state.json", ), { active: true, session_id: sessionId, team_name: teamName, stable_final_recommendation_emitted: true, }, ); } const TEAM_STOP_COMMIT_GUIDANCE = " If system-generated worker auto-checkpoint commits exist, rewrite them into Lore-format final commits before merge/finalization."; const DEFAULT_AUTO_NUDGE_RESPONSE = "continue with the current task only if it is already authorized"; const TEAM_ENV_KEYS = [ "OMX_TEAM_WORKER", "OMX_TEAM_INTERNAL_WORKER", "OMX_TEAM_STATE_ROOT", "OMX_TEAM_LEADER_CWD", "OMX_TEAM_MODE", "OMX_SESSION_ID", "OMX_ROOT", "OMX_STATE_ROOT", "SESSION_ID", "OMX_QUESTION_RETURN_PANE", "OMX_LEADER_PANE_ID", "TMUX", "TMUX_PANE", "OMX_TMUX_HUD_OWNER", "OMX_NATIVE_STOP_NO_PROGRESS_MAX_REPEATS", "OMX_NATIVE_STOP_NO_PROGRESS_IDLE_MS", ] as const; const priorTeamEnv = new Map< (typeof TEAM_ENV_KEYS)[number], string | undefined >(); beforeEach(() => { priorTeamEnv.clear(); for (const key of TEAM_ENV_KEYS) { priorTeamEnv.set(key, process.env[key]); delete process.env[key]; } }); afterEach(() => { for (const key of TEAM_ENV_KEYS) { const value = priorTeamEnv.get(key); if (typeof value === "string") process.env[key] = value; else delete process.env[key]; } priorTeamEnv.clear(); }); describe("codex native hook config", () => { it("builds the expected managed hooks.json shape", () => { const config = buildManagedCodexHooksConfig("/tmp/omx"); assert.deepEqual(Object.keys(config.hooks), [ "SessionStart", "PreToolUse", "PostToolUse", "UserPromptSubmit", "PreCompact", "PostCompact", "SubagentStop", "Stop", ]); const sessionStart = config.hooks.SessionStart[0] as { matcher?: string; hooks?: Array>; }; assert.equal(sessionStart.matcher, "startup|resume|clear"); assert.equal(sessionStart.hooks?.[0]?.statusMessage, undefined); const preToolUse = config.hooks.PreToolUse[0] as { matcher?: string; hooks?: Array>; }; assert.equal(preToolUse.matcher, undefined); assert.match( String(preToolUse.hooks?.[0]?.command || ""), /codex-native-hook\.js"?$/, ); assert.equal(preToolUse.hooks?.[0]?.statusMessage, undefined); const postToolUse = config.hooks.PostToolUse[0] as { matcher?: string; hooks?: Array>; }; assert.equal(postToolUse.matcher, undefined); assert.match( String(postToolUse.hooks?.[0]?.command || ""), /codex-native-hook\.js"?$/, ); assert.equal(postToolUse.hooks?.[0]?.statusMessage, undefined); const userPromptSubmit = config.hooks.UserPromptSubmit[0] as { matcher?: string; hooks?: Array>; }; assert.equal(userPromptSubmit.matcher, undefined); assert.match( String(userPromptSubmit.hooks?.[0]?.command || ""), /codex-native-hook\.js"?$/, ); assert.equal(userPromptSubmit.hooks?.[0]?.statusMessage, undefined); const stop = config.hooks.Stop[0] as { hooks?: Array>; }; assert.equal(stop.hooks?.[0]?.timeout, 30); const postCompact = config.hooks.PostCompact[0] as { matcher?: string; hooks?: Array>; }; assert.equal(postCompact.matcher, undefined); assert.match( String(postCompact.hooks?.[0]?.command || ""), /codex-native-hook\.js"?$/, ); assert.doesNotMatch( String(postCompact.hooks?.[0]?.command || ""), /PostCompact Nudge|additionalContext|printf/, ); }); }); describe("codex native hook dispatch", { concurrency: false }, () => { it("treats space-containing argv entry paths as the main module", () => { const entryPath = "/tmp/omx native/codex-native-hook.js"; assert.equal( isCodexNativeHookMainModule(pathToFileURL(entryPath).href, entryPath), true, ); }); it("does not treat a different module url as the main module", () => { assert.equal( isCodexNativeHookMainModule( pathToFileURL("/tmp/omx native/other-script.js").href, "/tmp/omx native/codex-native-hook.js", ), false, ); }); it("emits Stop-schema-safe block JSON when unidentifiable malformed stdin has native Stop runtime surface", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-cli-malformed-stop-surface-"), ); try { await mkdir(join(cwd, ".omx"), { recursive: true }); const result = spawnSync(process.execPath, [nativeHookScriptPath()], { cwd, input: "{", encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"], }); assert.equal(result.status, 0, result.stderr || result.stdout); assert.equal(result.stderr, ""); const output = parseSingleJsonStdout(result.stdout) as { decision?: string; continue?: boolean; reason?: string; stopReason?: string; systemMessage?: string; hookSpecificOutput?: unknown; }; assert.equal(output.decision, "block"); assert.equal(output.continue, undefined); assert.equal( output.reason, "OMX native hook received malformed JSON input. Preserve runtime state, inspect the emitting hook payload yourself, and retry with valid JSON.", ); assert.equal(output.stopReason, "native_hook_stdin_parse_error"); assert.equal(output.hookSpecificOutput, undefined); assert.match( String(output.systemMessage ?? ""), /stdin JSON parsing failed inside codex-native-hook:/, ); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("preserves non-Stop fail-closed JSON when malformed stdin identifies a non-Stop hook", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-cli-malformed-nonstop-"), ); try { await mkdir(join(cwd, ".omx"), { recursive: true }); const result = spawnSync(process.execPath, [nativeHookScriptPath()], { cwd, input: '{hook_event_name:"PreToolUse",', encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"], }); assert.equal(result.status, 0, result.stderr || result.stdout); assert.equal(result.stderr, ""); const output = parseSingleJsonStdout(result.stdout) as { continue?: boolean; decision?: string; stopReason?: string; systemMessage?: string; hookSpecificOutput?: unknown; }; const hookSpecificOutput = output.hookSpecificOutput as { hookEventName?: string; permissionDecision?: string; permissionDecisionReason?: string; } | undefined; assert.equal(output.continue, undefined); assert.equal(output.decision, undefined); assert.equal(output.stopReason, undefined); assert.equal(hookSpecificOutput?.hookEventName, "PreToolUse"); assert.equal(hookSpecificOutput?.permissionDecision, "deny"); assert.equal( hookSpecificOutput?.permissionDecisionReason, "OMX native hook received malformed JSON input. Preserve runtime state, inspect the emitting hook payload yourself, and retry with valid JSON.", ); assert.match( String(output.systemMessage ?? ""), /stdin JSON parsing failed inside codex-native-hook:/, ); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("redacts unterminated prompt-like malformed stdin fields", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-cli-malformed-unterminated-"), ); try { const privatePrompt = "PRIVATE_UNTERMINATED_PROMPT"; const malformed = `{hook_event_name:"PostToolUse", prompt:"${privatePrompt}`; const result = spawnSync(process.execPath, [nativeHookScriptPath()], { cwd, input: malformed, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"], }); assert.equal(result.status, 0, result.stderr || result.stdout); assert.equal(result.stderr, ""); const output = parseSingleJsonStdout(result.stdout); assert.equal(output.stopReason, "native_hook_stdin_parse_error"); const log = await readFile( join( cwd, ".omx", "logs", `native-hook-${new Date().toISOString().split("T")[0]}.jsonl`, ), "utf-8", ); const entry = JSON.parse(log.trim()) as Record; const prefix = String(entry.raw_input_prefix ?? ""); assert.doesNotMatch(prefix, new RegExp(privatePrompt)); assert.match(prefix, /prompt:"\[REDACTED\]"/); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("logs a bounded redacted raw stdin prefix when CLI stdin is malformed", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-cli-malformed-log-prefix-"), ); try { const secret = "sk-test-secret123456"; const promptText = "summarize private launch notes"; const malformed = `{hook_event_name:"PostToolUse", access_token:"${secret}", prompt:"${promptText}", text:"${promptText}", bad:"${"x".repeat(400)}"}${String.fromCharCode(10, 0, 7)}`; const result = spawnSync(process.execPath, [nativeHookScriptPath()], { cwd, input: malformed, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"], }); assert.equal(result.status, 0, result.stderr || result.stdout); assert.equal(result.stderr, ""); const output = parseSingleJsonStdout(result.stdout); assert.equal(output.stopReason, "native_hook_stdin_parse_error"); const log = await readFile( join( cwd, ".omx", "logs", `native-hook-${new Date().toISOString().split("T")[0]}.jsonl`, ), "utf-8", ); const entry = JSON.parse(log.trim()) as Record; const prefix = String(entry.raw_input_prefix ?? ""); assert.equal(entry.type, "native_hook_stdin_parse_error"); assert.equal( entry.raw_input_length, Buffer.byteLength(malformed, "utf-8"), ); assert.ok( prefix.length <= 240, `prefix should be bounded, got ${prefix.length}`, ); assert.doesNotMatch(prefix, /[\u0000-\u001f\u007f-\u009f]/); assert.doesNotMatch(prefix, new RegExp(secret)); assert.doesNotMatch(prefix, new RegExp(promptText)); assert.match(prefix, /\[REDACTED\]/); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("emits Stop-schema-safe block JSON when malformed stdin still identifies Stop", () => { const stdout = runNativeHookCli('{hook_event_name:"Stop",'); const output = parseSingleJsonStdout(stdout) as { decision?: string; reason?: string; stopReason?: string; systemMessage?: string; hookSpecificOutput?: unknown; }; assert.equal(output.decision, "block"); assert.equal( output.reason, "OMX native hook received malformed JSON input. Preserve runtime state, inspect the emitting hook payload yourself, and retry with valid JSON.", ); assert.equal(output.stopReason, "native_hook_stdin_parse_error"); assert.equal(output.hookSpecificOutput, undefined); assert.match( String(output.systemMessage ?? ""), /stdin JSON parsing failed inside codex-native-hook:/, ); }); it("emits no-op JSON stdout for PreToolUse non-Bash tools with null output", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-cli-pretool-nonbash-noop-"), ); try { const result = spawnSync(process.execPath, [nativeHookScriptPath()], { cwd, input: JSON.stringify({ hook_event_name: "PreToolUse", cwd, session_id: "sess-cli-pretool-nonbash-noop", thread_id: "thread-cli-pretool-nonbash-noop", turn_id: "turn-cli-pretool-nonbash-noop", tool_name: "Read", tool_input: { file_path: "package.json" }, }), encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"], }); assert.equal(result.status, 0, result.stderr || result.stdout); assert.equal(result.stderr, ""); assert.deepEqual(parseSingleJsonStdout(result.stdout), {}); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("emits PreToolUse CLI advisory JSON with only systemMessage", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-cli-pretool-schema-safe-"), ); try { const output = parseSingleJsonStdout( runNativeHookCli( { hook_event_name: "PreToolUse", cwd, session_id: "sess-cli-pretool-schema-safe", thread_id: "thread-cli-pretool-schema-safe", turn_id: "turn-cli-pretool-schema-safe", tool_name: "Bash", tool_input: { command: "rm -rf dist" }, }, { cwd }, ), ); assert.deepEqual(output, { systemMessage: "Destructive Bash command detected (`rm -rf dist`). Confirm the target and expected side effects before running it.", }); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("emits PreToolUse CLI block JSON as hook-specific deny with preserved systemMessage guidance (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("preserves ralplan PreToolUse planning guard as hook-specific deny JSON (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("allows Ralplan Markdown draft-only apply_patch on the live CLI path while denying mixed targets (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("rejects unauthenticated typed Team-worker claims before planning guards (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("preserves deep-interview PreToolUse planning guard as hook-specific deny JSON (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("synthesizes a deny for malformed explicit PreToolUse blocks instead of downgrading systemMessage to advisory (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("keeps wrapped ralplan implementation writes blocked at raw classification while allowing planning artifacts (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("blocks deep-interview PreToolUse implementation writes when terminal Autopilot run-state shadows stale active state (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("emits parseable no-op JSON stdout for inactive Stop CLI runs", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-cli-stop-noop-json-"), ); try { const stdout = runNativeHookCli( { hook_event_name: "Stop", cwd, session_id: "sess-cli-stop-noop-json", thread_id: "thread-cli-stop-noop-json", turn_id: "turn-cli-stop-noop-json", }, { cwd }, ); const output = parseSingleJsonStdout(stdout); assert.deepEqual(output, {}); assert.equal(existsSync(join(cwd, ".omx", "state")), false); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("emits no-op JSON stdout for Stop payloads with no runtime output", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-cli-stop-null-output-"), ); try { const result = spawnSync(process.execPath, [nativeHookScriptPath()], { cwd, input: JSON.stringify({ hook_event_name: "Stop", cwd, session_id: "sess-cli-stop-null-output", thread_id: "thread-cli-stop-null-output", turn_id: "turn-cli-stop-null-output", stop_hook_active: true, }), encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"], }); assert.equal(result.status, 0, result.stderr || result.stdout); assert.equal(result.stderr, ""); assert.deepEqual(parseSingleJsonStdout(result.stdout), {}); assert.equal(existsSync(join(cwd, ".omx", "state")), false); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("records bounded completion evidence for native subagent Stop events (#3729)", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-stop-completion-evidence-"), ); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-stop-subagent"; const threadId = "thread-stop-subagent"; const leaderThreadId = "leader-thread"; await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeSessionStart(cwd, sessionId, { nativeSessionId: leaderThreadId, pid: process.pid }); // Create transcript with thread_spawn metadata to make dispatcher detect as subagent const stopTranscriptPath = join(cwd, "stop-transcript.jsonl"); await writeFile(stopTranscriptPath, JSON.stringify({ type: "session_meta", payload: { id: threadId, source: { subagent: { thread_spawn: { parent_thread_id: leaderThreadId } } } } }) + "\n"); // Initialize subagent tracking by recording the native subagent observation await recordNativeSubagentAuthorityObservation(cwd, { sessionIds: [sessionId], childThreadId: threadId, parentThreadId: leaderThreadId, rootNativeSessionId: leaderThreadId, authorityEvidence: "valid", }); // Send Stop event from subagent thread const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: sessionId, thread_id: threadId, stop_hook_active: true, }, { cwd, sessionOwnerPid: process.pid }, ); // Verify completion evidence was recorded const tracking = JSON.parse( await readFile(join(stateDir, "subagent-tracking.json"), "utf-8"), ) as { sessions?: Record< string, { threads?: Record } >; }; const thread = tracking.sessions?.[sessionId]?.threads?.[threadId]; assert.ok(thread?.completed_at, "completion evidence must record completed_at timestamp"); assert.equal(thread?.completion_source, "native_stop_event", "completion evidence must mark completionSource as native_stop_event"); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("records bounded delivery evidence for native subagent SessionStart events (#3729)", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-sessionstart-delivery-evidence-"), ); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-sessionstart-subagent"; const threadId = "thread-sessionstart-subagent"; const leaderThreadId = "leader-thread"; await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeSessionStart(cwd, sessionId, { nativeSessionId: leaderThreadId, pid: process.pid }); // Create transcript with thread_spawn metadata to make dispatcher detect as subagent const transcriptPath = join(cwd, "sessionstart-transcript.jsonl"); await writeFile(transcriptPath, JSON.stringify({ type: "session_meta", payload: { id: threadId, source: { subagent: { thread_spawn: { parent_thread_id: leaderThreadId } } } } }) + "\n"); // Send SessionStart event from subagent thread // Note: session_id should be the child's thread ID, thread_id is required for evidence recording const result = await dispatchCodexNativeHook( { hook_event_name: "SessionStart", cwd, session_id: threadId, thread_id: threadId, transcript_path: transcriptPath, }, { cwd, sessionOwnerPid: process.pid }, ); // Verify delivery evidence was recorded (without completion_source) // Note: Sessions are keyed by canonical session ID in tracking state const canonicalFromPointer = JSON.parse( await readFile(join(stateDir, "session.json"), "utf-8"), ) as { session_id?: string }; const canonicalSessionIdResult = canonicalFromPointer.session_id || sessionId; const tracking = JSON.parse( await readFile(join(stateDir, "subagent-tracking.json"), "utf-8"), ) as { sessions?: Record< string, { threads?: Record } >; }; const thread = tracking.sessions?.[canonicalSessionIdResult]?.threads?.[threadId]; assert.ok(thread, "thread should exist in tracking"); // SessionStart should record the turn (turn_count should be incremented to 1) assert.equal(thread?.turn_count, 1, "SessionStart must record the turn (turn_count should be 1)"); // SessionStart should NOT set completed_at assert.ok(!thread?.completed_at, "SessionStart must not set completed_at timestamp"); // SessionStart should NOT set completion_source assert.ok(!thread?.completion_source, "SessionStart must not set completion_source"); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does NOT record completion evidence for leader Stop events (#3729)", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-stop-leader-no-record-"), ); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-stop-leader"; const leaderThreadId = "leader-thread-stop"; await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); // Initialize subagent tracking with only leader thread BEFORE writeSessionStart await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { [sessionId]: { session_id: sessionId, leader_thread_id: leaderThreadId, updated_at: new Date().toISOString(), threads: { [leaderThreadId]: { thread_id: leaderThreadId, kind: "leader", status: "available", first_seen_at: new Date().toISOString(), last_seen_at: new Date().toISOString(), }, }, }, }, }); // Send Stop event from leader thread const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: sessionId, thread_id: leaderThreadId, stop_hook_active: true, }, { cwd }, ); // Verify NO completion evidence was recorded for leader const tracking = JSON.parse( await readFile(join(stateDir, "subagent-tracking.json"), "utf-8"), ) as { sessions?: Record< string, { threads?: Record } >; }; const thread = tracking.sessions?.[sessionId]?.threads?.[leaderThreadId]; assert.ok(!thread?.completed_at, "leader Stop must not record completed_at timestamp"); assert.ok(!thread?.completion_source, "leader Stop must not record completion_source"); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("emits no-op JSON for oversized Stop stdin without parsing or creating inactive state", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-cli-stop-oversized-"), ); try { const oversizedStop = JSON.stringify({ hook_event_name: "Stop", cwd, session_id: "sess-cli-stop-oversized", transcript: "x".repeat(MAX_NATIVE_STDIN_JSON_BYTES + 1), }); const stdout = runNativeHookCli(oversizedStop, { cwd }); assert.deepEqual(parseSingleJsonStdout(stdout), {}); assert.equal(existsSync(join(cwd, ".omx", "state")), false); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("drains oversized stdin without breaking the parent writer pipe", async () => { const child = spawn(process.execPath, [nativeHookScriptPath()], { stdio: ["pipe", "ignore", "ignore"], }); const payload = JSON.stringify({ hook_event_name: "UserPromptSubmit", prompt: "x".repeat(MAX_NATIVE_STDIN_JSON_BYTES * 2), }); const exitCode = new Promise((resolve) => { child.once("close", resolve); }); const writeError = await new Promise((resolve) => { child.stdin.once("error", resolve); child.stdin.end(payload, () => resolve(null)); }); assert.equal(writeError, null); assert.equal(await exitCode, 0); }); it("blocks oversized Stop stdin when current session autopilot is active", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-cli-stop-oversized-active-"), ); try { await writeActiveAutopilotSession(cwd, "sess-cli-stop-oversized-active"); const oversizedStop = JSON.stringify({ hook_event_name: "Stop", cwd, session_id: "native-session-hidden-by-oversized-payload", transcript: "x".repeat(MAX_NATIVE_STDIN_JSON_BYTES + 1), }); const output = parseSingleJsonStdout( runNativeHookCli(oversizedStop, { cwd }), ) as { decision?: string; stopReason?: string; systemMessage?: string; }; assert.equal(output.decision, "block"); assert.equal( output.stopReason, "native_stop_stdin_oversized_active_workflow", ); assert.match( String(output.systemMessage ?? ""), /active current-session workflow state/, ); assert.equal(existsSync(join(cwd, ".omx", "logs")), false); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("emits no-op JSON for oversized Stop stdin for unrelated root autopilot state", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-cli-stop-oversized-stale-root-"), ); try { await writeJson(join(cwd, ".omx", "state", "session.json"), { session_id: "sess-current-without-active-autopilot", cwd, }); await writeJson(join(cwd, ".omx", "state", "autopilot-state.json"), { active: true, current_phase: "execution", }); const oversizedStop = JSON.stringify({ hook_event_name: "Stop", cwd, transcript: "x".repeat(MAX_NATIVE_STDIN_JSON_BYTES + 1), }); assert.deepEqual( parseSingleJsonStdout(runNativeHookCli(oversizedStop, { cwd })), {}, ); assert.equal(existsSync(join(cwd, ".omx", "logs")), false); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("emits no-op JSON for oversized Stop stdin when terminal run-state shadows stale autopilot state", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-cli-stop-oversized-terminal-run-"), ); try { const sessionId = "sess-cli-stop-oversized-terminal-run"; await writeActiveAutopilotSession(cwd, sessionId); await writeJson( join(cwd, ".omx", "state", "sessions", sessionId, "run-state.json"), { version: 1, active: false, mode: "autopilot", outcome: "finish", lifecycle_outcome: "finished", current_phase: "complete", completed_at: "2026-05-20T11:00:00.000Z", updated_at: "2026-05-20T11:00:00.000Z", }, ); const oversizedStop = JSON.stringify({ hook_event_name: "Stop", cwd, transcript: "x".repeat(MAX_NATIVE_STDIN_JSON_BYTES + 1), }); assert.deepEqual( parseSingleJsonStdout(runNativeHookCli(oversizedStop, { cwd })), {}, ); assert.equal(existsSync(join(cwd, ".omx", "logs")), false); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("accepts exact UTF-8 byte-limit PreToolUse and PostToolUse payloads and rejects only larger input", () => { for (const eventName of ["PreToolUse", "PostToolUse"] as const) { for (const byteLength of [MAX_NATIVE_STDIN_JSON_BYTES - 1, MAX_NATIVE_STDIN_JSON_BYTES]) { const result = runNativeHookCliResult(buildExactByteHookPayload(eventName, byteLength)); assert.equal(result.status, 0, result.stderr || result.stdout); assert.doesNotMatch(result.stdout, /native_hook_stdin_oversized/); } const result = runNativeHookCliResult(buildExactByteHookPayload(eventName, MAX_NATIVE_STDIN_JSON_BYTES + 1)); assert.equal(result.status, 0, result.stderr || result.stdout); assert.equal(result.stderr, ""); const expected = eventName === "PreToolUse" ? { systemMessage: OVERSIZED_STDIN_SYSTEM_MESSAGE, hookSpecificOutput: { hookEventName: "PreToolUse", permissionDecision: "deny", permissionDecisionReason: OVERSIZED_STDIN_SYSTEM_MESSAGE, }, } : { continue: false, stopReason: "native_hook_stdin_oversized", systemMessage: OVERSIZED_STDIN_SYSTEM_MESSAGE, }; assert.deepEqual(parseSingleJsonStdout(result.stdout), expected); } }); it("fails closed for oversized non-Stop stdin before parsing", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-cli-nonstop-oversized-"), ); try { const oversizedPrompt = JSON.stringify({ hook_event_name: "UserPromptSubmit", cwd, session_id: "sess-cli-prompt-oversized", prompt: "x".repeat(MAX_NATIVE_STDIN_JSON_BYTES + 1), }); const output = parseSingleJsonStdout( runNativeHookCli(oversizedPrompt, { cwd }), ) as { continue?: boolean; stopReason?: string; systemMessage?: string; }; assert.equal(output.continue, false); assert.equal(output.stopReason, "native_hook_stdin_oversized"); assert.match( String(output.systemMessage ?? ""), /rejected oversized stdin JSON before parsing/, ); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("preserves all structural blockers through raw UserPromptSubmit state and Stop", async () => { const cases = [ { name: "unsafe-token-boundary", prompt: "$ralplan\u200B.md", expectedSkill: null }, { name: "at-suffix", prompt: "$ralplan@docs", expectedSkill: null }, { name: "hash-suffix", prompt: "$ralplan#docs", expectedSkill: null }, { name: "equals-suffix", prompt: "$ralplan=docs", expectedSkill: null }, { name: "fullwidth-at-suffix", prompt: "$ralplan@docs", expectedSkill: null }, { name: "fullwidth-hash-suffix", prompt: "$ralplan#docs", expectedSkill: null }, { name: "fullwidth-equals-suffix", prompt: "$ralplan=docs", expectedSkill: null }, { name: "directive-use-ralplan", prompt: "use $ralplan plan this", expectedSkill: "ralplan" }, { name: "directive-please-use-ralplan", prompt: "please use $ralplan plan this", expectedSkill: "ralplan" }, { name: "directive-run-ralplan", prompt: "run $ralplan plan this", expectedSkill: "ralplan" }, { name: "directive-list-use-ralplan", prompt: "- use $ralplan plan this", expectedSkill: "ralplan" }, { name: "directive-documentation-then-command", prompt: "use $ralplan is the consensus-planning command\n$autopilot build it", expectedSkill: "autopilot" }, { name: "directive-documentation-then-implicit-command", prompt: "use $ralplan is the consensus-planning command\nUse autopilot mode.", expectedSkill: "autopilot" }, { name: "directive-documentation-trailing-prose-then-command", prompt: "use $ralplan is the workflow command for planning\n$autopilot build it", expectedSkill: "autopilot" }, { name: "directive-documentation-implicit-prose", prompt: "use $ralplan is the workflow command for autopilot mode", expectedSkill: null }, { name: "directive-documentation-alias-prose", prompt: "use $ralplan is the consensus-planning command\nAutopilot mode is its alias.", expectedSkill: null }, { name: "directive-coordinated-documentation-then-command", prompt: "- use $ralplan and $autopilot are workflow commands\n$ultragoal execute it", expectedSkill: "ultragoal", expectedStopBlock: false }, { name: "directive-documentation-semicolon-directive", prompt: "use $ralplan is the consensus-planning command; use $autopilot build it", expectedSkill: "autopilot" }, { name: "directive-two-documentation-blocks", prompt: "use $ralplan is the consensus-planning command\nuse $autopilot is the autonomous workflow command\n$ultragoal execute it", expectedSkill: "ultragoal", expectedStopBlock: false }, { name: "directive-documentation-embedded-token-then-command", prompt: "use $ralplan is the workflow command for $team\n$autopilot build it", expectedSkill: "autopilot" }, { name: "directive-documentation-task-noun-followup", prompt: "use $ralplan is the workflow command; use $autopilot update the documentation", expectedSkill: "autopilot" }, { name: "directive-documentation-implicit-semicolon-followup", prompt: "use $ralplan is the consensus-planning command; use autopilot mode.", expectedSkill: "autopilot" }, { name: "directive-documentation-transition-followup", prompt: "use $ralplan is the consensus-planning command; then use $autopilot build it", expectedSkill: "autopilot" }, { name: "directive-documentation-explicit-alias", prompt: "use $ralplan is the consensus-planning command; $team is its alias", expectedSkill: null }, { name: "directive-documentation-implicit-chain", prompt: "use $ralplan is the consensus-planning command\nAutopilot mode is its alias.\n$ultragoal execute it", expectedSkill: "ultragoal", expectedStopBlock: false }, { name: "directive-documentation-fullwidth-separator", prompt: "use $ralplan,$autopilot are workflow commands", expectedSkill: null }, { name: "directive-documentation-compact-slash", prompt: "use $ralplan/$autopilot are workflow commands\n$ultragoal execute it", expectedSkill: "ultragoal", expectedStopBlock: false }, { name: "reference-prompts-title-then-command", prompt: "[docs]: /target \"title\nUse /prompts:architect\"\n$ultragoal execute it", expectedSkill: "ultragoal", expectedStopBlock: false }, { name: "doc-period-implicit", prompt: "use $ralplan is the consensus-planning command. Use autopilot mode.", expectedSkill: "autopilot" }, { name: "doc-bare-implicit", prompt: "use $ralplan is the consensus-planning command; autopilot mode.", expectedSkill: "autopilot" }, { name: "doc-fullwidth-semicolon", prompt: "use $ralplan is the consensus-planning command; use $autopilot build it", expectedSkill: "autopilot" }, { name: "doc-but-followup", prompt: "use $ralplan is the workflow command; but use $autopilot build it", expectedSkill: "autopilot" }, { name: "doc-fullwidth-oxford", prompt: "use $ralplan, $autopilot, and $team are workflow commands", expectedSkill: null }, { name: "reference-zero-title", prompt: "[docs]: ./target\n(autopilot mode)", expectedSkill: null }, { name: "doc-also-alias-explicit", prompt: "use $ralplan is the consensus-planning command; $team is also its alias", expectedSkill: null }, { name: "doc-also-alias-implicit", prompt: "use $ralplan is the consensus-planning command\nAutopilot mode is also its alias.", expectedSkill: null }, { name: "doc-embedded-mention", prompt: "use $ralplan is the workflow command; $team appears in the documentation.", expectedSkill: null }, { name: "chained-negation", prompt: "$ralplan; $autopilot is prohibited", expectedSkill: "ralplan", expectedDeferredSkills: [] }, { name: "long-negation", prompt: `$ralplan; $autopilot${" ".repeat(193)}is prohibited`, expectedSkill: "ralplan", expectedDeferredSkills: [] }, { name: "doc-arabic-comma", prompt: "use $ralplan، $autopilot are workflow commands", expectedSkill: null }, { name: "arabic-negation", prompt: "$ralplan، $autopilot are prohibited", expectedSkill: null }, { name: "implicit-arabic-negation", prompt: "Autopilot mode، deep interview are prohibited.", expectedSkill: null }, { name: "fullwidth-frame-reset", prompt: "For instance: manual mode is slower。 Use autopilot mode.", expectedSkill: "autopilot" }, { name: "doc-abbreviation", prompt: "use $ralplan is the workflow command, e.g. use $autopilot in examples.", expectedSkill: null }, { name: "implicit-doc-mention", prompt: "use $ralplan is the workflow command; autopilot mode appears in the documentation.", expectedSkill: null }, { name: "implicit-doc-chain", prompt: "use $ralplan is the workflow command; autopilot mode is its alias; $ultragoal execute it", expectedSkill: "ultragoal", expectedStopBlock: false }, { name: "long-command-gap", prompt: `use $ralplan is the workflow command; use${" ".repeat(161)}$autopilot build it`, expectedSkill: "autopilot" }, { name: "ideographic-negation", prompt: "$ralplan、 $autopilot are prohibited", expectedSkill: null }, { name: "implicit-ideographic-negation", prompt: "Autopilot mode、 deep interview are prohibited.", expectedSkill: null }, { name: "doc-exclamation-followup", prompt: "use $ralplan is the consensus-planning command! run $autopilot", expectedSkill: "autopilot" }, { name: "doc-fullwidth-question-followup", prompt: "use $ralplan is the consensus-planning command? run $autopilot", expectedSkill: "autopilot" }, { name: "implicit-doc-predecessor", prompt: "Autopilot mode is workflow documentation.\n$ultragoal execute it", expectedSkill: "ultragoal", expectedStopBlock: false }, { name: "confusable-use-verb", prompt: "uſe $ralplan plan it", expectedSkill: null }, { name: "confusable-please-prefix", prompt: "pleaſe use $ralplan plan it", expectedSkill: null }, { name: "confusable-prompts-token-then-command", prompt: "/promptſ:architect; use autopilot mode.", expectedSkill: "autopilot" }, { name: "reserved-em-dash-boundary", prompt: "/prompts:architect— use autopilot mode", expectedSkill: null }, { name: "reserved-fullwidth-comma-boundary", prompt: "/prompts:architect, use autopilot mode", expectedSkill: null }, { name: "confusable-implicit-verb", prompt: "Do not use deep interview but uſe autopilot mode.", expectedSkill: null }, { name: "frame-fullwidth-colon", prompt: "For instance: use autopilot mode.", expectedSkill: null }, { name: "frame-fullwidth-comma", prompt: "For instance, use autopilot mode.", expectedSkill: null }, { name: "frame-arabic-comma", prompt: "For instance، use autopilot mode.", expectedSkill: null }, { name: "frame-ideo-comma", prompt: "For instance、 use autopilot mode.", expectedSkill: null }, { name: "doc-explicit-documented", prompt: "use $ralplan is the workflow command; $autopilot is documented in the guide.", expectedSkill: null }, { name: "doc-explicit-described", prompt: "$autopilot is described in the manual.", expectedSkill: null }, { name: "doc-comma-followup", prompt: "use $ralplan is the workflow command, but use $autopilot build it", expectedSkill: "autopilot" }, { name: "doc-fw-comma-followup", prompt: "use $ralplan is the workflow command, but use $autopilot build it", expectedSkill: "autopilot" }, { name: "doc-arabic-followup", prompt: "use $ralplan is the workflow command، but use $autopilot build it", expectedSkill: "autopilot" }, { name: "doc-ideo-followup", prompt: "use $ralplan is the workflow command、 but use $autopilot build it", expectedSkill: "autopilot" }, { name: "neg-arabic-followup", prompt: "Do not run $ralplan، use $autopilot build it", expectedSkill: "autopilot" }, { name: "neg-ideo-followup", prompt: "Do not run $ralplan、 use $autopilot build it", expectedSkill: "autopilot" }, { name: "implicit-doc-prefix-next", prompt: "The docs mention autopilot mode.\n$ralplan plan it", expectedSkill: "ralplan" }, { name: "implicit-doc-prefix-same-line", prompt: "The docs mention autopilot mode; use $ralplan plan it", expectedSkill: "ralplan" }, { name: "implicit-doc-subject-same-line", prompt: "Autopilot mode is workflow documentation; use $ralplan plan it", expectedSkill: "ralplan" }, { name: "compact-explicit-negation", prompt: "$ralplan,$autopilot are prohibited", expectedSkill: null }, { name: "compact-implicit-negation", prompt: "Autopilot mode،deep interview are prohibited.", expectedSkill: null }, { name: "doc-clause-local-prefix", prompt: "$ralplan; $autopilot is documented in the guide.", expectedSkill: "ralplan" }, { name: "doc-chain-described", prompt: "use $ralplan is the workflow command; autopilot mode is documented in the guide; $team execute it", expectedSkill: "team", insideTmux: true }, { name: "doc-chain-workflow", prompt: "use $ralplan is the workflow command; autopilot mode is workflow documentation; use $ultragoal execute it", expectedSkill: "ultragoal", expectedStopBlock: false }, { name: "ref-inline-explicit", prompt: "[docs]: $ralplan\n$autopilot build it", expectedSkill: "autopilot" }, { name: "ref-inline-prompts", prompt: "[docs]: /prompts:architect\n$autopilot build it", expectedSkill: "autopilot" }, { name: "list-fullwidth-explicit-doc", prompt: "- $ralplan: consensus-planning workflow", expectedSkill: null }, { name: "list-fullwidth-implicit-doc", prompt: "- autopilot mode: autonomous workflow command", expectedSkill: null }, { name: "possessive-straight", prompt: "$ralplan's workflow is documented", expectedSkill: null }, { name: "possessive-curly", prompt: "$ralplan’s workflow is documented", expectedSkill: null }, { name: "possessive-fullwidth", prompt: "$ralplan's workflow is documented", expectedSkill: null }, { name: "possessive-prompts", prompt: "/prompts:architect's syntax is documented", expectedSkill: null }, { name: "malformed-prefix-kata", prompt: "$・autopilot mode", expectedSkill: null }, { name: "malformed-prefix-half", prompt: "$・autopilot mode", expectedSkill: null }, { name: "malformed-prefix-arabic", prompt: "$٪autopilot mode", expectedSkill: null }, { name: "malformed-prefix-division", prompt: "$∕autopilot mode", expectedSkill: null }, { name: "doc-but-directive", prompt: "use $autopilot is documented but use $ralplan plan it", expectedSkill: "ralplan" }, { name: "list-directive-fw-colon", prompt: "- use $ralplan: consensus-planning workflow", expectedSkill: null }, { name: "doc-arabic-question", prompt: "use $ralplan is the workflow command؟ run $autopilot", expectedSkill: "autopilot" }, { name: "arabic-semicolon-negation", prompt: "$ralplan؛ $autopilot is prohibited", expectedSkill: "ralplan", expectedDeferredSkills: [] }, { name: "confusable-postposed-transition", prompt: "$ralplan is prohibited but uſe autopilot mode.", expectedSkill: null }, { name: "mixed-negation", prompt: "Autopilot mode and $ralplan are prohibited.", expectedSkill: null }, { name: "both-mixed-negation", prompt: "Both autopilot mode and $ralplan are prohibited.", expectedSkill: null }, { name: "mixed-documentation", prompt: "use $ralplan and autopilot mode are workflow commands", expectedSkill: null }, { name: "prose-doc-no-reopen", prompt: "$ralplan is prohibited because docs use $autopilot.", expectedSkill: null }, { name: "neg-fw-dot-reopen", prompt: "Do not run $ralplan. use $autopilot build it", expectedSkill: "autopilot" }, { name: "neg-greek-q-reopen", prompt: "Do not run $ralplan; use $autopilot build it", expectedSkill: "autopilot" }, { name: "unicode-attached-contrast", prompt: "Do not use deep interview яbut use autopilot mode.", expectedSkill: null }, { name: "prefix-list-followup", prompt: "Do not run $ralplan, $autopilot; use $team execute it", expectedSkill: "team", insideTmux: true }, { name: "mixed-postposed-chain", prompt: "$ralplan, autopilot mode, $team are prohibited.", expectedSkill: null }, { name: "implicit-first-doc-chain", prompt: "Autopilot mode and $ralplan are workflow commands; use $team execute it", expectedSkill: "team", insideTmux: true }, { name: "both-mixed-doc-followup", prompt: "Both autopilot mode and $ralplan are workflow commands; use $team execute it", expectedSkill: "team", insideTmux: true }, { name: "doc-semicolon-preserves-earlier", prompt: "Use autopilot mode; use $ralplan is the workflow command.", expectedSkill: "autopilot" }, { name: "doc-independent-comma", prompt: "Use autopilot mode, and $ralplan is documented in the guide.", expectedSkill: "autopilot" }, { name: "reference-unclosed-quote-destination", prompt: "[docs]: \"target\n$autopilot build it", expectedSkill: null }, { name: "reference-unclosed-inline-destination", prompt: "[docs]: `target\n$autopilot build it", expectedSkill: null }, { name: "mixed-prefix-negation-implicit", prompt: "Do not run $ralplan and use autopilot mode.", expectedSkill: null }, { name: "repeated-postposed-followup", prompt: "$team is prohibited and is forbidden; use $ralplan plan it", expectedSkill: "ralplan" }, { name: "doc-preserves-earlier", prompt: "Use autopilot mode; \"note\"; use $ralplan is the workflow command.", expectedSkill: "autopilot" }, { name: "doc-colon-followup", prompt: "use $ralplan is the workflow command: use $autopilot build it", expectedSkill: "autopilot" }, { name: "table-followup", prompt: "Mode | Meaning\n--- | ---\nmanual | documentation\n$ralplan plan it", expectedSkill: "ralplan" }, { name: "neg-advance-reopen", prompt: "Do not run $ralplan but advance to $ultragoal", expectedSkill: "ultragoal", expectedStopBlock: false, insideTmux: true }, { name: "neg-jump-reopen", prompt: "Do not run $ralplan but jump straight to $ultragoal", expectedSkill: "ultragoal", expectedStopBlock: false, insideTmux: true }, { name: "reference-plain-title", prompt: "[docs]: /target \"title\nplain text\"\n$ralplan plan it", expectedSkill: "ralplan" }, { name: "reference-plain-destination", prompt: "[docs]: ./target\n$ralplan plan it", expectedSkill: "ralplan" }, { name: "directive-use-the", prompt: "Do not run $ralplan; use the $autopilot build it", expectedSkill: "autopilot" }, { name: "directive-continue-after-quote", prompt: "\"quoted\"\ncontinue with $ralplan", expectedSkill: "ralplan" }, { name: "doc-advance-followup", prompt: "use $ralplan is the workflow command; advance to $ultragoal", expectedSkill: "ultragoal", expectedStopBlock: false, insideTmux: true }, { name: "directive-run-analyze", prompt: "run $analyze", expectedSkill: "analyze", expectedStopBlock: false }, { name: "directive-run-code-review", prompt: "run $code-review", expectedSkill: "code-review", expectedStopBlock: false }, { name: "directive-please-use-code-review", prompt: "please use $code-review", expectedSkill: "code-review", expectedStopBlock: false }, { name: "directive-please-run-ralplan", prompt: "please run $ralplan plan this", expectedSkill: "ralplan" }, { name: "directive-start-ralplan", prompt: "start $ralplan plan this", expectedSkill: "ralplan" }, { name: "directive-enable-deep-interview", prompt: "enable $deep-interview", expectedSkill: "deep-interview", expectedStopBlock: false }, { name: "directive-launch-autopilot", prompt: "launch $autopilot", expectedSkill: "autopilot" }, { name: "directive-invoke-ralph", prompt: "invoke $ultragoal", expectedSkill: "ultragoal", expectedStopBlock: false }, { name: "directive-activate-ultrawork", prompt: "activate $ultragoal", expectedSkill: "ultragoal", expectedStopBlock: false }, { name: "directive-resume-ralplan", prompt: "resume $ralplan plan this", expectedSkill: "ralplan" }, { name: "directive-continue-code-review", prompt: "continue $code-review", expectedSkill: "code-review", expectedStopBlock: false }, { name: "directive-documentation", prompt: "use $ralplan is the consensus-planning command", expectedSkill: null }, { name: "g1a-ordered-multi-skill", prompt: "$ralplan, $autopilot; $team", expectedSkill: "ralplan", expectedDeferredSkills: ["autopilot", "team"], expectedActiveSkills: ["ralplan"], expectedActiveDetailSkills: ["ralplan"], insideTmux: true }, { name: "g1c-duplicate-alias", prompt: "$autopilot $oh-my-codex:autopilot build it", expectedSkill: "autopilot", expectedDeferredSkills: [], expectedActiveSkills: ["autopilot"] }, { name: "b3-longer-valid-fence", prompt: "```text\n$ralplan plan it\n````\n$ralplan plan it", expectedSkill: "ralplan" }, { name: "b4-shorter-invalid-fence", prompt: "````text\n$ralplan plan it\n```\n$autopilot build it", expectedSkill: null }, { name: "b5-different-marker-invalid-fence", prompt: "```text\n$ralplan plan it\n~~~\n$autopilot build it", expectedSkill: null }, { name: "directive-non-leading-prose", prompt: "The docs say use $ralplan plan this", expectedSkill: null }, { name: "nested-bounded-child-unbounded-parent", prompt: "\"`x`\n$ralplan plan it", expectedSkill: null }, { name: "first-contiguous-block-terminal", prompt: "$ralplan plan it\n\"x\"\n$autopilot build it", expectedSkill: "ralplan", expectedDeferredSkills: [] }, { name: "leading-reserved-dominance", prompt: "/prompts:architect\n\"x\"\n$ralplan plan it", expectedSkill: null }, { name: "list-fence-root-opener", prompt: "- ```\n sample\n```\n$ralplan plan it", expectedSkill: null }, { name: "list-fence-relative-closer", prompt: "- ```\n sample\n ```\n$ralplan plan it", expectedSkill: "ralplan" }, { name: "reference-multiline-title-explicit", prompt: "[docs]: /target \"title\nuse /prompts:architect\n$ralplan plan it\"", expectedSkill: null }, { name: "reference-multiline-title-implicit", prompt: "[docs]: /target \"title\nuse autopilot mode\"", expectedSkill: null }, { name: "reference-next-line-title", prompt: "[docs]: ./target\n (autopilot mode)", expectedSkill: null }, { name: "reference-next-line-destination-title", prompt: "[docs]:\n ./target\n (autopilot mode)", expectedSkill: null }, { name: "kelvin-case-fold-suffix", prompt: "$ultraworK execute", expectedSkill: null }, { name: "katakana-middle-dot-suffix", prompt: "$ralplan・suffix plan it", expectedSkill: null }, { name: "halfwidth-middle-dot-suffix", prompt: "$ralplan・suffix plan it", expectedSkill: null }, { name: "arabic-percent-suffix", prompt: "$ralplan٪docs", expectedSkill: null }, { name: "division-slash-suffix", prompt: "$ralplan∕config", expectedSkill: null }, { name: "implicit-negative", prompt: "Do not use autopilot mode.", expectedSkill: null }, { name: "implicit-negative-list", prompt: "Do not use deep interview, autopilot mode.", expectedSkill: null }, { name: "implicit-negative-nor", prompt: "Do not use deep interview, nor autopilot mode.", expectedSkill: null }, { name: "implicit-negative-avoid", prompt: "Avoid autopilot mode.", expectedSkill: null }, { name: "implicit-negative-suffix", prompt: "Autopilot mode is not allowed.", expectedSkill: null }, { name: "implicit-negative-contraction", prompt: "Autopilot mode isn't allowed.", expectedSkill: null }, { name: "implicit-negative-prohibited", prompt: "Autopilot mode is prohibited.", expectedSkill: null }, { name: "implicit-no-prefix", prompt: "No autopilot mode.", expectedSkill: null }, { name: "implicit-quoted-doc", prompt: "The docs call this \"autopilot mode\".", expectedSkill: null }, { name: "implicit-fenced", prompt: "```\nautopilot mode\n```", expectedSkill: null }, { name: "implicit-list-fenced", prompt: "- ```\n autopilot mode\n ```", expectedSkill: null }, { name: "implicit-sibling-list-fence", prompt: "- ```\n first example\n- ```\n autopilot mode\n ```", expectedSkill: null }, { name: "implicit-prompts-protocol", prompt: "use /prompts:architect autopilot mode", expectedSkill: null }, { name: "implicit-quoted-prompts-positive", prompt: "Ignore the quoted \"/prompts:architect\" and use autopilot mode.", expectedSkill: "autopilot" }, { name: "implicit-doc-verb", prompt: "This documents autopilot mode.", expectedSkill: null }, { name: "implicit-guide-frame", prompt: "The guide says do not use deep interview but instead use autopilot mode.", expectedSkill: null }, { name: "implicit-markdown-link", prompt: "[autopilot mode](./docs.md)", expectedSkill: null }, { name: "implicit-markdown-heading", prompt: "## Autopilot mode", expectedSkill: null }, { name: "implicit-markdown-table", prompt: "| autopilot mode | workflow command |", expectedSkill: null }, { name: "implicit-dash-documentation", prompt: "Autopilot mode — autonomous workflow command", expectedSkill: null }, { name: "implicit-unicode-adjacency", prompt: "문서autopilot mode한글", expectedSkill: null }, { name: "implicit-korean-negative", prompt: "autopilot mode는 사용하지 마세요", expectedSkill: null }, { name: "implicit-postposed-coordinated-negative", prompt: "Autopilot mode and deep interview are prohibited.", expectedSkill: null }, { name: "implicit-postposed-modal-negative", prompt: "Autopilot mode should be avoided.", expectedSkill: null }, { name: "implicit-example-frame", prompt: "Example: do not use deep interview but instead use autopilot mode.", expectedSkill: null }, { name: "implicit-fullwidth-single-quote", prompt: "'autopilot mode'", expectedSkill: null }, { name: "escaped-plugin-autopilot", prompt: "\\$oh-my-codex:autopilot mode", expectedSkill: null }, { name: "implicit-comma-coordinated-negative", prompt: "Autopilot mode, deep interview, and team are prohibited.", expectedSkill: null }, { name: "implicit-for-example-frame", prompt: "For example, do not use deep interview but instead use autopilot mode.", expectedSkill: null }, { name: "implicit-docs-comma-frame", prompt: "According to the docs, do not use deep interview but instead use autopilot mode.", expectedSkill: null }, { name: "implicit-curly-dont-negative", prompt: "Don’t use autopilot mode.", expectedSkill: null }, { name: "implicit-fullwidth-dont-negative", prompt: "Don't use autopilot mode.", expectedSkill: null }, { name: "implicit-curly-isnt-negative", prompt: "Autopilot mode isn’t allowed.", expectedSkill: null }, { name: "implicit-copular-infinitive-negative", prompt: "Autopilot mode is to be avoided.", expectedSkill: null }, { name: "implicit-past-copular-infinitive-negative", prompt: "Autopilot mode was to be disabled.", expectedSkill: null }, { name: "implicit-article-coordinated-negative", prompt: "Autopilot mode and the deep interview workflow are prohibited.", expectedSkill: null }, { name: "implicit-as-example-frame", prompt: "As an example, do not use deep interview but instead use autopilot mode.", expectedSkill: null }, { name: "implicit-for-instance-frame", prompt: "For instance, do not use deep interview but instead use autopilot mode.", expectedSkill: null }, { name: "implicit-markdown-reference-link", prompt: "[autopilot mode][docs]", expectedSkill: null }, { name: "implicit-plural-coordinated-negative", prompt: "Autopilot mode and deep interview workflows are prohibited.", expectedSkill: null }, { name: "implicit-as-example-governing-frame", prompt: "As an example, ignore the docs and use autopilot mode.", expectedSkill: null }, { name: "implicit-for-instance-governing-frame", prompt: "For instance, ignore the docs and use autopilot mode.", expectedSkill: null }, { name: "implicit-markdown-definition", prompt: "[autopilot mode]: ./docs", expectedSkill: null }, { name: "implicit-markdown-shortcut-label", prompt: "[autopilot mode]", expectedSkill: null }, { name: "implicit-markdown-setext", prompt: "Autopilot mode\n===", expectedSkill: null }, { name: "explicit-markdown-pipe-table", prompt: "$ralplan | workflow\n--- | ---", expectedSkill: null }, { name: "implicit-markdown-table-body", prompt: "Mode | Meaning\n--- | ---\nautopilot mode | autonomous workflow command", expectedSkill: null }, { name: "implicit-as-well-as-negative", prompt: "Autopilot mode as well as deep interview workflows are prohibited.", expectedSkill: null }, { name: "implicit-along-with-negative", prompt: "Autopilot mode along with deep interview workflows are prohibited.", expectedSkill: null }, { name: "implicit-together-with-negative", prompt: "Autopilot mode together with deep interview workflows are prohibited.", expectedSkill: null }, { name: "implicit-ampersand-negative", prompt: "Autopilot mode & deep interview workflows are prohibited.", expectedSkill: null }, { name: "implicit-example-colon-frame", prompt: "As an example: ignore the docs and use autopilot mode.", expectedSkill: null }, { name: "implicit-instance-em-dash-frame", prompt: "For instance — ignore the docs and use autopilot mode.", expectedSkill: null }, { name: "implicit-example-hyphen-frame", prompt: "For example - ignore the docs and use autopilot mode.", expectedSkill: null }, { name: "implicit-instance-colon-frame-no-doc-word", prompt: "For instance: use autopilot mode.", expectedSkill: null }, { name: "implicit-instance-em-dash-frame-no-doc-word", prompt: "For instance — use autopilot mode.", expectedSkill: null }, { name: "implicit-markdown-later-table-body", prompt: "Mode | Meaning\n--- | ---\nmanual | docs\nautopilot mode | autonomous workflow command", expectedSkill: null }, { name: "implicit-embedded-shortcut-definition", prompt: "See [autopilot mode] for details.\n\n[autopilot mode]: ./docs", expectedSkill: null }, { name: "implicit-parenthetical-as-well-negative", prompt: "Autopilot mode, as well as deep interview workflows, are prohibited.", expectedSkill: null }, { name: "implicit-parenthetical-along-negative", prompt: "Autopilot mode, along with deep interview workflows, are prohibited.", expectedSkill: null }, { name: "implicit-parenthetical-together-negative", prompt: "Autopilot mode, together with deep interview workflows, are prohibited.", expectedSkill: null }, { name: "implicit-normalized-shortcut-definition", prompt: "See [autopilot mode] for details.\n\n[autopilot mode]: ./docs", expectedSkill: null }, { name: "implicit-ignore-exclusion", prompt: "Ignore autopilot mode.", expectedSkill: null }, { name: "implicit-skip-exclusion", prompt: "Skip autopilot mode.", expectedSkill: null }, { name: "implicit-exclude-exclusion", prompt: "Exclude autopilot mode.", expectedSkill: null }, { name: "explicit-postposed-prohibited", prompt: "$ralplan is prohibited.", expectedSkill: null }, { name: "explicit-postposed-modal-negative", prompt: "$ralplan should not be run.", expectedSkill: null }, { name: "explicit-postposed-coordinated-negative", prompt: "$ralplan and $autopilot are prohibited.", expectedSkill: null }, { name: "explicit-postposed-article-coordination", prompt: "$ralplan and the $autopilot workflow are prohibited.", expectedSkill: null }, { name: "explicit-postposed-implicit-coordination", prompt: "$ralplan and autopilot mode are prohibited.", expectedSkill: null }, { name: "implicit-blockquote-reference-definition", prompt: "See [autopilot mode] for details.\n\n> [autopilot mode]: ./docs", expectedSkill: null }, { name: "implicit-list-reference-definition", prompt: "See [autopilot mode] for details.\n\n- [autopilot mode]: ./docs", expectedSkill: null }, { name: "implicit-indented-blockquote-reference-definition", prompt: "See [autopilot mode] for details.\n\n> [autopilot mode]: ./docs", expectedSkill: null }, { name: "explicit-list-contained-indented-code", prompt: "- $ralplan", expectedSkill: null }, { name: "implicit-list-contained-indented-code", prompt: "1. autopilot mode", expectedSkill: null }, { name: "implicit-four-space-blockquote-definition", prompt: "See [autopilot mode] for details.\n\n> [autopilot mode]: ./docs", expectedSkill: null }, { name: "implicit-multiline-reference-definition", prompt: "See [autopilot mode] for details.\n\n[autopilot mode]:\n ./docs", expectedSkill: null }, { name: "explicit-list-tab-code-boundary", prompt: "- \t$ralplan", expectedSkill: null }, { name: "implicit-nested-list-contained-code", prompt: "- - autopilot mode", expectedSkill: null }, { name: "implicit-list-nested-blockquote", prompt: "- > autopilot mode", expectedSkill: null }, { name: "implicit-deep-nested-list-contained-code", prompt: "- - - - - - - - - autopilot mode", expectedSkill: null }, { name: "implicit-ordered-list-nested-blockquote", prompt: "1234. > autopilot mode", expectedSkill: null }, { name: "explicit-nested-list-positive", prompt: "- - $ralplan plan it", expectedSkill: "ralplan" }, { name: "explicit-four-digit-ordered-list-positive", prompt: "1234. $ralplan plan it", expectedSkill: "ralplan" }, { name: "explicit-list-fence-then-positive", prompt: "- ```\n $ralplan\n ```\n$autopilot build it", expectedSkill: "autopilot" }, { name: "explicit-nested-list-doc-then-positive", prompt: "- - $ralplan is the consensus-planning command\n$autopilot build it", expectedSkill: "autopilot" }, { name: "explicit-after-closed-blockquote", prompt: "> quoted context\n$ralplan plan it", expectedSkill: "ralplan" }, { name: "explicit-after-closed-fence", prompt: "```text\nquoted context\n```\n$ralplan plan it", expectedSkill: "ralplan" }, { name: "explicit-after-indented-code", prompt: " quoted context\n$ralplan plan it", expectedSkill: "ralplan" }, { name: "explicit-after-closed-quote", prompt: "\"quoted context\"\n$ralplan plan it", expectedSkill: "ralplan" }, { name: "explicit-after-prompts-context", prompt: "Use /prompts:architect.\n$ralplan plan it", expectedSkill: "ralplan" }, { name: "explicit-after-bare-prompts-precedence", prompt: "Prose\n/prompts:architect\n$ralplan plan this", expectedSkill: null }, { name: "explicit-after-unclosed-fence", prompt: "```text\nquoted context\n$ralplan plan it", expectedSkill: null }, { name: "explicit-after-mismatched-fence", prompt: "```text\nquoted context\n~~~\n$ralplan plan it", expectedSkill: null }, { name: "explicit-after-unclosed-quote", prompt: "\"quoted context\n$ralplan plan it", expectedSkill: null }, { name: "explicit-after-blockquote-prose", prompt: "> quoted context\nThe docs mention $ralplan only", expectedSkill: null }, { name: "explicit-after-quote-negative", prompt: "\"quoted context\"\nDo not run $ralplan", expectedSkill: null }, { name: "explicit-stale-predecessor-prose", prompt: "> quoted context\nProse\n$ralplan implement this", expectedSkill: null }, { name: "explicit-stale-predecessor-directive-clause", prompt: "> quoted context\nProse\nUse $ralplan plan this", expectedSkill: null }, { name: "explicit-stale-predecessor-prompts", prompt: "> quoted context\n/prompts:architect\n$ralplan plan this", expectedSkill: null }, { name: "explicit-stale-predecessor-second-block", prompt: "> quoted context\n$ralplan plan it\nLater discussion.\n$autopilot build it", expectedSkill: "ralplan", expectedDeferredSkills: [] }, { name: "explicit-negative-before-unclosed-quote", prompt: "Do not run $ralplan.\n\"unclosed context\n$autopilot build it", expectedSkill: null }, { name: "explicit-reference-before-unclosed-quote", prompt: "[$ralplan]: ./docs\n\"unclosed context\n$autopilot build it", expectedSkill: null }, { name: "explicit-prompts-inside-unclosed-quote", prompt: "\"Use /prompts:architect\n$ralplan plan it", expectedSkill: null }, { name: "explicit-malformed-prompts-suffix", prompt: "/prompts:architect한글\n$ralplan plan it", expectedSkill: null }, { name: "explicit-nested-list-prompts-positive", prompt: "- Use /prompts:architect.\n$ralplan plan it", expectedSkill: "ralplan" }, { name: "explicit-nested-list-fence-positive", prompt: "- - ```\n quoted context\n ```\n$ralplan plan it", expectedSkill: "ralplan" }, { name: "implicit-reference-destination", prompt: "[docs]:\nautopilot", expectedSkill: null }, { name: "explicit-middle-dot-suffix", prompt: "$ralplan·suffix plan it", expectedSkill: null }, { name: "explicit-percent-suffix", prompt: "$ralplan%docs", expectedSkill: null }, { name: "explicit-fullwidth-percent-suffix", prompt: "$ralplan%docs", expectedSkill: null }, { name: "explicit-postposed-also-negative", prompt: "$ralplan is also prohibited.", expectedSkill: null }, { name: "implicit-postposed-still-negative", prompt: "Autopilot mode is still prohibited.", expectedSkill: null }, { name: "implicit-commonmark-case-fold", prompt: "See [ẞ autopilot mode] for details.\n\n[SS autopilot mode]: ./docs", expectedSkill: null }, { name: "implicit-commonmark-escaped-bracket", prompt: "See [foo\\] autopilot mode] for details.\n\n[foo\\] autopilot mode]: ./docs", expectedSkill: null }, { name: "implicit-version-decimal-frame", prompt: "For instance: in version 1.2, use autopilot mode.", expectedSkill: null }, { name: "implicit-abbreviation-frame", prompt: "For instance: e.g. use autopilot mode.", expectedSkill: null }, { name: "implicit-slash-documentation", prompt: "Autopilot mode / deep interview are workflow commands.", expectedSkill: null }, { name: "implicit-low-quote", prompt: "„autopilot mode“", expectedSkill: null }, { name: "slash-list-documentation", prompt: "- $ralplan / $autopilot are workflow commands", expectedSkill: null }, { name: "compact-slash-list-documentation", prompt: "- $ralplan/$autopilot are workflow commands", expectedSkill: null }, { name: "slash-list-composition", prompt: "- $ralplan / $autopilot are workflow commands\n$autopilot execute it", expectedSkill: "autopilot" }, { name: "compact-slash-list-composition", prompt: "- $ralplan/$autopilot are workflow commands\n$autopilot execute it", expectedSkill: "autopilot" }, { name: "but-use-positive", prompt: "Do not run $ralplan but use $autopilot build it", expectedSkill: "autopilot" }, { name: "but-instead-use-positive", prompt: "Do not run $ralplan but instead use $autopilot build it", expectedSkill: "autopilot" }, { name: "em-dash-instead-use-positive", prompt: "Do not run $ralplan — instead use $autopilot build it", expectedSkill: "autopilot" }, { name: "implicit-positive-contrast", prompt: "Do not use deep interview, but use autopilot mode.", expectedSkill: "autopilot" }, { name: "implicit-but-instead-positive", prompt: "Do not use deep interview but instead use autopilot mode.", expectedSkill: "autopilot" }, { name: "implicit-list-verb-positive", prompt: "List files and use autopilot mode.", expectedSkill: "autopilot" }, { name: "implicit-dont-stop-positive", prompt: "No, don't stop.", expectedSkill: null }, { name: "implicit-doc-suffix", prompt: "autopilot mode is workflow documentation.", expectedSkill: null }, { name: "implicit-inline-code", prompt: "`autopilot mode`", expectedSkill: null }, { name: "implicit-blockquote", prompt: "> autopilot mode", expectedSkill: null }, { name: "implicit-doc-then-positive", prompt: "Autopilot mode is workflow documentation.\nUse autopilot mode.", expectedSkill: "autopilot" }, { name: "prompts-then-positive", prompt: "Use /prompts:architect.\nUse autopilot mode.", expectedSkill: "autopilot" }, { name: "mixed-negative-explicit-positive-implicit", prompt: "Do not run $ralplan but instead use autopilot mode.", expectedSkill: "autopilot" }, { name: "mixed-quoted-explicit-positive-implicit", prompt: "Ignore \"$ralplan\" and use autopilot mode.", expectedSkill: "autopilot" }, { name: "escaped-prompts-positive-implicit", prompt: "Ignore \\/prompts:architect and use autopilot mode.", expectedSkill: "autopilot" }, { name: "url-prompts-positive-implicit", prompt: "See https://example.com/prompts:architect and use autopilot mode.", expectedSkill: "autopilot" }, { name: "implicit-positive-modal-used", prompt: "Autopilot mode should be used.", expectedSkill: "autopilot" }, { name: "implicit-positive-modal-enabled", prompt: "Autopilot mode must be enabled.", expectedSkill: "autopilot" }, { name: "implicit-positive-modal-run", prompt: "Autopilot mode can be run.", expectedSkill: "autopilot" }, { name: "implicit-fullwidth-possessive-positive", prompt: "User's request: use autopilot mode.", expectedSkill: "autopilot" }, { name: "markdown-prompts-link-positive", prompt: "See [/prompts:architect](./docs.md) and use autopilot mode.", expectedSkill: "autopilot" }, { name: "implicit-subordinate-positive", prompt: "Use autopilot mode, while deep interview is prohibited.", expectedSkill: "autopilot" }, { name: "docs-sentence-then-positive", prompt: "Read the docs. Use autopilot mode.", expectedSkill: "autopilot" }, { name: "docs-semicolon-then-positive", prompt: "The docs are stale; use autopilot mode.", expectedSkill: "autopilot" }, { name: "docs-comma-directive-positive", prompt: "Ignore the docs, use autopilot mode.", expectedSkill: "autopilot" }, { name: "escaped-prompts-explicit-followup", prompt: "Ignore \\/prompts:architect\n$ralplan plan it", expectedSkill: "ralplan" }, { name: "url-prompts-explicit-followup", prompt: "See https://example.com/prompts:architect\n$ralplan plan it", expectedSkill: "ralplan" }, { name: "link-prompts-explicit-followup", prompt: "See [/prompts:architect](./docs.md)\n$ralplan plan it", expectedSkill: "ralplan" }, { name: "linked-explicit-implicit-positive", prompt: "See [$ralplan](./docs.md) and use autopilot mode.", expectedSkill: "autopilot" }, { name: "url-explicit-implicit-positive", prompt: "See https://example.com/$ralplan and use autopilot mode.", expectedSkill: "autopilot" }, { name: "implicit-coordinated-clause-positive", prompt: "Use autopilot mode, and deep interview is prohibited.", expectedSkill: "autopilot" }, { name: "docs-and-directive-positive", prompt: "Ignore the docs and use autopilot mode.", expectedSkill: "autopilot" }, { name: "escaped-prompts-same-line-explicit", prompt: "Ignore \\/prompts:architect; use $ralplan plan it", expectedSkill: "ralplan" }, { name: "url-prompts-same-line-explicit", prompt: "See https://example.com/prompts:architect; use $ralplan plan it", expectedSkill: "ralplan" }, { name: "link-prompts-same-line-explicit", prompt: "See [/prompts:architect](./docs.md); use $ralplan plan it", expectedSkill: "ralplan" }, { name: "reference-prompts-same-line-explicit", prompt: "See [/prompts:architect][docs]; use $ralplan plan it", expectedSkill: "ralplan" }, { name: "heading-prompts-explicit-followup", prompt: "## /prompts:architect\n$ralplan plan it", expectedSkill: "ralplan" }, { name: "table-prompts-explicit-followup", prompt: "| /prompts:architect |\n$ralplan plan it", expectedSkill: "ralplan" }, { name: "heading-explicit-implicit-positive", prompt: "## $ralplan\nUse autopilot mode.", expectedSkill: "autopilot" }, { name: "table-explicit-implicit-positive", prompt: "| $ralplan |\nUse autopilot mode.", expectedSkill: "autopilot" }, { name: "reference-explicit-implicit-positive", prompt: "See [$ralplan][docs] and use autopilot mode.", expectedSkill: "autopilot" }, { name: "windows-path-explicit-implicit-positive", prompt: "See C:\\docs\\$ralplan and use autopilot mode.", expectedSkill: "autopilot" }, { name: "implicit-fullwidth-plural-possessive-positive", prompt: "Users' request: use autopilot mode.", expectedSkill: "autopilot" }, { name: "implicit-modal-coordinated-clause-positive", prompt: "Use autopilot mode, and deep interview should be avoided.", expectedSkill: "autopilot" }, { name: "docs-but-directive-positive", prompt: "Ignore the docs but use autopilot mode.", expectedSkill: "autopilot" }, { name: "prompts-markdown-definition-explicit-followup", prompt: "[/prompts:architect]: ./docs\n$ralplan plan it", expectedSkill: "ralplan" }, { name: "prompts-markdown-shortcut-explicit-followup", prompt: "[/prompts:architect]\n$ralplan plan it", expectedSkill: "ralplan" }, { name: "explicit-markdown-definition-implicit-positive", prompt: "[$ralplan]: ./docs\nUse autopilot mode.", expectedSkill: "autopilot" }, { name: "explicit-markdown-shortcut-implicit-positive", prompt: "[$ralplan]\nUse autopilot mode.", expectedSkill: "autopilot" }, { name: "explicit-markdown-setext-implicit-positive", prompt: "$ralplan\n===\nUse autopilot mode.", expectedSkill: "autopilot" }, { name: "explicit-markdown-pipe-table-implicit-positive", prompt: "$ralplan | workflow\n--- | ---\nUse autopilot mode.", expectedSkill: "autopilot" }, { name: "heading-explicit-later-explicit", prompt: "## $ralplan\n$autopilot build it", expectedSkill: "autopilot" }, { name: "linked-explicit-later-explicit", prompt: "See [$ralplan](./docs.md); use $autopilot build it", expectedSkill: "autopilot" }, { name: "windows-path-explicit-later-explicit", prompt: "See C:\\docs\\$ralplan; use $autopilot build it", expectedSkill: "autopilot" }, { name: "table-body-explicit-later-explicit", prompt: "Mode | Meaning\n--- | ---\n$ralplan | planning\n$autopilot build it", expectedSkill: "autopilot" }, { name: "embedded-shortcut-explicit-implicit-positive", prompt: "See [$ralplan] for details.\n\n[$ralplan]: ./docs\nUse autopilot mode.", expectedSkill: "autopilot" }, { name: "quoted-explicit-later-explicit", prompt: "Ignore \"$ralplan\" and use $autopilot build it", expectedSkill: "autopilot" }, { name: "inline-code-explicit-later-explicit", prompt: "Ignore `$ralplan` and use $autopilot build it", expectedSkill: "autopilot" }, { name: "normalized-shortcut-explicit-implicit-positive", prompt: "See [$ralplan ] for details.\n\n[$ralplan]: ./docs\nUse autopilot mode.", expectedSkill: "autopilot" }, { name: "intro-frame-sentence-reset-positive", prompt: "For instance: manual mode is slower. Use autopilot mode.", expectedSkill: "autopilot" }, { name: "inline-link-overlap-later-explicit", prompt: "See [`$ralplan`](./docs.md) and use $autopilot build it", expectedSkill: "autopilot" }, { name: "implicit-exclusion-later-positive", prompt: "Ignore deep interview and use autopilot mode.", expectedSkill: "autopilot" }, { name: "link-destination-later-explicit", prompt: "See [docs](https://example.com/$ralplan) and use $autopilot build it", expectedSkill: "autopilot" }, { name: "link-title-later-explicit", prompt: "See [docs](./docs.md \"$ralplan reference\") and use $autopilot build it", expectedSkill: "autopilot" }, { name: "nested-destination-later-explicit", prompt: "See [docs](https://example.com/(v1)/$ralplan) and use $autopilot build it", expectedSkill: "autopilot" }, { name: "nested-link-text-later-explicit", prompt: "See [$ralplan](https://example.com/(v1)) and use $autopilot build it", expectedSkill: "autopilot" }, { name: "quoted-title-parenthesis-later-explicit", prompt: "See [docs](./docs.md \"$ralplan (reference\") and use $autopilot build it", expectedSkill: "autopilot" }, { name: "blockquote-code-definition-boundary", prompt: "Use autopilot mode.\n\n> [autopilot mode]: ./docs", expectedSkill: "autopilot" }, { name: "explicit-list-tab-content-boundary", prompt: "-\t $ralplan", expectedSkill: "ralplan" }, { name: "postposed-negative-but-later-explicit", prompt: "$ralplan is prohibited but use $autopilot build it", expectedSkill: "autopilot" }, { name: "postposed-negative-and-later-explicit", prompt: "$ralplan is prohibited and use $autopilot build it", expectedSkill: "autopilot" }, { name: "later-positive", prompt: "Do not run $ralplan, instead $autopilot build it", expectedSkill: "autopilot" }, { name: "list-documentation", prompt: "- $ralplan, $autopilot are workflow commands", expectedSkill: null }, { name: "list-composition", prompt: "- $ralplan is the consensus-planning command\n$autopilot build it", expectedSkill: "autopilot" }, { name: "escaped-quote", prompt: "\"$ralplan \\\"; $autopilot build it", expectedSkill: null }, { name: "fence-prefix", prompt: "```\n$ralplan\n> ```\n$autopilot build it", expectedSkill: null }, { name: "matching-fence-control", prompt: "> ```\n> $ralplan\n> ```\n$autopilot build it", expectedSkill: "autopilot" }, ] as const; for (const testCase of cases) { const cwd = await mkdtemp(join(tmpdir(), `omx-native-raw-classification-${testCase.name}-`)); const sessionId = `sess-raw-${testCase.name}`; const previousTmux = process.env.TMUX; const previousTmuxPane = process.env.TMUX_PANE; const previousTeamMode = process.env.OMX_TEAM_MODE; if ("insideTmux" in testCase && testCase.insideTmux) { process.env.TMUX = "/tmp/tmux-pr3140-regression"; process.env.TMUX_PANE = "%3140"; process.env.OMX_TEAM_MODE = "enabled"; } else { delete process.env.TMUX; delete process.env.TMUX_PANE; delete process.env.OMX_TEAM_MODE; } try { const submit = await dispatchCodexNativeHook({ hook_event_name: "UserPromptSubmit", cwd, source: "codex-app", session_id: sessionId, thread_id: `thread-${testCase.name}`, turn_id: `turn-${testCase.name}`, prompt: testCase.prompt, }, { cwd }); assert.equal((submit.outputJson as { continue?: boolean } | null)?.continue, undefined, testCase.name); const sessionDir = join(cwd, ".omx", "state", "sessions", sessionId); const skillStatePath = join(sessionDir, "skill-active-state.json"); if (testCase.expectedSkill === null) { // No activation. A RETIRED token additionally leaves a refusal record - active false with no // active skills - which is a diagnostic rather than an activation, so assert that nothing was // activated instead of that no file exists. if (existsSync(skillStatePath)) { const refused = JSON.parse(await readFile(skillStatePath, "utf-8")) as { active?: boolean; active_skills?: unknown[] }; assert.equal(refused.active, false, testCase.name); assert.deepEqual(refused.active_skills ?? [], [], testCase.name); } } else { const skillState = JSON.parse(await readFile(skillStatePath, "utf-8")) as { active?: boolean; skill?: string; phase?: string; error?: string; deferred_skills?: string[]; active_skills?: Array<{ skill?: string }> }; assert.equal(skillState.active, true, testCase.name); assert.equal(skillState.skill, testCase.expectedSkill, testCase.name); if ("expectedDeferredSkills" in testCase) { assert.deepEqual(skillState.deferred_skills ?? [], testCase.expectedDeferredSkills, testCase.name); } if ("expectedActiveSkills" in testCase) { assert.deepEqual(skillState.active_skills?.map((entry) => entry.skill) ?? [], testCase.expectedActiveSkills, testCase.name); } if ("expectedActiveDetailSkills" in testCase) { const activeDetailSkills = (await Promise.all(["ralplan", "autopilot"].map(async (skill) => { const detailPath = join(sessionDir, `${skill}-state.json`); if (!existsSync(detailPath)) return null; const detailState = JSON.parse(await readFile(detailPath, "utf-8")) as { active?: boolean }; return detailState.active ? skill : null; }))).filter((skill): skill is string => skill !== null); assert.deepEqual(activeDetailSkills, testCase.expectedActiveDetailSkills, testCase.name); } } const stop = await dispatchCodexNativeHook({ hook_event_name: "Stop", cwd, source: "codex-app", session_id: sessionId, thread_id: `thread-${testCase.name}`, turn_id: `stop-${testCase.name}`, }, { cwd }); if (testCase.expectedSkill === null) { assert.equal(stop.outputJson, null, testCase.name); } else if ("expectedStopBlock" in testCase && !testCase.expectedStopBlock) { assert.notEqual((stop.outputJson as { decision?: string } | null)?.decision, "block", testCase.name); } else assert.equal((stop.outputJson as { decision?: string } | null)?.decision, "block", testCase.name); } finally { if (previousTmux === undefined) delete process.env.TMUX; else process.env.TMUX = previousTmux; if (previousTmuxPane === undefined) delete process.env.TMUX_PANE; else process.env.TMUX_PANE = previousTmuxPane; if (previousTeamMode === undefined) delete process.env.OMX_TEAM_MODE; else process.env.OMX_TEAM_MODE = previousTeamMode; await rm(cwd, { recursive: true, force: true }); } } }); it("preserves exact structural prompt classification through compiled UserPromptSubmit state and Stop", async () => { const cases = [ { name: "mixed-indent", prompt: " \t$ralplan plan this", expectedSkill: null }, { name: "at-suffix", prompt: "$ralplan@docs", expectedSkill: null }, { name: "hash-suffix", prompt: "$ralplan#docs", expectedSkill: null }, { name: "equals-suffix", prompt: "$ralplan=docs", expectedSkill: null }, { name: "fullwidth-at-suffix", prompt: "$ralplan@docs", expectedSkill: null }, { name: "fullwidth-hash-suffix", prompt: "$ralplan#docs", expectedSkill: null }, { name: "fullwidth-equals-suffix", prompt: "$ralplan=docs", expectedSkill: null }, { name: "directive-use-ralplan", prompt: "use $ralplan plan this", expectedSkill: "ralplan" }, { name: "directive-please-use-ralplan", prompt: "please use $ralplan plan this", expectedSkill: "ralplan" }, { name: "directive-run-ralplan", prompt: "run $ralplan plan this", expectedSkill: "ralplan" }, { name: "directive-list-use-ralplan", prompt: "- use $ralplan plan this", expectedSkill: "ralplan" }, { name: "directive-documentation-then-command", prompt: "use $ralplan is the consensus-planning command\n$autopilot build it", expectedSkill: "autopilot" }, { name: "directive-documentation-then-implicit-command", prompt: "use $ralplan is the consensus-planning command\nUse autopilot mode.", expectedSkill: "autopilot" }, { name: "directive-documentation-trailing-prose-then-command", prompt: "use $ralplan is the workflow command for planning\n$autopilot build it", expectedSkill: "autopilot" }, { name: "directive-documentation-implicit-prose", prompt: "use $ralplan is the workflow command for autopilot mode", expectedSkill: null }, { name: "directive-documentation-alias-prose", prompt: "use $ralplan is the consensus-planning command\nAutopilot mode is its alias.", expectedSkill: null }, { name: "directive-coordinated-documentation-then-command", prompt: "- use $ralplan and $autopilot are workflow commands\n$ultragoal execute it", expectedSkill: "ultragoal", expectedStopBlock: false }, { name: "directive-documentation-semicolon-directive", prompt: "use $ralplan is the consensus-planning command; use $autopilot build it", expectedSkill: "autopilot" }, { name: "directive-two-documentation-blocks", prompt: "use $ralplan is the consensus-planning command\nuse $autopilot is the autonomous workflow command\n$ultragoal execute it", expectedSkill: "ultragoal", expectedStopBlock: false }, { name: "directive-documentation-embedded-token-then-command", prompt: "use $ralplan is the workflow command for $team\n$autopilot build it", expectedSkill: "autopilot" }, { name: "directive-documentation-task-noun-followup", prompt: "use $ralplan is the workflow command; use $autopilot update the documentation", expectedSkill: "autopilot" }, { name: "directive-documentation-implicit-semicolon-followup", prompt: "use $ralplan is the consensus-planning command; use autopilot mode.", expectedSkill: "autopilot" }, { name: "directive-documentation-transition-followup", prompt: "use $ralplan is the consensus-planning command; then use $autopilot build it", expectedSkill: "autopilot" }, { name: "directive-documentation-explicit-alias", prompt: "use $ralplan is the consensus-planning command; $team is its alias", expectedSkill: null }, { name: "directive-documentation-implicit-chain", prompt: "use $ralplan is the consensus-planning command\nAutopilot mode is its alias.\n$ultragoal execute it", expectedSkill: "ultragoal", expectedStopBlock: false }, { name: "directive-documentation-fullwidth-separator", prompt: "use $ralplan,$autopilot are workflow commands", expectedSkill: null }, { name: "directive-documentation-compact-slash", prompt: "use $ralplan/$autopilot are workflow commands\n$ultragoal execute it", expectedSkill: "ultragoal", expectedStopBlock: false }, { name: "reference-prompts-title-then-command", prompt: "[docs]: /target \"title\nUse /prompts:architect\"\n$ultragoal execute it", expectedSkill: "ultragoal", expectedStopBlock: false }, { name: "doc-period-implicit", prompt: "use $ralplan is the consensus-planning command. Use autopilot mode.", expectedSkill: "autopilot" }, { name: "doc-bare-implicit", prompt: "use $ralplan is the consensus-planning command; autopilot mode.", expectedSkill: "autopilot" }, { name: "doc-fullwidth-semicolon", prompt: "use $ralplan is the consensus-planning command; use $autopilot build it", expectedSkill: "autopilot" }, { name: "doc-but-followup", prompt: "use $ralplan is the workflow command; but use $autopilot build it", expectedSkill: "autopilot" }, { name: "doc-fullwidth-oxford", prompt: "use $ralplan, $autopilot, and $team are workflow commands", expectedSkill: null }, { name: "reference-zero-title", prompt: "[docs]: ./target\n(autopilot mode)", expectedSkill: null }, { name: "doc-also-alias-explicit", prompt: "use $ralplan is the consensus-planning command; $team is also its alias", expectedSkill: null }, { name: "doc-also-alias-implicit", prompt: "use $ralplan is the consensus-planning command\nAutopilot mode is also its alias.", expectedSkill: null }, { name: "doc-embedded-mention", prompt: "use $ralplan is the workflow command; $team appears in the documentation.", expectedSkill: null }, { name: "chained-negation", prompt: "$ralplan; $autopilot is prohibited", expectedSkill: "ralplan", expectedDeferredSkills: [] }, { name: "long-negation", prompt: `$ralplan; $autopilot${" ".repeat(193)}is prohibited`, expectedSkill: "ralplan", expectedDeferredSkills: [] }, { name: "doc-arabic-comma", prompt: "use $ralplan، $autopilot are workflow commands", expectedSkill: null }, { name: "arabic-negation", prompt: "$ralplan، $autopilot are prohibited", expectedSkill: null }, { name: "implicit-arabic-negation", prompt: "Autopilot mode، deep interview are prohibited.", expectedSkill: null }, { name: "fullwidth-frame-reset", prompt: "For instance: manual mode is slower。 Use autopilot mode.", expectedSkill: "autopilot" }, { name: "doc-abbreviation", prompt: "use $ralplan is the workflow command, e.g. use $autopilot in examples.", expectedSkill: null }, { name: "implicit-doc-mention", prompt: "use $ralplan is the workflow command; autopilot mode appears in the documentation.", expectedSkill: null }, { name: "implicit-doc-chain", prompt: "use $ralplan is the workflow command; autopilot mode is its alias; $ultragoal execute it", expectedSkill: "ultragoal", expectedStopBlock: false }, { name: "long-command-gap", prompt: `use $ralplan is the workflow command; use${" ".repeat(161)}$autopilot build it`, expectedSkill: "autopilot" }, { name: "ideographic-negation", prompt: "$ralplan、 $autopilot are prohibited", expectedSkill: null }, { name: "implicit-ideographic-negation", prompt: "Autopilot mode、 deep interview are prohibited.", expectedSkill: null }, { name: "doc-exclamation-followup", prompt: "use $ralplan is the consensus-planning command! run $autopilot", expectedSkill: "autopilot" }, { name: "doc-fullwidth-question-followup", prompt: "use $ralplan is the consensus-planning command? run $autopilot", expectedSkill: "autopilot" }, { name: "implicit-doc-predecessor", prompt: "Autopilot mode is workflow documentation.\n$ultragoal execute it", expectedSkill: "ultragoal", expectedStopBlock: false }, { name: "confusable-use-verb", prompt: "uſe $ralplan plan it", expectedSkill: null }, { name: "confusable-please-prefix", prompt: "pleaſe use $ralplan plan it", expectedSkill: null }, { name: "confusable-prompts-token-then-command", prompt: "/promptſ:architect; use autopilot mode.", expectedSkill: "autopilot" }, { name: "reserved-em-dash-boundary", prompt: "/prompts:architect— use autopilot mode", expectedSkill: null }, { name: "reserved-fullwidth-comma-boundary", prompt: "/prompts:architect, use autopilot mode", expectedSkill: null }, { name: "confusable-implicit-verb", prompt: "Do not use deep interview but uſe autopilot mode.", expectedSkill: null }, { name: "frame-fullwidth-colon", prompt: "For instance: use autopilot mode.", expectedSkill: null }, { name: "frame-fullwidth-comma", prompt: "For instance, use autopilot mode.", expectedSkill: null }, { name: "frame-arabic-comma", prompt: "For instance، use autopilot mode.", expectedSkill: null }, { name: "frame-ideo-comma", prompt: "For instance、 use autopilot mode.", expectedSkill: null }, { name: "doc-explicit-documented", prompt: "use $ralplan is the workflow command; $autopilot is documented in the guide.", expectedSkill: null }, { name: "doc-explicit-described", prompt: "$autopilot is described in the manual.", expectedSkill: null }, { name: "doc-comma-followup", prompt: "use $ralplan is the workflow command, but use $autopilot build it", expectedSkill: "autopilot" }, { name: "doc-fw-comma-followup", prompt: "use $ralplan is the workflow command, but use $autopilot build it", expectedSkill: "autopilot" }, { name: "doc-arabic-followup", prompt: "use $ralplan is the workflow command، but use $autopilot build it", expectedSkill: "autopilot" }, { name: "doc-ideo-followup", prompt: "use $ralplan is the workflow command、 but use $autopilot build it", expectedSkill: "autopilot" }, { name: "neg-arabic-followup", prompt: "Do not run $ralplan، use $autopilot build it", expectedSkill: "autopilot" }, { name: "neg-ideo-followup", prompt: "Do not run $ralplan、 use $autopilot build it", expectedSkill: "autopilot" }, { name: "implicit-doc-prefix-next", prompt: "The docs mention autopilot mode.\n$ralplan plan it", expectedSkill: "ralplan" }, { name: "implicit-doc-prefix-same-line", prompt: "The docs mention autopilot mode; use $ralplan plan it", expectedSkill: "ralplan" }, { name: "implicit-doc-subject-same-line", prompt: "Autopilot mode is workflow documentation; use $ralplan plan it", expectedSkill: "ralplan" }, { name: "compact-explicit-negation", prompt: "$ralplan,$autopilot are prohibited", expectedSkill: null }, { name: "compact-implicit-negation", prompt: "Autopilot mode،deep interview are prohibited.", expectedSkill: null }, { name: "doc-clause-local-prefix", prompt: "$ralplan; $autopilot is documented in the guide.", expectedSkill: "ralplan" }, { name: "doc-chain-described", prompt: "use $ralplan is the workflow command; autopilot mode is documented in the guide; $team execute it", expectedSkill: "team", insideTmux: true }, { name: "doc-chain-workflow", prompt: "use $ralplan is the workflow command; autopilot mode is workflow documentation; use $ultragoal execute it", expectedSkill: "ultragoal", expectedStopBlock: false }, { name: "ref-inline-explicit", prompt: "[docs]: $ralplan\n$autopilot build it", expectedSkill: "autopilot" }, { name: "ref-inline-prompts", prompt: "[docs]: /prompts:architect\n$autopilot build it", expectedSkill: "autopilot" }, { name: "list-fullwidth-explicit-doc", prompt: "- $ralplan: consensus-planning workflow", expectedSkill: null }, { name: "list-fullwidth-implicit-doc", prompt: "- autopilot mode: autonomous workflow command", expectedSkill: null }, { name: "possessive-straight", prompt: "$ralplan's workflow is documented", expectedSkill: null }, { name: "possessive-curly", prompt: "$ralplan’s workflow is documented", expectedSkill: null }, { name: "possessive-fullwidth", prompt: "$ralplan's workflow is documented", expectedSkill: null }, { name: "possessive-prompts", prompt: "/prompts:architect's syntax is documented", expectedSkill: null }, { name: "malformed-prefix-kata", prompt: "$・autopilot mode", expectedSkill: null }, { name: "malformed-prefix-half", prompt: "$・autopilot mode", expectedSkill: null }, { name: "malformed-prefix-arabic", prompt: "$٪autopilot mode", expectedSkill: null }, { name: "malformed-prefix-division", prompt: "$∕autopilot mode", expectedSkill: null }, { name: "doc-but-directive", prompt: "use $autopilot is documented but use $ralplan plan it", expectedSkill: "ralplan" }, { name: "list-directive-fw-colon", prompt: "- use $ralplan: consensus-planning workflow", expectedSkill: null }, { name: "doc-arabic-question", prompt: "use $ralplan is the workflow command؟ run $autopilot", expectedSkill: "autopilot" }, { name: "arabic-semicolon-negation", prompt: "$ralplan؛ $autopilot is prohibited", expectedSkill: "ralplan", expectedDeferredSkills: [] }, { name: "confusable-postposed-transition", prompt: "$ralplan is prohibited but uſe autopilot mode.", expectedSkill: null }, { name: "mixed-negation", prompt: "Autopilot mode and $ralplan are prohibited.", expectedSkill: null }, { name: "both-mixed-negation", prompt: "Both autopilot mode and $ralplan are prohibited.", expectedSkill: null }, { name: "mixed-documentation", prompt: "use $ralplan and autopilot mode are workflow commands", expectedSkill: null }, { name: "prose-doc-no-reopen", prompt: "$ralplan is prohibited because docs use $autopilot.", expectedSkill: null }, { name: "neg-fw-dot-reopen", prompt: "Do not run $ralplan. use $autopilot build it", expectedSkill: "autopilot" }, { name: "neg-greek-q-reopen", prompt: "Do not run $ralplan; use $autopilot build it", expectedSkill: "autopilot" }, { name: "unicode-attached-contrast", prompt: "Do not use deep interview яbut use autopilot mode.", expectedSkill: null }, { name: "prefix-list-followup", prompt: "Do not run $ralplan, $autopilot; use $team execute it", expectedSkill: "team", insideTmux: true }, { name: "mixed-postposed-chain", prompt: "$ralplan, autopilot mode, $team are prohibited.", expectedSkill: null }, { name: "implicit-first-doc-chain", prompt: "Autopilot mode and $ralplan are workflow commands; use $team execute it", expectedSkill: "team", insideTmux: true }, { name: "both-mixed-doc-followup", prompt: "Both autopilot mode and $ralplan are workflow commands; use $team execute it", expectedSkill: "team", insideTmux: true }, { name: "doc-semicolon-preserves-earlier", prompt: "Use autopilot mode; use $ralplan is the workflow command.", expectedSkill: "autopilot" }, { name: "doc-independent-comma", prompt: "Use autopilot mode, and $ralplan is documented in the guide.", expectedSkill: "autopilot" }, { name: "reference-unclosed-quote-destination", prompt: "[docs]: \"target\n$autopilot build it", expectedSkill: null }, { name: "reference-unclosed-inline-destination", prompt: "[docs]: `target\n$autopilot build it", expectedSkill: null }, { name: "mixed-prefix-negation-implicit", prompt: "Do not run $ralplan and use autopilot mode.", expectedSkill: null }, { name: "repeated-postposed-followup", prompt: "$team is prohibited and is forbidden; use $ralplan plan it", expectedSkill: "ralplan" }, { name: "doc-preserves-earlier", prompt: "Use autopilot mode; \"note\"; use $ralplan is the workflow command.", expectedSkill: "autopilot" }, { name: "doc-colon-followup", prompt: "use $ralplan is the workflow command: use $autopilot build it", expectedSkill: "autopilot" }, { name: "table-followup", prompt: "Mode | Meaning\n--- | ---\nmanual | documentation\n$ralplan plan it", expectedSkill: "ralplan" }, { name: "neg-advance-reopen", prompt: "Do not run $ralplan but advance to $ultragoal", expectedSkill: "ultragoal", expectedStopBlock: false, insideTmux: true }, { name: "neg-jump-reopen", prompt: "Do not run $ralplan but jump straight to $ultragoal", expectedSkill: "ultragoal", expectedStopBlock: false, insideTmux: true }, { name: "reference-plain-title", prompt: "[docs]: /target \"title\nplain text\"\n$ralplan plan it", expectedSkill: "ralplan" }, { name: "reference-plain-destination", prompt: "[docs]: ./target\n$ralplan plan it", expectedSkill: "ralplan" }, { name: "directive-use-the", prompt: "Do not run $ralplan; use the $autopilot build it", expectedSkill: "autopilot" }, { name: "directive-continue-after-quote", prompt: "\"quoted\"\ncontinue with $ralplan", expectedSkill: "ralplan" }, { name: "doc-advance-followup", prompt: "use $ralplan is the workflow command; advance to $ultragoal", expectedSkill: "ultragoal", expectedStopBlock: false, insideTmux: true }, { name: "directive-run-analyze", prompt: "run $analyze", expectedSkill: "analyze", expectedStopBlock: false }, { name: "directive-run-code-review", prompt: "run $code-review", expectedSkill: "code-review", expectedStopBlock: false }, { name: "directive-please-use-code-review", prompt: "please use $code-review", expectedSkill: "code-review", expectedStopBlock: false }, { name: "directive-please-run-ralplan", prompt: "please run $ralplan plan this", expectedSkill: "ralplan" }, { name: "directive-start-ralplan", prompt: "start $ralplan plan this", expectedSkill: "ralplan" }, { name: "directive-enable-deep-interview", prompt: "enable $deep-interview", expectedSkill: "deep-interview", expectedStopBlock: false }, { name: "directive-launch-autopilot", prompt: "launch $autopilot", expectedSkill: "autopilot" }, { name: "directive-invoke-ralph", prompt: "invoke $ultragoal", expectedSkill: "ultragoal", expectedStopBlock: false }, { name: "directive-activate-ultrawork", prompt: "activate $ultragoal", expectedSkill: "ultragoal", expectedStopBlock: false }, { name: "directive-resume-ralplan", prompt: "resume $ralplan plan this", expectedSkill: "ralplan" }, { name: "directive-continue-code-review", prompt: "continue $code-review", expectedSkill: "code-review", expectedStopBlock: false }, { name: "directive-documentation", prompt: "use $ralplan is the consensus-planning command", expectedSkill: null }, { name: "g1a-ordered-multi-skill", prompt: "$ralplan, $autopilot; $team", expectedSkill: "ralplan", expectedDeferredSkills: ["autopilot", "team"], expectedActiveSkills: ["ralplan"], expectedActiveDetailSkills: ["ralplan"], insideTmux: true }, { name: "g1c-duplicate-alias", prompt: "$autopilot $oh-my-codex:autopilot build it", expectedSkill: "autopilot", expectedDeferredSkills: [], expectedActiveSkills: ["autopilot"] }, { name: "b3-longer-valid-fence", prompt: "```text\n$ralplan plan it\n````\n$ralplan plan it", expectedSkill: "ralplan" }, { name: "b4-shorter-invalid-fence", prompt: "````text\n$ralplan plan it\n```\n$autopilot build it", expectedSkill: null }, { name: "b5-different-marker-invalid-fence", prompt: "```text\n$ralplan plan it\n~~~\n$autopilot build it", expectedSkill: null }, { name: "directive-non-leading-prose", prompt: "The docs say use $ralplan plan this", expectedSkill: null }, { name: "nested-bounded-child-unbounded-parent", prompt: "\"`x`\n$ralplan plan it", expectedSkill: null }, { name: "first-contiguous-block-terminal", prompt: "$ralplan plan it\n\"x\"\n$autopilot build it", expectedSkill: "ralplan", expectedDeferredSkills: [] }, { name: "leading-reserved-dominance", prompt: "/prompts:architect\n\"x\"\n$ralplan plan it", expectedSkill: null }, { name: "list-fence-root-opener", prompt: "- ```\n sample\n```\n$ralplan plan it", expectedSkill: null }, { name: "list-fence-relative-closer", prompt: "- ```\n sample\n ```\n$ralplan plan it", expectedSkill: "ralplan" }, { name: "reference-multiline-title-explicit", prompt: "[docs]: /target \"title\nuse /prompts:architect\n$ralplan plan it\"", expectedSkill: null }, { name: "reference-multiline-title-implicit", prompt: "[docs]: /target \"title\nuse autopilot mode\"", expectedSkill: null }, { name: "reference-next-line-title", prompt: "[docs]: ./target\n (autopilot mode)", expectedSkill: null }, { name: "reference-next-line-destination-title", prompt: "[docs]:\n ./target\n (autopilot mode)", expectedSkill: null }, { name: "kelvin-case-fold-suffix", prompt: "$ultraworK execute", expectedSkill: null }, { name: "katakana-middle-dot-suffix", prompt: "$ralplan・suffix plan it", expectedSkill: null }, { name: "halfwidth-middle-dot-suffix", prompt: "$ralplan・suffix plan it", expectedSkill: null }, { name: "arabic-percent-suffix", prompt: "$ralplan٪docs", expectedSkill: null }, { name: "division-slash-suffix", prompt: "$ralplan∕config", expectedSkill: null }, { name: "implicit-negative", prompt: "Do not use autopilot mode.", expectedSkill: null }, { name: "implicit-negative-list", prompt: "Do not use deep interview, autopilot mode.", expectedSkill: null }, { name: "implicit-negative-nor", prompt: "Do not use deep interview, nor autopilot mode.", expectedSkill: null }, { name: "implicit-negative-avoid", prompt: "Avoid autopilot mode.", expectedSkill: null }, { name: "implicit-negative-suffix", prompt: "Autopilot mode is not allowed.", expectedSkill: null }, { name: "implicit-negative-contraction", prompt: "Autopilot mode isn't allowed.", expectedSkill: null }, { name: "implicit-negative-prohibited", prompt: "Autopilot mode is prohibited.", expectedSkill: null }, { name: "implicit-no-prefix", prompt: "No autopilot mode.", expectedSkill: null }, { name: "implicit-quoted-doc", prompt: "The docs call this \"autopilot mode\".", expectedSkill: null }, { name: "implicit-fenced", prompt: "```\nautopilot mode\n```", expectedSkill: null }, { name: "implicit-list-fenced", prompt: "- ```\n autopilot mode\n ```", expectedSkill: null }, { name: "implicit-sibling-list-fence", prompt: "- ```\n first example\n- ```\n autopilot mode\n ```", expectedSkill: null }, { name: "implicit-prompts-protocol", prompt: "use /prompts:architect autopilot mode", expectedSkill: null }, { name: "implicit-quoted-prompts-positive", prompt: "Ignore the quoted \"/prompts:architect\" and use autopilot mode.", expectedSkill: "autopilot" }, { name: "implicit-doc-verb", prompt: "This documents autopilot mode.", expectedSkill: null }, { name: "implicit-guide-frame", prompt: "The guide says do not use deep interview but instead use autopilot mode.", expectedSkill: null }, { name: "implicit-markdown-link", prompt: "[autopilot mode](./docs.md)", expectedSkill: null }, { name: "implicit-markdown-heading", prompt: "## Autopilot mode", expectedSkill: null }, { name: "implicit-markdown-table", prompt: "| autopilot mode | workflow command |", expectedSkill: null }, { name: "implicit-dash-documentation", prompt: "Autopilot mode — autonomous workflow command", expectedSkill: null }, { name: "implicit-unicode-adjacency", prompt: "문서autopilot mode한글", expectedSkill: null }, { name: "implicit-korean-negative", prompt: "autopilot mode는 사용하지 마세요", expectedSkill: null }, { name: "implicit-postposed-coordinated-negative", prompt: "Autopilot mode and deep interview are prohibited.", expectedSkill: null }, { name: "implicit-postposed-modal-negative", prompt: "Autopilot mode should be avoided.", expectedSkill: null }, { name: "implicit-example-frame", prompt: "Example: do not use deep interview but instead use autopilot mode.", expectedSkill: null }, { name: "implicit-fullwidth-single-quote", prompt: "'autopilot mode'", expectedSkill: null }, { name: "escaped-plugin-autopilot", prompt: "\\$oh-my-codex:autopilot mode", expectedSkill: null }, { name: "implicit-comma-coordinated-negative", prompt: "Autopilot mode, deep interview, and team are prohibited.", expectedSkill: null }, { name: "implicit-for-example-frame", prompt: "For example, do not use deep interview but instead use autopilot mode.", expectedSkill: null }, { name: "implicit-docs-comma-frame", prompt: "According to the docs, do not use deep interview but instead use autopilot mode.", expectedSkill: null }, { name: "implicit-curly-dont-negative", prompt: "Don’t use autopilot mode.", expectedSkill: null }, { name: "implicit-fullwidth-dont-negative", prompt: "Don't use autopilot mode.", expectedSkill: null }, { name: "implicit-curly-isnt-negative", prompt: "Autopilot mode isn’t allowed.", expectedSkill: null }, { name: "implicit-copular-infinitive-negative", prompt: "Autopilot mode is to be avoided.", expectedSkill: null }, { name: "implicit-past-copular-infinitive-negative", prompt: "Autopilot mode was to be disabled.", expectedSkill: null }, { name: "implicit-article-coordinated-negative", prompt: "Autopilot mode and the deep interview workflow are prohibited.", expectedSkill: null }, { name: "implicit-as-example-frame", prompt: "As an example, do not use deep interview but instead use autopilot mode.", expectedSkill: null }, { name: "implicit-for-instance-frame", prompt: "For instance, do not use deep interview but instead use autopilot mode.", expectedSkill: null }, { name: "implicit-markdown-reference-link", prompt: "[autopilot mode][docs]", expectedSkill: null }, { name: "implicit-plural-coordinated-negative", prompt: "Autopilot mode and deep interview workflows are prohibited.", expectedSkill: null }, { name: "implicit-as-example-governing-frame", prompt: "As an example, ignore the docs and use autopilot mode.", expectedSkill: null }, { name: "implicit-for-instance-governing-frame", prompt: "For instance, ignore the docs and use autopilot mode.", expectedSkill: null }, { name: "implicit-markdown-definition", prompt: "[autopilot mode]: ./docs", expectedSkill: null }, { name: "implicit-markdown-shortcut-label", prompt: "[autopilot mode]", expectedSkill: null }, { name: "implicit-markdown-setext", prompt: "Autopilot mode\n===", expectedSkill: null }, { name: "explicit-markdown-pipe-table", prompt: "$ralplan | workflow\n--- | ---", expectedSkill: null }, { name: "implicit-markdown-table-body", prompt: "Mode | Meaning\n--- | ---\nautopilot mode | autonomous workflow command", expectedSkill: null }, { name: "implicit-as-well-as-negative", prompt: "Autopilot mode as well as deep interview workflows are prohibited.", expectedSkill: null }, { name: "implicit-along-with-negative", prompt: "Autopilot mode along with deep interview workflows are prohibited.", expectedSkill: null }, { name: "implicit-together-with-negative", prompt: "Autopilot mode together with deep interview workflows are prohibited.", expectedSkill: null }, { name: "implicit-ampersand-negative", prompt: "Autopilot mode & deep interview workflows are prohibited.", expectedSkill: null }, { name: "implicit-example-colon-frame", prompt: "As an example: ignore the docs and use autopilot mode.", expectedSkill: null }, { name: "implicit-instance-em-dash-frame", prompt: "For instance — ignore the docs and use autopilot mode.", expectedSkill: null }, { name: "implicit-example-hyphen-frame", prompt: "For example - ignore the docs and use autopilot mode.", expectedSkill: null }, { name: "implicit-instance-colon-frame-no-doc-word", prompt: "For instance: use autopilot mode.", expectedSkill: null }, { name: "implicit-instance-em-dash-frame-no-doc-word", prompt: "For instance — use autopilot mode.", expectedSkill: null }, { name: "implicit-markdown-later-table-body", prompt: "Mode | Meaning\n--- | ---\nmanual | docs\nautopilot mode | autonomous workflow command", expectedSkill: null }, { name: "implicit-embedded-shortcut-definition", prompt: "See [autopilot mode] for details.\n\n[autopilot mode]: ./docs", expectedSkill: null }, { name: "implicit-parenthetical-as-well-negative", prompt: "Autopilot mode, as well as deep interview workflows, are prohibited.", expectedSkill: null }, { name: "implicit-parenthetical-along-negative", prompt: "Autopilot mode, along with deep interview workflows, are prohibited.", expectedSkill: null }, { name: "implicit-parenthetical-together-negative", prompt: "Autopilot mode, together with deep interview workflows, are prohibited.", expectedSkill: null }, { name: "implicit-normalized-shortcut-definition", prompt: "See [autopilot mode] for details.\n\n[autopilot mode]: ./docs", expectedSkill: null }, { name: "implicit-ignore-exclusion", prompt: "Ignore autopilot mode.", expectedSkill: null }, { name: "implicit-skip-exclusion", prompt: "Skip autopilot mode.", expectedSkill: null }, { name: "implicit-exclude-exclusion", prompt: "Exclude autopilot mode.", expectedSkill: null }, { name: "explicit-postposed-prohibited", prompt: "$ralplan is prohibited.", expectedSkill: null }, { name: "explicit-postposed-modal-negative", prompt: "$ralplan should not be run.", expectedSkill: null }, { name: "explicit-postposed-coordinated-negative", prompt: "$ralplan and $autopilot are prohibited.", expectedSkill: null }, { name: "explicit-postposed-article-coordination", prompt: "$ralplan and the $autopilot workflow are prohibited.", expectedSkill: null }, { name: "explicit-postposed-implicit-coordination", prompt: "$ralplan and autopilot mode are prohibited.", expectedSkill: null }, { name: "implicit-blockquote-reference-definition", prompt: "See [autopilot mode] for details.\n\n> [autopilot mode]: ./docs", expectedSkill: null }, { name: "implicit-list-reference-definition", prompt: "See [autopilot mode] for details.\n\n- [autopilot mode]: ./docs", expectedSkill: null }, { name: "implicit-indented-blockquote-reference-definition", prompt: "See [autopilot mode] for details.\n\n> [autopilot mode]: ./docs", expectedSkill: null }, { name: "explicit-list-contained-indented-code", prompt: "- $ralplan", expectedSkill: null }, { name: "implicit-list-contained-indented-code", prompt: "1. autopilot mode", expectedSkill: null }, { name: "implicit-four-space-blockquote-definition", prompt: "See [autopilot mode] for details.\n\n> [autopilot mode]: ./docs", expectedSkill: null }, { name: "implicit-multiline-reference-definition", prompt: "See [autopilot mode] for details.\n\n[autopilot mode]:\n ./docs", expectedSkill: null }, { name: "explicit-list-tab-code-boundary", prompt: "- \t$ralplan", expectedSkill: null }, { name: "implicit-nested-list-contained-code", prompt: "- - autopilot mode", expectedSkill: null }, { name: "implicit-list-nested-blockquote", prompt: "- > autopilot mode", expectedSkill: null }, { name: "implicit-deep-nested-list-contained-code", prompt: "- - - - - - - - - autopilot mode", expectedSkill: null }, { name: "implicit-ordered-list-nested-blockquote", prompt: "1234. > autopilot mode", expectedSkill: null }, { name: "explicit-nested-list-positive", prompt: "- - $ralplan plan it", expectedSkill: "ralplan" }, { name: "explicit-four-digit-ordered-list-positive", prompt: "1234. $ralplan plan it", expectedSkill: "ralplan" }, { name: "explicit-list-fence-then-positive", prompt: "- ```\n $ralplan\n ```\n$autopilot build it", expectedSkill: "autopilot" }, { name: "explicit-nested-list-doc-then-positive", prompt: "- - $ralplan is the consensus-planning command\n$autopilot build it", expectedSkill: "autopilot" }, { name: "explicit-after-closed-blockquote", prompt: "> quoted context\n$ralplan plan it", expectedSkill: "ralplan" }, { name: "explicit-after-closed-fence", prompt: "```text\nquoted context\n```\n$ralplan plan it", expectedSkill: "ralplan" }, { name: "explicit-after-indented-code", prompt: " quoted context\n$ralplan plan it", expectedSkill: "ralplan" }, { name: "explicit-after-closed-quote", prompt: "\"quoted context\"\n$ralplan plan it", expectedSkill: "ralplan" }, { name: "explicit-after-prompts-context", prompt: "Use /prompts:architect.\n$ralplan plan it", expectedSkill: "ralplan" }, { name: "explicit-after-bare-prompts-precedence", prompt: "Prose\n/prompts:architect\n$ralplan plan this", expectedSkill: null }, { name: "explicit-after-unclosed-fence", prompt: "```text\nquoted context\n$ralplan plan it", expectedSkill: null }, { name: "explicit-after-mismatched-fence", prompt: "```text\nquoted context\n~~~\n$ralplan plan it", expectedSkill: null }, { name: "explicit-after-unclosed-quote", prompt: "\"quoted context\n$ralplan plan it", expectedSkill: null }, { name: "explicit-after-blockquote-prose", prompt: "> quoted context\nThe docs mention $ralplan only", expectedSkill: null }, { name: "explicit-after-quote-negative", prompt: "\"quoted context\"\nDo not run $ralplan", expectedSkill: null }, { name: "explicit-stale-predecessor-prose", prompt: "> quoted context\nProse\n$ralplan implement this", expectedSkill: null }, { name: "explicit-stale-predecessor-directive-clause", prompt: "> quoted context\nProse\nUse $ralplan plan this", expectedSkill: null }, { name: "explicit-stale-predecessor-prompts", prompt: "> quoted context\n/prompts:architect\n$ralplan plan this", expectedSkill: null }, { name: "explicit-stale-predecessor-second-block", prompt: "> quoted context\n$ralplan plan it\nLater discussion.\n$autopilot build it", expectedSkill: "ralplan", expectedDeferredSkills: [] }, { name: "explicit-negative-before-unclosed-quote", prompt: "Do not run $ralplan.\n\"unclosed context\n$autopilot build it", expectedSkill: null }, { name: "explicit-reference-before-unclosed-quote", prompt: "[$ralplan]: ./docs\n\"unclosed context\n$autopilot build it", expectedSkill: null }, { name: "explicit-prompts-inside-unclosed-quote", prompt: "\"Use /prompts:architect\n$ralplan plan it", expectedSkill: null }, { name: "explicit-malformed-prompts-suffix", prompt: "/prompts:architect한글\n$ralplan plan it", expectedSkill: null }, { name: "explicit-nested-list-prompts-positive", prompt: "- Use /prompts:architect.\n$ralplan plan it", expectedSkill: "ralplan" }, { name: "explicit-nested-list-fence-positive", prompt: "- - ```\n quoted context\n ```\n$ralplan plan it", expectedSkill: "ralplan" }, { name: "implicit-reference-destination", prompt: "[docs]:\nautopilot", expectedSkill: null }, { name: "explicit-middle-dot-suffix", prompt: "$ralplan·suffix plan it", expectedSkill: null }, { name: "explicit-percent-suffix", prompt: "$ralplan%docs", expectedSkill: null }, { name: "explicit-fullwidth-percent-suffix", prompt: "$ralplan%docs", expectedSkill: null }, { name: "explicit-postposed-also-negative", prompt: "$ralplan is also prohibited.", expectedSkill: null }, { name: "implicit-postposed-still-negative", prompt: "Autopilot mode is still prohibited.", expectedSkill: null }, { name: "implicit-commonmark-case-fold", prompt: "See [ẞ autopilot mode] for details.\n\n[SS autopilot mode]: ./docs", expectedSkill: null }, { name: "implicit-commonmark-escaped-bracket", prompt: "See [foo\\] autopilot mode] for details.\n\n[foo\\] autopilot mode]: ./docs", expectedSkill: null }, { name: "implicit-version-decimal-frame", prompt: "For instance: in version 1.2, use autopilot mode.", expectedSkill: null }, { name: "implicit-abbreviation-frame", prompt: "For instance: e.g. use autopilot mode.", expectedSkill: null }, { name: "implicit-slash-documentation", prompt: "Autopilot mode / deep interview are workflow commands.", expectedSkill: null }, { name: "implicit-low-quote", prompt: "„autopilot mode“", expectedSkill: null }, { name: "implicit-list-documentation", prompt: "- autopilot mode is a workflow command", expectedSkill: null }, { name: "implicit-doc-prefix", prompt: "The reference describes autopilot mode.", expectedSkill: null }, { name: "implicit-doc-suffix", prompt: "autopilot mode is workflow documentation.", expectedSkill: null }, { name: "implicit-inline-code", prompt: "`autopilot mode`", expectedSkill: null }, { name: "implicit-blockquote", prompt: "> autopilot mode", expectedSkill: null }, { name: "documentation-suffix", prompt: "$ralplan.md is the workflow documentation file", expectedSkill: null }, { name: "path-suffix", prompt: "$autopilot/config", expectedSkill: null }, { name: "unicode-suffix", prompt: "$ralplan한글", expectedSkill: null }, { name: "zero-width-suffix", prompt: "$ralplan\u200B.md", expectedSkill: null }, { name: "compatibility-path-suffix", prompt: "$ralplan/config", expectedSkill: null }, { name: "nul-suffix", prompt: "$ralplan\u0000md", expectedSkill: null }, { name: "bidi-control-suffix", prompt: "$ralplan\u202Emd", expectedSkill: null }, { name: "bom-suffix", prompt: "$ralplan\uFEFF.md", expectedSkill: null }, { name: "fullwidth-dot-suffix", prompt: "$ralplan.md", expectedSkill: null }, { name: "direct-prompts-reservation", prompt: "/prompts:architect $ralplan plan this", expectedSkill: null }, { name: "list-documentation", prompt: "- $ralplan is the consensus-planning command", expectedSkill: null }, { name: "colon-list-documentation", prompt: "- $ralplan: consensus-planning workflow", expectedSkill: null }, { name: "dash-list-documentation", prompt: "- $ralplan — consensus-planning command", expectedSkill: null }, { name: "plural-list-documentation", prompt: "- $ralplan, $autopilot are workflow commands", expectedSkill: null }, { name: "conjunction-list-documentation", prompt: "- $ralplan and $autopilot are workflow commands", expectedSkill: null }, { name: "oxford-list-documentation", prompt: "- $ralplan, $autopilot, and $team are workflow commands", expectedSkill: null }, { name: "slash-list-documentation", prompt: "- $ralplan / $autopilot are workflow commands", expectedSkill: null }, { name: "compact-slash-list-documentation", prompt: "- $ralplan/$autopilot are workflow commands", expectedSkill: null }, { name: "negative-then-positive", prompt: "Do not run $ralplan; instead $autopilot build issue #3140", expectedSkill: "autopilot" }, { name: "comma-negative-then-positive", prompt: "Do not run $ralplan, instead $autopilot build it", expectedSkill: "autopilot" }, { name: "use-negative-then-positive", prompt: "Do not run $ralplan; use $autopilot build it", expectedSkill: "autopilot" }, { name: "but-use-negative-then-positive", prompt: "Do not run $ralplan but use $autopilot build it", expectedSkill: "autopilot" }, { name: "but-instead-use-negative-then-positive", prompt: "Do not run $ralplan but instead use $autopilot build it", expectedSkill: "autopilot" }, { name: "em-dash-instead-use-positive", prompt: "Do not run $ralplan — instead use $autopilot build it", expectedSkill: "autopilot" }, { name: "implicit-positive-contrast", prompt: "Do not use deep interview, but use autopilot mode.", expectedSkill: "autopilot" }, { name: "implicit-but-instead-positive", prompt: "Do not use deep interview but instead use autopilot mode.", expectedSkill: "autopilot" }, { name: "implicit-list-verb-positive", prompt: "List files and use autopilot mode.", expectedSkill: "autopilot" }, { name: "implicit-dont-stop-positive", prompt: "No, don't stop.", expectedSkill: null }, { name: "implicit-doc-then-positive", prompt: "Autopilot mode is workflow documentation.\nUse autopilot mode.", expectedSkill: "autopilot" }, { name: "prompts-then-positive", prompt: "Use /prompts:architect.\nUse autopilot mode.", expectedSkill: "autopilot" }, { name: "mixed-negative-explicit-positive-implicit", prompt: "Do not run $ralplan but instead use autopilot mode.", expectedSkill: "autopilot" }, { name: "mixed-quoted-explicit-positive-implicit", prompt: "Ignore \"$ralplan\" and use autopilot mode.", expectedSkill: "autopilot" }, { name: "escaped-prompts-positive-implicit", prompt: "Ignore \\/prompts:architect and use autopilot mode.", expectedSkill: "autopilot" }, { name: "url-prompts-positive-implicit", prompt: "See https://example.com/prompts:architect and use autopilot mode.", expectedSkill: "autopilot" }, { name: "implicit-positive-modal-used", prompt: "Autopilot mode should be used.", expectedSkill: "autopilot" }, { name: "implicit-positive-modal-enabled", prompt: "Autopilot mode must be enabled.", expectedSkill: "autopilot" }, { name: "implicit-positive-modal-run", prompt: "Autopilot mode can be run.", expectedSkill: "autopilot" }, { name: "implicit-fullwidth-possessive-positive", prompt: "User's request: use autopilot mode.", expectedSkill: "autopilot" }, { name: "markdown-prompts-link-positive", prompt: "See [/prompts:architect](./docs.md) and use autopilot mode.", expectedSkill: "autopilot" }, { name: "implicit-subordinate-positive", prompt: "Use autopilot mode, while deep interview is prohibited.", expectedSkill: "autopilot" }, { name: "docs-sentence-then-positive", prompt: "Read the docs. Use autopilot mode.", expectedSkill: "autopilot" }, { name: "docs-semicolon-then-positive", prompt: "The docs are stale; use autopilot mode.", expectedSkill: "autopilot" }, { name: "docs-comma-directive-positive", prompt: "Ignore the docs, use autopilot mode.", expectedSkill: "autopilot" }, { name: "escaped-prompts-explicit-followup", prompt: "Ignore \\/prompts:architect\n$ralplan plan it", expectedSkill: "ralplan" }, { name: "url-prompts-explicit-followup", prompt: "See https://example.com/prompts:architect\n$ralplan plan it", expectedSkill: "ralplan" }, { name: "link-prompts-explicit-followup", prompt: "See [/prompts:architect](./docs.md)\n$ralplan plan it", expectedSkill: "ralplan" }, { name: "linked-explicit-implicit-positive", prompt: "See [$ralplan](./docs.md) and use autopilot mode.", expectedSkill: "autopilot" }, { name: "url-explicit-implicit-positive", prompt: "See https://example.com/$ralplan and use autopilot mode.", expectedSkill: "autopilot" }, { name: "implicit-coordinated-clause-positive", prompt: "Use autopilot mode, and deep interview is prohibited.", expectedSkill: "autopilot" }, { name: "docs-and-directive-positive", prompt: "Ignore the docs and use autopilot mode.", expectedSkill: "autopilot" }, { name: "escaped-prompts-same-line-explicit", prompt: "Ignore \\/prompts:architect; use $ralplan plan it", expectedSkill: "ralplan" }, { name: "url-prompts-same-line-explicit", prompt: "See https://example.com/prompts:architect; use $ralplan plan it", expectedSkill: "ralplan" }, { name: "link-prompts-same-line-explicit", prompt: "See [/prompts:architect](./docs.md); use $ralplan plan it", expectedSkill: "ralplan" }, { name: "reference-prompts-same-line-explicit", prompt: "See [/prompts:architect][docs]; use $ralplan plan it", expectedSkill: "ralplan" }, { name: "heading-prompts-explicit-followup", prompt: "## /prompts:architect\n$ralplan plan it", expectedSkill: "ralplan" }, { name: "table-prompts-explicit-followup", prompt: "| /prompts:architect |\n$ralplan plan it", expectedSkill: "ralplan" }, { name: "heading-explicit-implicit-positive", prompt: "## $ralplan\nUse autopilot mode.", expectedSkill: "autopilot" }, { name: "table-explicit-implicit-positive", prompt: "| $ralplan |\nUse autopilot mode.", expectedSkill: "autopilot" }, { name: "reference-explicit-implicit-positive", prompt: "See [$ralplan][docs] and use autopilot mode.", expectedSkill: "autopilot" }, { name: "windows-path-explicit-implicit-positive", prompt: "See C:\\docs\\$ralplan and use autopilot mode.", expectedSkill: "autopilot" }, { name: "implicit-fullwidth-plural-possessive-positive", prompt: "Users' request: use autopilot mode.", expectedSkill: "autopilot" }, { name: "implicit-modal-coordinated-clause-positive", prompt: "Use autopilot mode, and deep interview should be avoided.", expectedSkill: "autopilot" }, { name: "docs-but-directive-positive", prompt: "Ignore the docs but use autopilot mode.", expectedSkill: "autopilot" }, { name: "prompts-markdown-definition-explicit-followup", prompt: "[/prompts:architect]: ./docs\n$ralplan plan it", expectedSkill: "ralplan" }, { name: "prompts-markdown-shortcut-explicit-followup", prompt: "[/prompts:architect]\n$ralplan plan it", expectedSkill: "ralplan" }, { name: "explicit-markdown-definition-implicit-positive", prompt: "[$ralplan]: ./docs\nUse autopilot mode.", expectedSkill: "autopilot" }, { name: "explicit-markdown-shortcut-implicit-positive", prompt: "[$ralplan]\nUse autopilot mode.", expectedSkill: "autopilot" }, { name: "explicit-markdown-setext-implicit-positive", prompt: "$ralplan\n===\nUse autopilot mode.", expectedSkill: "autopilot" }, { name: "explicit-markdown-pipe-table-implicit-positive", prompt: "$ralplan | workflow\n--- | ---\nUse autopilot mode.", expectedSkill: "autopilot" }, { name: "heading-explicit-later-explicit", prompt: "## $ralplan\n$autopilot build it", expectedSkill: "autopilot" }, { name: "linked-explicit-later-explicit", prompt: "See [$ralplan](./docs.md); use $autopilot build it", expectedSkill: "autopilot" }, { name: "windows-path-explicit-later-explicit", prompt: "See C:\\docs\\$ralplan; use $autopilot build it", expectedSkill: "autopilot" }, { name: "table-body-explicit-later-explicit", prompt: "Mode | Meaning\n--- | ---\n$ralplan | planning\n$autopilot build it", expectedSkill: "autopilot" }, { name: "embedded-shortcut-explicit-implicit-positive", prompt: "See [$ralplan] for details.\n\n[$ralplan]: ./docs\nUse autopilot mode.", expectedSkill: "autopilot" }, { name: "quoted-explicit-later-explicit", prompt: "Ignore \"$ralplan\" and use $autopilot build it", expectedSkill: "autopilot" }, { name: "inline-code-explicit-later-explicit", prompt: "Ignore `$ralplan` and use $autopilot build it", expectedSkill: "autopilot" }, { name: "normalized-shortcut-explicit-implicit-positive", prompt: "See [$ralplan ] for details.\n\n[$ralplan]: ./docs\nUse autopilot mode.", expectedSkill: "autopilot" }, { name: "intro-frame-sentence-reset-positive", prompt: "For instance: manual mode is slower. Use autopilot mode.", expectedSkill: "autopilot" }, { name: "inline-link-overlap-later-explicit", prompt: "See [`$ralplan`](./docs.md) and use $autopilot build it", expectedSkill: "autopilot" }, { name: "implicit-exclusion-later-positive", prompt: "Ignore deep interview and use autopilot mode.", expectedSkill: "autopilot" }, { name: "link-destination-later-explicit", prompt: "See [docs](https://example.com/$ralplan) and use $autopilot build it", expectedSkill: "autopilot" }, { name: "link-title-later-explicit", prompt: "See [docs](./docs.md \"$ralplan reference\") and use $autopilot build it", expectedSkill: "autopilot" }, { name: "nested-destination-later-explicit", prompt: "See [docs](https://example.com/(v1)/$ralplan) and use $autopilot build it", expectedSkill: "autopilot" }, { name: "nested-link-text-later-explicit", prompt: "See [$ralplan](https://example.com/(v1)) and use $autopilot build it", expectedSkill: "autopilot" }, { name: "quoted-title-parenthesis-later-explicit", prompt: "See [docs](./docs.md \"$ralplan (reference\") and use $autopilot build it", expectedSkill: "autopilot" }, { name: "blockquote-code-definition-boundary", prompt: "Use autopilot mode.\n\n> [autopilot mode]: ./docs", expectedSkill: "autopilot" }, { name: "explicit-list-tab-content-boundary", prompt: "-\t $ralplan", expectedSkill: "ralplan" }, { name: "postposed-negative-but-later-explicit", prompt: "$ralplan is prohibited but use $autopilot build it", expectedSkill: "autopilot" }, { name: "postposed-negative-and-later-explicit", prompt: "$ralplan is prohibited and use $autopilot build it", expectedSkill: "autopilot" }, { name: "inline-negative-then-positive", prompt: "Quoted inline-code `$ralplan`; use $autopilot build it", expectedSkill: "autopilot" }, { name: "negative-line-then-positive", prompt: "Without $ralplan.\n$autopilot build it", expectedSkill: "autopilot" }, { name: "quoted-then-positive", prompt: "Quoted example: \"$ralplan plan it\".\n$autopilot build it", expectedSkill: "autopilot" }, { name: "inert-prompts-then-positive", prompt: "\"Use /prompts:architect\"\n$ralplan plan it", expectedSkill: "ralplan" }, { name: "list-doc-then-positive", prompt: "- $ralplan is the consensus-planning command\n$autopilot build it", expectedSkill: "autopilot" }, { name: "prompt-doc-then-positive", prompt: "- /prompts:architect is the prompt command documentation\n$ralplan plan it", expectedSkill: "ralplan" }, { name: "conjunction-list-doc-then-positive", prompt: "- $ralplan and $autopilot are workflow commands\n$autopilot execute it", expectedSkill: "autopilot" }, { name: "oxford-list-doc-then-positive", prompt: "- $ralplan, $autopilot, and $team are workflow commands\n$autopilot execute it", expectedSkill: "autopilot" }, { name: "slash-list-doc-then-positive", prompt: "- $ralplan / $autopilot are workflow commands\n$autopilot execute it", expectedSkill: "autopilot" }, { name: "compact-slash-list-doc-then-positive", prompt: "- $ralplan/$autopilot are workflow commands\n$autopilot execute it", expectedSkill: "autopilot" }, { name: "escaped-quote-range", prompt: "\"$ralplan \\\"; $autopilot build it", expectedSkill: null }, { name: "blockquote-fence-closer", prompt: "```\n$ralplan\n> ```\n$autopilot build it", expectedSkill: null }, { name: "double-negative-control", prompt: "Do not run $ralplan; do not run $autopilot", expectedSkill: null }, { name: "comma-negative-control", prompt: "Do not run $ralplan, $autopilot", expectedSkill: null }, { name: "unseparated-control", prompt: "Do not run $ralplan and use $autopilot build it", expectedSkill: null }, { name: "even-escape-control", prompt: "\"$ralplan \\\\\"; use $autopilot build it", expectedSkill: "autopilot" }, { name: "matching-fence-control", prompt: "> ```\n> $ralplan\n> ```\n$autopilot build it", expectedSkill: "autopilot" }, { name: "invalid-fence-closer", prompt: "```\n$ralplan\n``` still code\n$autopilot build it", expectedSkill: null }, { name: "documentation-clause-control", prompt: "Do not run $ralplan. We only document $autopilot behavior", expectedSkill: null }, { name: "punctuation-multi-workflow", prompt: "$ralplan, $autopilot build issue #3140", expectedSkill: "ralplan", expectedDeferredSkills: ["autopilot"], }, ] as const; for (const [caseIndex, testCase] of cases.entries()) { const cwd = await mkdtemp(join(tmpdir(), `omx-native-compiled-classification-${caseIndex}-`)); const sessionId = `sess-compiled-${caseIndex}`; const env = { ...process.env, OMX_ROOT: "", OMX_STATE_ROOT: "", OMX_SESSION_ID: "", CODEX_SESSION_ID: "", OMX_TEAM_STATE_ROOT: "", OMX_TEAM_WORKER: "", OMX_TEAM_INTERNAL_WORKER: "", OMX_TEAM_LEADER_CWD: "", OMX_TEAM_MODE: "insideTmux" in testCase && testCase.insideTmux ? "enabled" : "", SESSION_ID: "", OMX_QUESTION_RETURN_PANE: "", OMX_LEADER_PANE_ID: "", OMX_TMUX_HUD_OWNER: "", TMUX: "insideTmux" in testCase && testCase.insideTmux ? "/tmp/tmux-pr3140-regression" : "", TMUX_PANE: "insideTmux" in testCase && testCase.insideTmux ? "%3140" : "", }; try { const submit = parseSingleJsonStdout(runNativeHookCli({ hook_event_name: "UserPromptSubmit", cwd, source: "codex-app", session_id: sessionId, thread_id: `thread-${caseIndex}`, turn_id: `turn-${caseIndex}`, prompt: testCase.prompt, }, { cwd, env })); assert.equal(submit.continue, undefined, testCase.name); const sessionDir = join(cwd, ".omx", "state", "sessions", sessionId); const skillStatePath = join(sessionDir, "skill-active-state.json"); if (testCase.expectedSkill === null) { // No activation. A RETIRED token additionally leaves a refusal record - active false with no // active skills - which is a diagnostic rather than an activation, so assert that nothing was // activated instead of that no file exists. if (existsSync(skillStatePath)) { const refused = JSON.parse(await readFile(skillStatePath, "utf-8")) as { active?: boolean; active_skills?: unknown[] }; assert.equal(refused.active, false, testCase.name); assert.deepEqual(refused.active_skills ?? [], [], testCase.name); } assert.equal(existsSync(join(sessionDir, "ralplan-state.json")), false, testCase.name); assert.equal(existsSync(join(sessionDir, "autopilot-state.json")), false, testCase.name); } else { assert.equal(existsSync(skillStatePath), true, testCase.name); const skillState = JSON.parse(await readFile(skillStatePath, "utf-8")) as { active?: boolean; skill?: string; phase?: string; error?: string; deferred_skills?: string[]; active_skills?: Array<{ skill?: string }>; }; assert.equal(skillState.active, true, testCase.name); assert.equal(skillState.skill, testCase.expectedSkill, testCase.name); if ("expectedDeferredSkills" in testCase) { assert.deepEqual(skillState.deferred_skills ?? [], testCase.expectedDeferredSkills, testCase.name); } if ("expectedActiveSkills" in testCase) { assert.deepEqual(skillState.active_skills?.map((entry) => entry.skill) ?? [], testCase.expectedActiveSkills, testCase.name); } if ("expectedActiveDetailSkills" in testCase) { const activeDetailSkills = (await Promise.all(["ralplan", "autopilot"].map(async (skill) => { const detailPath = join(sessionDir, `${skill}-state.json`); if (!existsSync(detailPath)) return null; const detailState = JSON.parse(await readFile(detailPath, "utf-8")) as { active?: boolean }; return detailState.active ? skill : null; }))).filter((skill): skill is string => skill !== null); assert.deepEqual(activeDetailSkills, testCase.expectedActiveDetailSkills, testCase.name); } } const stop = parseSingleJsonStdout(runNativeHookCli({ hook_event_name: "Stop", cwd, source: "codex-app", session_id: sessionId, thread_id: `thread-${caseIndex}`, turn_id: `stop-${caseIndex}`, }, { cwd, env })); if (testCase.expectedSkill === null) assert.deepEqual(stop, {}, testCase.name); else if ("expectedStopBlock" in testCase && !testCase.expectedStopBlock) { assert.notEqual(stop.decision, "block", testCase.name); } else assert.equal(stop.decision, "block", testCase.name); } finally { await rm(cwd, { recursive: true, force: true }); } } }); it("keeps terminal Autopilot failed and inactive when disabled-Team restart lacks host receipt verification", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-disabled-team-terminal-restart-")); const sessionId = "sess-disabled-team-terminal-restart"; const threadId = "thread-disabled-team-terminal-restart"; const turnId = "turn-disabled-team-terminal-restart"; const sessionDir = join(cwd, ".omx", "state", "sessions", sessionId); const autopilotPath = join(sessionDir, "autopilot-state.json"); try { await writeJson(join(cwd, ".omx", "setup-scope.json"), { scope: "project", teamMode: "disabled", }); await writeJson(autopilotPath, { mode: "autopilot", active: false, current_phase: "complete", completed_at: "2026-07-12T23:00:00.000Z", session_id: sessionId, thread_id: threadId, turn_id: turnId, }); const before = await readFile(autopilotPath); const restart = await dispatchCodexNativeHook({ hook_event_name: "UserPromptSubmit", cwd, source: "codex-app", session_id: sessionId, thread_id: threadId, turn_id: turnId, prompt: "$team $autopilot retry", }, { cwd }); assert.equal(restart.skillState?.skill, "autopilot"); // #3463: autopilot restarts normally assert.notEqual(restart.skillState?.error, "documented_host_consensus_receipt_unavailable"); const restartGuidance = String((restart.outputJson as { hookSpecificOutput?: { additionalContext?: string } } | null)?.hookSpecificOutput?.additionalContext ?? ""); // #3463: autopilot restarts normally; guidance no longer includes host-receipt denial assert.doesNotMatch(restartGuidance, /documented_host_consensus_receipt_unavailable/); assert.equal(existsSync(join(cwd, ".omx", "state", "team-state.json")), false); const restartedState = JSON.parse(await readFile(autopilotPath, "utf-8")) as { active?: boolean; current_phase?: string; error?: string; }; assert.notDeepEqual(await readFile(autopilotPath), before); // #3463: autopilot restarts as active (no longer preflight-denied) assert.notEqual(restartedState.error, "documented_host_consensus_receipt_unavailable"); assert.notEqual(restartedState.current_phase, "failed"); assert.notEqual(restartedState.error, "documented_host_consensus_receipt_unavailable"); // #3463: deep-interview state may now exist // #3463: ultragoal state not created yet const stop = parseSingleJsonStdout(runNativeHookCli({ hook_event_name: "Stop", cwd, source: "codex-app", session_id: sessionId, thread_id: threadId, turn_id: "stop-disabled-team-terminal-restart", }, { cwd, env: { ...process.env, OMX_ROOT: "", OMX_STATE_ROOT: "" }, })); // #3463: autopilot is now active (not preflight-denied), so Stop blocks. assert.notEqual(stop.decision, undefined); } finally { await rm(cwd, { recursive: true, force: true }); } }); async function assertG2aDocumentationReferenceIsInert(transport: "raw" | "compiled"): Promise { const name = `g2a-${transport}`; const cwd = await mkdtemp(join(tmpdir(), `omx-native-${name}-`)); const sessionId = `sess-${name}`; const threadId = `thread-${name}`; const stateDir = join(cwd, ".omx", "state"); const sessionDir = join(stateDir, "sessions", sessionId); const skillDetailPaths = [ join(stateDir, "skill-active-state.json"), join(stateDir, "ralplan-state.json"), join(sessionDir, "skill-active-state.json"), join(sessionDir, "ralplan-state.json"), ]; const env = { ...process.env, OMX_ROOT: "", OMX_STATE_ROOT: "", OMX_TEAM_MODE: "" }; const payload = { hook_event_name: "UserPromptSubmit" as const, cwd, source: "codex-app", session_id: sessionId, thread_id: threadId, turn_id: `turn-${name}`, prompt: "use $ralplan is the consensus-planning command" }; try { await writeJson(join(cwd, ".omx", "setup-scope.json"), { scope: "project", teamMode: "disabled" }); assert.deepEqual(skillDetailPaths.map(existsSync), [false, false, false, false], name); if (transport === "raw") { const submit = await dispatchCodexNativeHook(payload, { cwd }); assert.equal(submit.skillState, null, name); assert.equal(submit.outputJson, null, name); } else { assert.deepEqual(parseSingleJsonStdout(runNativeHookCli(payload, { cwd, env })), {}, name); } assert.deepEqual(skillDetailPaths.map(existsSync), [false, false, false, false], name); const stopPayload = { ...payload, hook_event_name: "Stop" as const, turn_id: `stop-${name}` }; if (transport === "raw") { assert.equal((await dispatchCodexNativeHook(stopPayload, { cwd })).outputJson, null, name); } else { assert.notEqual(parseSingleJsonStdout(runNativeHookCli(stopPayload, { cwd, env })).decision, "block", name); } assert.deepEqual(skillDetailPaths.map(existsSync), [false, false, false, false], name); } finally { await rm(cwd, { recursive: true, force: true }); } } async function assertG2bTerminalStateIsInert(transport: "raw" | "compiled"): Promise { const name = `g2b-${transport}`; const cwd = await mkdtemp(join(tmpdir(), `omx-native-${name}-`)); const sessionId = `sess-${name}`; const threadId = `thread-${name}`; const stateDir = join(cwd, ".omx", "state"); const sessionDir = join(stateDir, "sessions", sessionId); const paths = [ join(stateDir, "skill-active-state.json"), join(stateDir, "autopilot-state.json"), join(sessionDir, "skill-active-state.json"), join(sessionDir, "autopilot-state.json"), join(stateDir, "session.json"), ]; const env = { ...process.env, OMX_ROOT: "", OMX_STATE_ROOT: "", OMX_TEAM_MODE: "" }; const payload = { hook_event_name: "UserPromptSubmit" as const, cwd, source: "codex-app", session_id: sessionId, thread_id: threadId, turn_id: `turn-${name}`, prompt: "do not start $autopilot — café" }; try { await writeJson(join(cwd, ".omx", "setup-scope.json"), { scope: "project", teamMode: "disabled" }); await writeJson(paths[0], { active: false, skill: "ralplan", phase: "complete", session_id: "root-skill-session", thread_id: "root-skill-thread", turn_id: "root-skill-turn", completed_at: "2026-07-01T00:00:00.000Z", updated_at: "2026-07-01T00:00:01.000Z" }); await writeJson(paths[1], { active: false, mode: "autopilot", current_phase: "complete", session_id: "root-detail-session", thread_id: "root-detail-thread", turn_id: "root-detail-turn", completed_at: "2026-07-02T00:00:00.000Z", updated_at: "2026-07-02T00:00:01.000Z" }); await writeJson(paths[2], { active: false, skill: "team", phase: "complete", session_id: "session-skill-session", thread_id: "session-skill-thread", turn_id: "session-skill-turn", completed_at: "2026-07-03T00:00:00.000Z", updated_at: "2026-07-03T00:00:01.000Z" }); await writeJson(paths[3], { active: false, mode: "autopilot", current_phase: "complete", session_id: "session-detail-session", thread_id: "session-detail-thread", turn_id: "session-detail-turn", completed_at: "2026-07-04T00:00:00.000Z", updated_at: "2026-07-04T00:00:01.000Z" }); await writeJson(paths[4], { session_id: sessionId, native_session_id: `native-${name}`, thread_id: "pointer-thread", turn_id: "pointer-turn", created_at: "2026-07-05T00:00:00.000Z", updated_at: "2026-07-05T00:00:01.000Z", cwd }); const buffers = await Promise.all(paths.map((path) => readFile(path))); if (transport === "raw") { const submit = await dispatchCodexNativeHook(payload, { cwd }); assert.equal(submit.skillState, null, name); assert.equal(submit.outputJson, null, name); } else { assert.deepEqual(parseSingleJsonStdout(runNativeHookCli(payload, { cwd, env })), {}, name); } assert.deepEqual(await Promise.all(paths.map((path) => readFile(path))), buffers, name); const stopPayload = { ...payload, hook_event_name: "Stop" as const, turn_id: `stop-${name}` }; if (transport === "raw") { assert.equal((await dispatchCodexNativeHook(stopPayload, { cwd })).outputJson, null, name); } else { assert.notEqual(parseSingleJsonStdout(runNativeHookCli(stopPayload, { cwd, env })).decision, "block", name); } assert.deepEqual(await Promise.all(paths.map((path) => readFile(path))), buffers, name); } finally { await rm(cwd, { recursive: true, force: true }); } } it("keeps G2a documentation reference state-free through raw UserPromptSubmit and Stop", async () => { await assertG2aDocumentationReferenceIsInert("raw"); }); it("keeps G2a documentation reference state-free through compiled UserPromptSubmit and Stop", async () => { await assertG2aDocumentationReferenceIsInert("compiled"); }); it("keeps G2b negated terminal root/session conflict byte-identical through raw UserPromptSubmit and Stop", async () => { await assertG2bTerminalStateIsInert("raw"); }); it("keeps G2b negated terminal root/session conflict byte-identical through compiled UserPromptSubmit and Stop", async () => { await assertG2bTerminalStateIsInert("compiled"); }); async function assertG1bURestart(transport: "raw" | "compiled"): Promise { const cwd = await mkdtemp(join(tmpdir(), `omx-native-${transport}-g1bu-restart-`)); const sessionId = `sess-g1bu-${transport}`; const threadId = `thread-g1bu-${transport}`; const priorTurnId = `turn-g1bu-${transport}-old`; const turnId = `turn-g1bu-${transport}-new`; const prompt = "$team $autopilot restart — café"; const stateDir = join(cwd, ".omx", "state"); const sessionDir = join(stateDir, "sessions", sessionId); const sessionSkillPath = join(sessionDir, "skill-active-state.json"); const sessionAutopilotPath = join(sessionDir, "autopilot-state.json"); const env = { ...process.env, OMX_ROOT: "", OMX_STATE_ROOT: "", OMX_TEAM_MODE: "", TMUX: "", TMUX_PANE: "" }; try { await writeJson(join(cwd, ".omx", "setup-scope.json"), { scope: "project", teamMode: "disabled" }); await writeJson(join(stateDir, "skill-active-state.json"), { active: true, skill: "autopilot", phase: "completing", session_id: sessionId, thread_id: threadId, turn_id: priorTurnId, marker: "root-skill", active_skills: [{ skill: "autopilot", phase: "completing", active: true, session_id: sessionId, thread_id: threadId, turn_id: priorTurnId }] }); await writeJson(join(stateDir, "autopilot-state.json"), { active: false, mode: "autopilot", current_phase: "complete", session_id: sessionId, thread_id: threadId, turn_id: priorTurnId, marker: "root-autopilot" }); await writeJson(sessionSkillPath, { active: true, skill: "autopilot", phase: "completing", session_id: sessionId, thread_id: threadId, turn_id: priorTurnId, marker: "session-skill", active_skills: [{ skill: "autopilot", phase: "completing", active: true, session_id: sessionId, thread_id: threadId, turn_id: priorTurnId }] }); await writeJson(sessionAutopilotPath, { active: false, mode: "autopilot", current_phase: "complete", session_id: sessionId, thread_id: threadId, turn_id: priorTurnId, marker: "session-autopilot" }); const payload = { hook_event_name: "UserPromptSubmit" as const, cwd, source: "codex-app", session_id: sessionId, thread_id: threadId, turn_id: turnId, prompt }; let output: Record | null; if (transport === "raw") { const result = await dispatchCodexNativeHook(payload, { cwd }); assert.equal(result.skillState?.skill, "autopilot"); assert.equal(result.skillState?.active, true); output = result.outputJson; } else { const input = Buffer.from(JSON.stringify(payload), "utf-8"); assert.equal(input.toString("utf-8"), JSON.stringify(payload)); output = parseSingleJsonStdout(runNativeHookCli(input.toString("utf-8"), { cwd, env })); } assert.doesNotMatch(JSON.stringify(output), /\$team" -> team/); const skillState = JSON.parse(await readFile(sessionSkillPath, "utf-8")) as { active?: boolean; skill?: string; turn_id?: string; active_skills?: Array<{ skill?: string }> }; assert.equal(skillState.active, true); assert.equal(skillState.skill, "autopilot"); assert.equal(skillState.turn_id, turnId); assert.deepEqual(skillState.active_skills?.map((entry) => entry.skill), ["autopilot"]); const autopilotState = JSON.parse(await readFile(sessionAutopilotPath, "utf-8")) as { active?: boolean; current_phase?: string; turn_id?: string }; assert.equal(autopilotState.active, true); assert.equal(autopilotState.current_phase, "deep-interview"); assert.equal(autopilotState.turn_id, turnId); assert.equal(existsSync(join(stateDir, "team-state.json")), false); const stopPayload = { ...payload, hook_event_name: "Stop" as const, turn_id: `stop-g1bu-${transport}` }; if (transport === "raw") { assert.equal((await dispatchCodexNativeHook(stopPayload, { cwd })).outputJson?.decision, "block"); } else { assert.equal(parseSingleJsonStdout(runNativeHookCli(stopPayload, { cwd, env })).decision, "block"); } } finally { await rm(cwd, { recursive: true, force: true }); } } it("restarts terminal Autopilot through disabled-Team raw UserPromptSubmit for G1b-U", async () => { await assertG1bURestart("raw"); }); it("restarts terminal Autopilot through disabled-Team compiled UserPromptSubmit for G1b-U", async () => { await assertG1bURestart("compiled"); }); it("does not crash Stop hook dispatch when the exec follow-up queue is malformed", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-stop-exec-followup-corrupt-")); try { const session = await writeSessionStart(cwd, "sess-exec-followup-corrupt"); const queuePath = join(cwd, ".omx", "state", "sessions", session.session_id, "exec-followups.json"); await mkdir(dirname(queuePath), { recursive: true }); await writeFile(queuePath, '{"version":1,"records":[', "utf-8"); const result = await dispatchCodexNativeHook({ hook_event_name: "Stop", cwd, session_id: session.session_id, }); assert.equal(result.hookEventName, "Stop"); assert.equal(result.outputJson, null); const queueDirEntries = await readdir(dirname(queuePath)); assert.ok( queueDirEntries.some((entry) => entry.startsWith("exec-followups.json.corrupt-"), ), ); const auditPath = join( cwd, ".omx", "logs", `exec-followups-${new Date().toISOString().slice(0, 10)}.jsonl`, ); assert.match( await readFile(auditPath, "utf-8"), /exec_followup_queue_corrupt_recovered/, ); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("session-scoped Stop with queued exec follow-up for active ralplan delivers it once and marks consumed", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-stop-scoped-exec-followup-")); try { const nativeSessionId = "codex-root-scoped-exec-followup"; const activeSessionId = "omx-active-other"; const stateDir = join(cwd, ".omx", "state"); // Create native session owner const nativeState = await writeNativeSessionOwner(cwd, nativeSessionId, { pid: process.pid, }); // Set native session as active FIRST so injectExecFollowup can find it await writeJson(join(stateDir, "session.json"), { session_id: nativeSessionId }); // Write an active ralplan skill state for this native session await writeJson( join(stateDir, "sessions", nativeSessionId, "skill-active-state.json"), { version: 1, active: true, skill: "ralplan", phase: "planning", session_id: nativeSessionId, }, ); await writeJson( join(stateDir, "sessions", nativeSessionId, "ralplan-state.json"), { active: true, mode: "ralplan", current_phase: "planning", session_id: nativeSessionId, }, ); // Inject a queued exec follow-up for this native session (while it's active) const queued = await injectExecFollowup({ cwd, sessionId: nativeSessionId, actor: "test", prompt: "Verify the exec follow-up implementation.", nowIso: "2026-09-30T12:00:00.000Z", }); // Change active session so resolveInternalSessionIdForPayload fails for native session ID // This triggers the ownerState logic in dispatchCodexNativeHook's Stop handler await mkdir(join(stateDir, "sessions", activeSessionId), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: activeSessionId }); // Dispatch Stop with native session ID (triggers sessionScopedOnly via ownerState) const stopResult = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: nativeSessionId, }, { cwd }, ); // The output should deliver the queued exec follow-up assert.equal(stopResult.outputJson?.decision, "block"); assert.match( String(stopResult.outputJson?.reason), new RegExp(queued.queued.id), ); // Verify that the follow-up is marked as delivered const after = await readPendingExecFollowups(cwd, nativeSessionId); assert.equal(after.pending.length, 0); const persisted = JSON.parse(await readFile(queued.queuePath, "utf-8")) as { records: Array<{ delivered_at?: string; delivery_event?: string }>; }; assert.equal(persisted.records[0]?.delivery_event, "stop-hook"); assert.ok(persisted.records[0]?.delivered_at); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("session-scoped second Stop does not redeliver consumed exec follow-up", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-stop-scoped-no-redeliver-")); try { const nativeSessionId = "codex-root-scoped-no-redeliver"; const activeSessionId = "omx-active-other-2"; const stateDir = join(cwd, ".omx", "state"); // Create native session owner const nativeState = await writeNativeSessionOwner(cwd, nativeSessionId, { pid: process.pid, }); // Set native session as active FIRST so injectExecFollowup can find it await writeJson(join(stateDir, "session.json"), { session_id: nativeSessionId }); // Write an active ralplan skill state await writeJson( join(stateDir, "sessions", nativeSessionId, "skill-active-state.json"), { version: 1, active: true, skill: "ralplan", phase: "planning", session_id: nativeSessionId, }, ); await writeJson( join(stateDir, "sessions", nativeSessionId, "ralplan-state.json"), { active: true, mode: "ralplan", current_phase: "planning", session_id: nativeSessionId, }, ); // Inject a queued exec follow-up (while native session is active) const queued = await injectExecFollowup({ cwd, sessionId: nativeSessionId, actor: "test", prompt: "First exec follow-up.", nowIso: "2026-09-30T12:00:00.000Z", }); // Change active session so resolveInternalSessionIdForPayload fails for native session ID await mkdir(join(stateDir, "sessions", activeSessionId), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: activeSessionId }); // First Stop dispatches and delivers the follow-up const firstStop = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: nativeSessionId, }, { cwd }, ); assert.equal(firstStop.outputJson?.decision, "block"); assert.match( String(firstStop.outputJson?.reason), new RegExp(queued.queued.id), ); // Verify follow-up is now consumed const afterFirst = await readPendingExecFollowups(cwd, nativeSessionId); assert.equal(afterFirst.pending.length, 0); // Second Stop should not deliver it again (already consumed) const secondStop = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: nativeSessionId, }, { cwd }, ); // Second stop should block on the skill, not on exec follow-up assert.match( String(secondStop.outputJson?.reason ?? ""), /ralplan is still active/, ); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("non-scoped Stop with exec follow-up for foreign session does not deliver", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-stop-foreign-exec-")); try { // Create one native session with exec follow-up const nativeSessionId1 = "codex-root-foreign-1"; const activeSessionId = "omx-active-other-3"; const stateDir = join(cwd, ".omx", "state"); const nativeState1 = await writeNativeSessionOwner(cwd, nativeSessionId1, { pid: process.pid, }); // Set native session as active FIRST so injectExecFollowup can find it await writeJson(join(stateDir, "session.json"), { session_id: nativeSessionId1 }); await writeJson( join(stateDir, "sessions", nativeSessionId1, "skill-active-state.json"), { version: 1, active: true, skill: "ralplan", phase: "planning", session_id: nativeSessionId1, }, ); await writeJson( join(stateDir, "sessions", nativeSessionId1, "ralplan-state.json"), { active: true, mode: "ralplan", current_phase: "planning", session_id: nativeSessionId1, }, ); // Inject exec follow-up for session1 (while it's active) const queued = await injectExecFollowup({ cwd, sessionId: nativeSessionId1, actor: "test", prompt: "Follow-up for session 1.", nowIso: "2026-09-30T12:00:00.000Z", }); // Change active session so resolveInternalSessionIdForPayload fails for native session ID await mkdir(join(stateDir, "sessions", activeSessionId), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: activeSessionId }); // Try to stop a different foreign session (not scoped, no owner) const foreignNativeSessionId = "codex-root-foreign-2"; const stopResult = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: foreignNativeSessionId, }, { cwd }, ); // Stop for foreign session should not deliver the follow-up from session1 assert.equal(stopResult.omxEventName, "stop"); assert.ok( !String(stopResult.outputJson?.reason ?? "").includes(queued.queued.id), ); // Follow-up should still be pending for session1 const stillPending = await readPendingExecFollowups(cwd, nativeSessionId1); assert.equal(stillPending.pending.length, 1); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not block Stop on stale session autopilot mirror when canonical skill state is inactive", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-stale-autopilot-mirror-"), ); try { const sessionId = "sess-stale-autopilot-mirror"; await writeJson(join(cwd, ".omx", "state", "session.json"), { session_id: sessionId, cwd, }); await writeJson(join(cwd, ".omx", "state", "skill-active-state.json"), { version: 1, active: false, skill: "", phase: "cancelled", active_skills: [], }); await writeJson( join( cwd, ".omx", "state", "sessions", sessionId, "autopilot-state.json", ), { active: true, mode: "autopilot", current_phase: "ultragoal", session_id: sessionId, }, ); await writeJson( join( cwd, ".omx", "state", "sessions", sessionId, "skill-active-state.json", ), { version: 1, active: true, skill: "autopilot", phase: "ultragoal", session_id: sessionId, active_skills: [ { skill: "autopilot", phase: "ultragoal", active: true, session_id: sessionId, }, ], }, ); const stdout = runNativeHookCli( { hook_event_name: "Stop", cwd, session_id: sessionId, thread_id: "thread-stale-autopilot-mirror", }, { cwd }, ); assert.deepEqual(parseSingleJsonStdout(stdout), {}); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("includes blocking state source and canonical agreement in Stop diagnostics", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-autopilot-diagnostic-"), ); try { const sessionId = "sess-autopilot-diagnostic"; await writeJson(join(cwd, ".omx", "state", "session.json"), { session_id: sessionId, cwd, }); await writeJson(join(cwd, ".omx", "state", "skill-active-state.json"), { version: 1, active: true, skill: "autopilot", phase: "ultragoal", session_id: sessionId, active_skills: [ { skill: "autopilot", phase: "ultragoal", active: true, session_id: sessionId, }, ], }); await writeJson( join( cwd, ".omx", "state", "sessions", sessionId, "autopilot-state.json", ), { active: true, mode: "autopilot", current_phase: "ultragoal", session_id: sessionId, }, ); const output = parseSingleJsonStdout( runNativeHookCli( { hook_event_name: "Stop", cwd, session_id: sessionId, thread_id: "thread-autopilot-diagnostic", }, { cwd }, ), ); assert.equal(output.decision, "block"); assert.equal(output.stopReason, "autopilot_ultragoal"); assert.match( String(output.reason ?? ""), /state: \.omx\/state\/sessions\/sess-autopilot-diagnostic\/autopilot-state\.json/, ); assert.match(String(output.reason ?? ""), /canonical: canonical_agrees/); assert.match( String(output.systemMessage ?? ""), /state: \.omx\/state\/sessions\/sess-autopilot-diagnostic\/autopilot-state\.json/, ); assert.match( String(output.systemMessage ?? ""), /canonical: canonical_agrees/, ); assert.deepEqual(Object.keys(output).sort(), [ "decision", "reason", "stopReason", "systemMessage", ]); assert.equal("statePath" in output, false); assert.equal("canonicalDisagreement" in output, false); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("keeps canonical phase disagreements in active Autopilot Stop diagnostics", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-autopilot-canonical-phase-"), ); try { const sessionId = "sess-autopilot-canonical-phase"; await writeJson(join(cwd, ".omx", "state", "session.json"), { session_id: sessionId, cwd, }); await writeJson(join(cwd, ".omx", "state", "skill-active-state.json"), { version: 1, active: true, skill: "autopilot", phase: "execution", session_id: sessionId, active_skills: [ { skill: "autopilot", phase: "execution", active: true, session_id: sessionId, }, ], }); await writeJson( join( cwd, ".omx", "state", "sessions", sessionId, "autopilot-state.json", ), { active: true, mode: "autopilot", current_phase: "ultragoal", session_id: sessionId, }, ); const output = parseSingleJsonStdout( runNativeHookCli( { hook_event_name: "Stop", cwd, session_id: sessionId, thread_id: "thread-autopilot-canonical-phase", }, { cwd }, ), ); assert.equal(output.decision, "block"); assert.equal(output.stopReason, "autopilot_ultragoal"); assert.match( String(output.reason ?? ""), /canonical: canonical_phase:execution/, ); assert.match( String(output.systemMessage ?? ""), /canonical: canonical_phase:execution/, ); assert.deepEqual(Object.keys(output).sort(), [ "decision", "reason", "stopReason", "systemMessage", ]); assert.equal("statePath" in output, false); assert.equal("canonicalDisagreement" in output, false); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("emits exactly one parseable JSON object for active Stop CLI continuation", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-cli-stop-json-")); try { await writeActiveAutopilotSession(cwd, "sess-cli-stop-json"); const stdout = runNativeHookCli( { hook_event_name: "Stop", cwd, session_id: "sess-cli-stop-json", thread_id: "thread-cli-stop-json", turn_id: "turn-cli-stop-json", }, { cwd }, ); const output = parseSingleJsonStdout(stdout); assert.equal(output.decision, "block"); assert.equal(output.stopReason, "autopilot_execution"); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("keeps noisy Stop hook plugin stdout out of native Stop CLI stdout", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-cli-stop-noisy-plugin-"), ); try { await writeActiveAutopilotSession(cwd, "sess-cli-stop-noisy-plugin"); await mkdir(join(cwd, ".omx", "hooks"), { recursive: true }); await writeFile( join(cwd, ".omx", "hooks", "noisy.mjs"), `export async function onHookEvent(event) { if (event.event === "stop") console.log("PLUGIN_NOISE"); } `, "utf-8", ); const stdout = runNativeHookCli({ hook_event_name: "Stop", cwd, session_id: "sess-cli-stop-noisy-plugin", thread_id: "thread-cli-stop-noisy-plugin", turn_id: "turn-cli-stop-noisy-plugin", }, { cwd }); assert.doesNotMatch(stdout, /PLUGIN_NOISE/); const output = parseSingleJsonStdout(stdout); assert.equal(output.decision, "block"); assert.equal(output.stopReason, "autopilot_execution"); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("emits deterministic Stop JSON stdout when Stop dispatch fails", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-cli-stop-dispatch-failure-")); try { const stdout = runNativeHookCli({ hook_event_name: "Stop", cwd, session_id: "sess-cli-stop-dispatch-failure", thread_id: "thread-cli-stop-dispatch-failure", turn_id: "turn-cli-stop-dispatch-failure", }, { cwd, env: { ...process.env, NODE_ENV: "test", OMX_NATIVE_HOOK_TEST_THROW_STOP_DISPATCH: "1", }, }); const output = parseSingleJsonStdout(stdout); assert.equal(output.decision, "block"); assert.equal(output.stopReason, "native_stop_dispatch_failure"); assert.match(String(output.reason), /failed before normal continuation handling/); assert.match(String(output.systemMessage), /test-induced Stop dispatch failure/); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("logs Stop dispatch failures without foreground stderr noise", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-cli-stop-dispatch-silent-")); try { const result = spawnSync(process.execPath, [nativeHookScriptPath()], { cwd, input: JSON.stringify({ hook_event_name: "Stop", cwd, session_id: "sess-cli-stop-dispatch-silent", thread_id: "thread-cli-stop-dispatch-silent", turn_id: "turn-cli-stop-dispatch-silent", }), encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"], env: { ...process.env, NODE_ENV: "test", OMX_NATIVE_HOOK_TEST_THROW_STOP_DISPATCH: "1", }, }); assert.equal(result.status, 0, result.stderr || result.stdout); assert.equal(result.stderr, ""); const output = parseSingleJsonStdout(result.stdout); assert.equal(output.stopReason, "native_stop_dispatch_failure"); const logFiles = await readdir(join(cwd, ".omx", "logs")); assert.equal(logFiles.some((name) => /^native-hook-\d{4}-\d{2}-\d{2}\.jsonl$/.test(name)), true); const logName = logFiles.find((name) => /^native-hook-\d{4}-\d{2}-\d{2}\.jsonl$/.test(name)); assert.notEqual(logName, undefined); const [record] = (await readFile(join(cwd, ".omx", "logs", logName ?? ""), "utf-8")) .trim() .split("\n") .map((line) => JSON.parse(line) as Record); assert.equal(typeof record?.timestamp, "string"); assert.equal(record?.type, "native_hook_dispatch_error"); assert.equal(record?.hook_event_name, "Stop"); assert.equal(record?.session_id, "sess-cli-stop-dispatch-silent"); assert.equal(record?.thread_id, "thread-cli-stop-dispatch-silent"); assert.equal(record?.turn_id, "turn-cli-stop-dispatch-silent"); assert.equal(record?.error, "test-induced Stop dispatch failure"); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("routes native-hook CLI error logs through OMX_ROOT", async () => { const project = await mkdtemp(join(tmpdir(), "omx-native-hook-cli-log-project-")); const runtimeRoot = await mkdtemp(join(tmpdir(), "omx-native-hook-cli-log-runtime-")); const projectLogsDir = join(project, ".omx", "logs"); const sentinelPath = join(projectLogsDir, "sentinel.jsonl"); const sentinel = "preserve this project log\n"; try { await mkdir(projectLogsDir, { recursive: true }); await writeFile(sentinelPath, sentinel); const env: NodeJS.ProcessEnv = { ...process.env, OMX_ROOT: runtimeRoot, NODE_ENV: "test", OMX_NATIVE_HOOK_TEST_THROW_DISPATCH: "1", }; delete env.OMX_STATE_ROOT; delete env.OMX_TEAM_STATE_ROOT; delete env.OMX_TEAM_ROOT; delete env.OMX_PLUGIN_ROOT; delete env.OMX_PLUGIN_CACHE_PIN_PENDING; delete env.OMX_PLUGIN_CACHE_PIN_REQUIRED; const result = spawnSync(process.execPath, [nativeHookScriptPath()], { cwd: project, input: JSON.stringify({ hook_event_name: "Stop", cwd: project }), encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"], env, }); assert.equal(result.status, 0, result.stderr || result.stdout); assert.equal(result.stderr, ""); const output = parseSingleJsonStdout(result.stdout); assert.equal(output.decision, "block"); assert.equal(output.stopReason, "native_stop_dispatch_failure"); assert.equal(existsSync(join(runtimeRoot, ".omx", "logs")), true); const runtimeLogsDir = join(runtimeRoot, ".omx", "logs"); const logFiles = await readdir(runtimeLogsDir); assert.equal(logFiles.length, 1); assert.match(logFiles[0] ?? "", /^native-hook-\d{4}-\d{2}-\d{2}\.jsonl$/); const [record] = (await readFile(join(runtimeLogsDir, logFiles[0] ?? ""), "utf-8")) .trim() .split("\n") .map((line) => JSON.parse(line) as Record); assert.deepEqual( { type: record?.type, hook_event_name: record?.hook_event_name, error: record?.error, }, { type: "native_hook_dispatch_error", hook_event_name: "Stop", error: "test-induced dispatch failure", }, ); assert.equal(await readFile(sentinelPath, "utf-8"), sentinel); assert.deepEqual(await readdir(projectLogsDir), ["sentinel.jsonl"]); } finally { await rm(project, { recursive: true, force: true }); await rm(runtimeRoot, { recursive: true, force: true }); } }); it("honors OMX_STATE_ROOT, OMX_ROOT precedence, and the cwd default for native-hook logs", async () => { const stateRoot = await mkdtemp(join(tmpdir(), "omx-native-hook-state-root-")); const unusedStateRoot = await mkdtemp(join(tmpdir(), "omx-native-hook-unused-state-root-")); const omxRoot = await mkdtemp(join(tmpdir(), "omx-native-hook-root-")); const baseEnv: NodeJS.ProcessEnv = { ...process.env, NODE_ENV: "test", OMX_NATIVE_HOOK_TEST_THROW_DISPATCH: "1", }; delete baseEnv.OMX_ROOT; delete baseEnv.OMX_STATE_ROOT; delete baseEnv.OMX_TEAM_STATE_ROOT; delete baseEnv.OMX_TEAM_ROOT; delete baseEnv.OMX_PLUGIN_ROOT; delete baseEnv.OMX_PLUGIN_CACHE_PIN_PENDING; delete baseEnv.OMX_PLUGIN_CACHE_PIN_REQUIRED; const cases: Array<{ name: string; expectedRoot?: string; unusedRoot?: string; overrides: NodeJS.ProcessEnv; }> = [ { name: "OMX_STATE_ROOT alone", expectedRoot: stateRoot, overrides: { OMX_STATE_ROOT: stateRoot }, }, { name: "OMX_ROOT precedence", expectedRoot: omxRoot, unusedRoot: unusedStateRoot, overrides: { OMX_ROOT: omxRoot, OMX_STATE_ROOT: unusedStateRoot }, }, { name: "cwd default", overrides: {} }, ]; try { for (const scenario of cases) { const project = await mkdtemp(join(tmpdir(), "omx-native-hook-root-contract-")); try { const result = spawnSync(process.execPath, [nativeHookScriptPath()], { cwd: project, input: JSON.stringify({ hook_event_name: "Stop", cwd: project }), encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"], env: { ...baseEnv, ...scenario.overrides }, }); assert.equal(result.status, 0, `${scenario.name}: ${result.stderr || result.stdout}`); assert.equal(result.stderr, "", scenario.name); assert.equal( parseSingleJsonStdout(result.stdout).stopReason, "native_stop_dispatch_failure", scenario.name, ); const selectedRoot = scenario.expectedRoot ?? project; const selectedLogsDir = join(selectedRoot, ".omx", "logs"); const logFiles = await readdir(selectedLogsDir); assert.equal( logFiles.some((name) => /^native-hook-\d{4}-\d{2}-\d{2}\.jsonl$/.test(name)), true, scenario.name, ); if (scenario.expectedRoot) { assert.equal(existsSync(join(project, ".omx", "logs")), false, scenario.name); } if (scenario.unusedRoot) { assert.equal(existsSync(join(scenario.unusedRoot, ".omx", "logs")), false, scenario.name); } } finally { await rm(project, { recursive: true, force: true }); } } } finally { await rm(stateRoot, { recursive: true, force: true }); await rm(unusedStateRoot, { recursive: true, force: true }); await rm(omxRoot, { recursive: true, force: true }); } }); it("keeps CLI failure handling non-fatal when the selected native-hook log root is unwritable", async () => { const project = await mkdtemp(join(tmpdir(), "omx-native-hook-unwritable-project-")); const runtimeParent = await mkdtemp(join(tmpdir(), "omx-native-hook-unwritable-runtime-")); const runtimeRoot = join(runtimeParent, "runtime-root"); try { await writeFile(runtimeRoot, "not a directory\n"); const env: NodeJS.ProcessEnv = { ...process.env, OMX_ROOT: runtimeRoot, NODE_ENV: "test", OMX_NATIVE_HOOK_TEST_THROW_DISPATCH: "1", }; delete env.OMX_STATE_ROOT; delete env.OMX_TEAM_STATE_ROOT; delete env.OMX_TEAM_ROOT; delete env.OMX_PLUGIN_ROOT; delete env.OMX_PLUGIN_CACHE_PIN_PENDING; delete env.OMX_PLUGIN_CACHE_PIN_REQUIRED; const result = spawnSync(process.execPath, [nativeHookScriptPath()], { cwd: project, input: JSON.stringify({ hook_event_name: "Stop", cwd: project }), encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"], env, }); assert.equal(result.status, 0, result.stderr || result.stdout); assert.equal(result.stderr, ""); assert.equal(parseSingleJsonStdout(result.stdout).stopReason, "native_stop_dispatch_failure"); assert.equal(existsSync(join(project, ".omx")), false); assert.equal(await readFile(runtimeRoot, "utf-8"), "not a directory\n"); } finally { await rm(project, { recursive: true, force: true }); await rm(runtimeParent, { recursive: true, force: true }); } }); it("keeps non-Stop dispatch failures fail-closed without foreground stderr noise", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-cli-pretool-dispatch-silent-")); try { const result = spawnSync(process.execPath, [nativeHookScriptPath()], { cwd, input: JSON.stringify({ hook_event_name: "PreToolUse", cwd, session_id: "sess-cli-pretool-dispatch-silent", thread_id: "thread-cli-pretool-dispatch-silent", turn_id: "turn-cli-pretool-dispatch-silent", tool_name: "Bash", tool_input: { command: "pwd" }, }), encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"], env: { ...process.env, NODE_ENV: "test", OMX_NATIVE_HOOK_TEST_THROW_DISPATCH: "1", }, }); assert.equal(result.status, 1); assert.equal(result.stdout, ""); assert.equal(result.stderr, ""); const logFiles = await readdir(join(cwd, ".omx", "logs")); assert.equal(logFiles.some((name) => /^native-hook-\d{4}-\d{2}-\d{2}\.jsonl$/.test(name)), true); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("maps Codex events onto OMX logical surfaces", () => { assert.equal(mapCodexHookEventToOmxEvent("SessionStart"), "session-start"); assert.equal(mapCodexHookEventToOmxEvent("UserPromptSubmit"), "keyword-detector"); assert.equal(mapCodexHookEventToOmxEvent("PreToolUse"), "pre-tool-use"); assert.equal(mapCodexHookEventToOmxEvent("PostToolUse"), "post-tool-use"); assert.equal(mapCodexHookEventToOmxEvent("PreCompact"), "pre-compact"); assert.equal(mapCodexHookEventToOmxEvent("PostCompact"), "post-compact"); assert.equal(mapCodexHookEventToOmxEvent("Stop"), "stop"); }); it("does not write PreCompact stdout that Codex rejects as hook JSON", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-precompact-")); try { writePage(cwd, { filename: "architecture.md", frontmatter: { title: "Architecture", tags: ["architecture"], created: "2026-05-08T00:00:00.000Z", updated: "2026-05-08T00:00:00.000Z", sources: [], links: [], category: "architecture", confidence: "high", schemaVersion: WIKI_SCHEMA_VERSION, }, content: "\n# Architecture\n\nCompaction-relevant architecture note.\n", }); const result = await dispatchCodexNativeHook({ hook_event_name: "PreCompact", cwd, session_id: "sess-precompact", }); assert.equal(result.hookEventName, "PreCompact"); assert.equal(result.omxEventName, "pre-compact"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("emits no CLI stdout for PreCompact when no Codex action is needed", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-precompact-cli-")); try { writePage(cwd, { filename: "architecture.md", frontmatter: { title: "Architecture", tags: ["architecture"], created: "2026-05-08T00:00:00.000Z", updated: "2026-05-08T00:00:00.000Z", sources: [], links: [], category: "architecture", confidence: "high", schemaVersion: WIKI_SCHEMA_VERSION, }, content: "\n# Architecture\n\nCompaction-relevant architecture note.\n", }); const stdout = runNativeHookCli({ hook_event_name: "PreCompact", cwd, session_id: "sess-precompact-cli", }); assert.equal(stdout, ""); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not write PostCompact stdout that Codex rejects as hook JSON", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-postcompact-")); try { const result = await dispatchCodexNativeHook({ hook_event_name: "PostCompact", cwd, session_id: "sess-postcompact", }); assert.equal(result.hookEventName, "PostCompact"); assert.equal(result.omxEventName, "post-compact"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("emits no CLI stdout for PostCompact when no Codex action is needed", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-postcompact-cli-")); try { const stdout = runNativeHookCli({ hook_event_name: "PostCompact", cwd, session_id: "sess-postcompact-cli", }); assert.equal(stdout, ""); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("writes SessionStart state against the long-lived session owner pid and injects environment context", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-session-start-")); try { const result = await dispatchCodexNativeHook( { hook_event_name: "SessionStart", cwd, session_id: "sess-start-1", }, { cwd, sessionOwnerPid: 43210, }, ); assert.equal(result.omxEventName, "session-start"); const additionalContext = String( (result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? "", ); assert.match(additionalContext, /\[Execution environment\]/); assert.match(additionalContext, /native-hook \/ Codex App outside tmux/); assert.match(additionalContext, /omx team, omx hud, and omx quest(?:ion) need an attached tmux OMX CLI shell|omx team and omx hud need an attached tmux OMX CLI shell/); assert.match(additionalContext, /not available from this outside-tmux surface/); const sessionState = JSON.parse( await readFile(join(cwd, ".omx", "state", "session.json"), "utf-8"), ) as { session_id?: string; native_session_id?: string; pid?: number; launch_lineage_token?: string }; assert.equal(sessionState.session_id, "sess-start-1"); assert.equal(sessionState.native_session_id, "sess-start-1"); assert.equal(sessionState.pid, 43210); assert.equal(sessionState.launch_lineage_token, undefined, 'native SessionStart must never mint or backfill wrapper lineage authority'); const ownerState = JSON.parse( await readFile( join(cwd, ".omx", "state", "sessions", "sess-start-1", "session-owner.json"), "utf-8", ), ) as { session_id?: string; native_session_id?: string; pid?: number }; assert.equal(ownerState.session_id, "sess-start-1"); assert.equal(ownerState.native_session_id, "sess-start-1"); assert.equal(ownerState.pid, 43210); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("preserves wrapper lineage and detached metadata through shipped native SessionStart reconciliation", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-session-start-wrapper-lineage-")); try { const established = await establishLaunchSessionBinding(cwd, "omx-launch-lineage"); assert.equal(established.kind, "committed-released"); if (established.kind !== "committed-released") return; const metadata = await updateDetachedSessionMetadata(established.binding, { tmuxSessionName: "omx-detached-lineage", tmuxPaneId: "%3202", }); assert.equal(metadata.kind, "committed-released"); const pointerPath = join(cwd, ".omx", "state", "session.json"); const ownedPointer = JSON.parse(await readFile(pointerPath, "utf-8")) as Record; await writeFile(pointerPath, JSON.stringify({ ...ownedPointer, owner_omx_session_id: "omx-launch-lineage" }), "utf-8"); await dispatchCodexNativeHook( { hook_event_name: "SessionStart", cwd, session_id: "codex-native-lineage" }, { cwd, sessionOwnerPid: process.pid }, ); const sessionState = JSON.parse( await readFile(join(cwd, ".omx", "state", "session.json"), "utf-8"), ) as { session_id?: string; native_session_id?: string; owner_omx_session_id?: string; started_at?: string; launch_lineage_token?: string; tmux_session_name?: string; tmux_pane_id?: string; }; assert.equal(sessionState.session_id, "omx-launch-lineage"); assert.equal(sessionState.native_session_id, "codex-native-lineage"); assert.equal(sessionState.owner_omx_session_id, "omx-launch-lineage"); assert.equal(typeof sessionState.started_at, "string"); assert.equal(sessionState.launch_lineage_token, established.binding.launchLineageToken); assert.equal(sessionState.tmux_session_name, "omx-detached-lineage"); assert.equal(sessionState.tmux_pane_id, "%3202"); const firstFinalization = finalizeBoundOnce(established.binding, "test"); const secondFinalization = finalizeBoundOnce(established.binding, "duplicate"); assert.equal(firstFinalization, secondFinalization); assert.equal((await firstFinalization).finalized, true); assert.equal((await closeLaunchSessionBindingOnce(established.binding)).status, "closed"); assert.equal((await closeLaunchSessionBindingOnce(established.binding)).status, "closed"); } finally { await rm(cwd, { recursive: true, force: true }); } }); it('does not advertise a neutralized Ralplan seed in SessionStart context', async () => { const cwd = await mkdtemp(join(tmpdir(), 'omx-native-hook-neutralized-ralplan-')); const previousSessionId = process.env.OMX_SESSION_ID; try { const sessionId = 'neutralized-session'; await writeSessionStart(cwd, sessionId); const directory = join(cwd, '.omx', 'state', 'sessions', sessionId); await mkdir(directory, { recursive: true }); await writeFile(join(directory, 'ralplan-state.json'), JSON.stringify({ active: true, mode: 'ralplan', current_phase: 'planning', started_at: '2026-01-01T00:00:00.000Z', updated_at: '2026-01-01T00:00:00.000Z', session_id: sessionId })); await writeFile(join(directory, 'skill-active-state.json'), JSON.stringify({ version: 1, active: true, skill: 'ralplan', keyword: '$RALPLAN', phase: 'planning', activated_at: '2026-01-01T00:00:00.000Z', updated_at: '2026-01-01T00:00:00.000Z', source: 'keyword-detector', session_id: sessionId, initialized_mode: 'ralplan', initialized_state_path: `.omx/state/sessions/${sessionId}/ralplan-state.json`, active_skills: [{ skill: 'ralplan', active: true, phase: 'planning', activated_at: '2026-01-01T00:00:00.000Z', updated_at: '2026-01-01T00:00:00.000Z', session_id: sessionId }] })); process.env.OMX_SESSION_ID = sessionId; assert.equal(await neutralizeOwnedRoutingRalplan(cwd), true); const result = await dispatchCodexNativeHook({ hook_event_name: 'SessionStart', cwd, session_id: sessionId }, { cwd }); const context = String((result.outputJson as { hookSpecificOutput?: { additionalContext?: string } } | null)?.hookSpecificOutput?.additionalContext ?? ''); assert.doesNotMatch(context, /- ralplan phase:/); } finally { previousSessionId === undefined ? delete process.env.OMX_SESSION_ID : process.env.OMX_SESSION_ID = previousSessionId; await rm(cwd, { recursive: true, force: true }); } }); it("reconciles native SessionStart on Windows EPERM with one degraded-durability warning", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-session-start-windows-eperm-")); const originalWrite = process.stderr.write; const warnings: string[] = []; process.stderr.write = ((value: string) => { warnings.push(value); return true; }) as typeof process.stderr.write; try { await dispatchCodexNativeHook( { hook_event_name: "SessionStart", cwd, session_id: "sess-start-windows-eperm" }, { cwd, sessionStartOptions: { platform: "win32", regularFileSync: async () => { throw Object.assign(new Error("EPERM"), { code: "EPERM" }); }, }, }, ); assert.deepEqual(warnings, [ "[omx] warning: Windows EPERM regular-file fsync unsupported in session pointer start/reconcile; operation succeeded with degraded durability.\n", ]); } finally { process.stderr.write = originalWrite; await rm(cwd, { recursive: true, force: true }); } }); it("adds resume-by-id instructions for persisted subagents on SessionStart resume", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-subagent-reopen-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "omx-reopen-session"; await writeSessionStart(cwd, sessionId, { nativeSessionId: "codex-leader-reopen", pid: process.pid }); await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { [sessionId]: { session_id: sessionId, leader_thread_id: "codex-leader-reopen", updated_at: "2026-07-09T00:00:00.000Z", threads: { "codex-leader-reopen": { thread_id: "codex-leader-reopen", kind: "leader", first_seen_at: "2026-07-09T00:00:00.000Z", last_seen_at: "2026-07-09T00:00:00.000Z", turn_count: 1, }, "thread-architect-reopen": { thread_id: "thread-architect-reopen", kind: "subagent", first_seen_at: "2026-07-09T00:01:00.000Z", last_seen_at: "2026-07-09T00:01:00.000Z", turn_count: 2, role: "architect", lane_id: "plan-review", scope: "SessionStart reopen", status: "available", provenance_kind: "native_subagent", direct_child_root_id: "codex-leader-reopen", direct_child_parent_id: "codex-leader-reopen", last_handoff_summary: "reviewed the restart plan", }, }, }, }, }); const result = await dispatchCodexNativeHook( { hook_event_name: "SessionStart", cwd, session_id: "codex-leader-reopen", source: "resume", available_tools: ["resume_agent"], }, { cwd, sessionOwnerPid: process.pid }, ); const additionalContext = String( (result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? "", ); assert.match(additionalContext, /\[Persisted subagent reopen\]/); assert.match(additionalContext, /resume_agent\("thread-architect-reopen"\)/); assert.match(additionalContext, /role: architect; lane: plan-review; scope: SessionStart reopen; status: available/); assert.match(additionalContext, /saved subagent ids found: 1/); const tracking = JSON.parse(await readFile(join(stateDir, "subagent-tracking.json"), "utf-8")) as { sessions?: Record }>; }; assert.match( tracking.sessions?.[sessionId]?.threads?.["thread-architect-reopen"]?.resume_requested_at ?? "", /^\d{4}-\d{2}-\d{2}T/, ); } finally { await rm(cwd, { recursive: true, force: true }); } }); for (const status of ["closed", "available"] as const) { it(`issue #3284 excludes the current root when persisted as a ${status} subagent`, async () => { const cwd = await mkdtemp(join(tmpdir(), `omx-3284-root-self-${status}-`)); try { const stateDir = join(cwd, ".omx", "state"); const canonicalSessionId = `omx-3284-${status}`; const rootNativeSessionId = `codex-root-${status}`; const childId = `codex-child-${status}`; await writeSessionStart(cwd, canonicalSessionId, { nativeSessionId: rootNativeSessionId, pid: process.pid }); await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { [canonicalSessionId]: { session_id: canonicalSessionId, leader_thread_id: `turn-like-${status}`, updated_at: "2026-07-23T00:00:00.000Z", threads: { [rootNativeSessionId]: { thread_id: rootNativeSessionId, kind: "subagent", status, first_seen_at: "2026-07-23T00:00:00.000Z", last_seen_at: "2026-07-23T00:00:00.000Z", turn_count: 1 }, [childId]: { thread_id: childId, kind: "subagent", status: "available", provenance_kind: "native_subagent", direct_child_root_id: rootNativeSessionId, direct_child_parent_id: rootNativeSessionId, first_seen_at: "2026-07-23T00:01:00.000Z", last_seen_at: "2026-07-23T00:01:00.000Z", turn_count: 1 }, } }, [rootNativeSessionId]: { session_id: rootNativeSessionId, leader_thread_id: `turn-like-correlation-${status}`, updated_at: "2026-07-23T00:00:00.000Z", threads: { [rootNativeSessionId]: { thread_id: rootNativeSessionId, kind: "subagent", status, provenance_kind: "native_subagent", direct_child_root_id: rootNativeSessionId, direct_child_parent_id: rootNativeSessionId, resume_requested_at: "2026-07-23T00:02:00.000Z", first_seen_at: "2026-07-23T00:00:00.000Z", last_seen_at: "2026-07-23T00:00:00.000Z", turn_count: 1 }, } }, }, }); const result = await dispatchCodexNativeHook({ hook_event_name: "SessionStart", cwd, session_id: rootNativeSessionId, source: "resume", available_tools: ["resume_agent"] }, { cwd, sessionOwnerPid: process.pid }); const context = String((result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? ""); assert.equal(context.includes(`resume_agent(${JSON.stringify(rootNativeSessionId)})`), false); assert.equal(context.includes(`resume_agent(${JSON.stringify(childId)})`), true); const tracking = JSON.parse(await readFile(join(stateDir, "subagent-tracking.json"), "utf-8")); assert.equal(tracking.sessions[canonicalSessionId].threads[rootNativeSessionId].resume_requested_at, undefined); assert.equal(tracking.sessions[canonicalSessionId].threads[rootNativeSessionId].kind, "leader"); assert.equal(tracking.sessions[canonicalSessionId].leader_thread_id, rootNativeSessionId); assert.equal(tracking.sessions[rootNativeSessionId].threads[rootNativeSessionId].kind, "leader"); assert.equal(tracking.sessions[rootNativeSessionId].threads[rootNativeSessionId].resume_requested_at, undefined); assert.equal(tracking.sessions[rootNativeSessionId].threads[rootNativeSessionId].direct_child_root_id, undefined); assert.equal(tracking.sessions[rootNativeSessionId].leader_thread_id, rootNativeSessionId); assert.match(tracking.sessions[canonicalSessionId].threads[childId].resume_requested_at, /^\d{4}-\d{2}-\d{2}T/); } finally { await rm(cwd, { recursive: true, force: true }); } }); } it("issue #3284 excludes a delegated top-level root labelled thread_source subagent with no parent evidence", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-3284-delegated-root-")); try { const stateDir = join(cwd, ".omx", "state"); const canonicalSessionId = "omx-3284-delegated"; const rootNativeSessionId = "codex-delegated-root"; const childId = "codex-delegated-child"; await writeSessionStart(cwd, canonicalSessionId, { nativeSessionId: rootNativeSessionId, pid: process.pid }); // Evidence comment 5069022871: rollout provenance marks the task // thread_source "subagent", yet it has no parent_thread_id/forked_from_id // and is the root of its current collaboration tree. const transcriptPath = join(cwd, "rollout-delegated-root.jsonl"); await writeFile(transcriptPath, `${JSON.stringify({ type: "session_meta", payload: { id: rootNativeSessionId, source: { thread_source: "subagent" } }, })}\n`); await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { [canonicalSessionId]: { session_id: canonicalSessionId, leader_thread_id: "turn-like-foreign", updated_at: "2026-07-24T00:00:00.000Z", threads: { [rootNativeSessionId]: { thread_id: rootNativeSessionId, kind: "subagent", status: "closed", first_seen_at: "2026-07-24T00:00:00.000Z", last_seen_at: "2026-07-24T00:00:00.000Z", turn_count: 1 }, [childId]: { thread_id: childId, kind: "subagent", status: "available", provenance_kind: "native_subagent", direct_child_root_id: rootNativeSessionId, direct_child_parent_id: rootNativeSessionId, first_seen_at: "2026-07-24T00:01:00.000Z", last_seen_at: "2026-07-24T00:01:00.000Z", turn_count: 1 }, } }, }, }); const result = await dispatchCodexNativeHook({ hook_event_name: "SessionStart", cwd, session_id: rootNativeSessionId, source: "resume", transcript_path: transcriptPath, available_tools: ["resume_agent"] }, { cwd, sessionOwnerPid: process.pid }); const context = String((result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? ""); assert.equal(context.includes(`resume_agent(${JSON.stringify(rootNativeSessionId)})`), false); assert.equal(context.includes(`resume_agent(${JSON.stringify(childId)})`), true); const tracking = JSON.parse(await readFile(join(stateDir, "subagent-tracking.json"), "utf-8")); assert.equal(tracking.sessions[canonicalSessionId].threads[rootNativeSessionId].kind, "leader"); assert.equal(tracking.sessions[canonicalSessionId].leader_thread_id, rootNativeSessionId); assert.equal(tracking.sessions[canonicalSessionId].threads[rootNativeSessionId].resume_requested_at, undefined); assert.match(tracking.sessions[canonicalSessionId].threads[childId].resume_requested_at, /^\d{4}-\d{2}-\d{2}T/); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("issue #3284 repairs a root inversion on a non-reopen SessionStart source without emitting reopen output", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-3284-repair-other-source-")); try { const stateDir = join(cwd, ".omx", "state"); const canonicalSessionId = "omx-3284-other-source"; const rootNativeSessionId = "codex-root-other-source"; await writeSessionStart(cwd, canonicalSessionId, { nativeSessionId: rootNativeSessionId, pid: process.pid }); await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { [canonicalSessionId]: { session_id: canonicalSessionId, leader_thread_id: "turn-like-foreign", updated_at: "2026-07-24T00:00:00.000Z", threads: { [rootNativeSessionId]: { thread_id: rootNativeSessionId, kind: "subagent", status: "closed", first_seen_at: "2026-07-24T00:00:00.000Z", last_seen_at: "2026-07-24T00:00:00.000Z", turn_count: 1 }, } }, }, }); const result = await dispatchCodexNativeHook({ hook_event_name: "SessionStart", cwd, session_id: rootNativeSessionId, source: "clear" }, { cwd, sessionOwnerPid: process.pid }); const context = String((result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? ""); assert.doesNotMatch(context, /\[Persisted subagent reopen\]/); const tracking = JSON.parse(await readFile(join(stateDir, "subagent-tracking.json"), "utf-8")); assert.equal(tracking.sessions[canonicalSessionId].threads[rootNativeSessionId].kind, "leader"); assert.equal(tracking.sessions[canonicalSessionId].leader_thread_id, rootNativeSessionId); } finally { await rm(cwd, { recursive: true, force: true }); } }); for (const source of ["startup", "resume"] as const) { it(`issue #3284 quarantines root reopen authority without asserting leader identity for a self-parented child SessionStart on ${source}`, async () => { const cwd = await mkdtemp(join(tmpdir(), `omx-3284-self-parented-${source}-`)); try { const stateDir = join(cwd, ".omx", "state"); const canonicalSessionId = `omx-3284-self-parented-${source}`; const nativeRoleThreadId = `codex-role-thread-${source}`; await mkdir(join(stateDir, "sessions", canonicalSessionId), { recursive: true }); await writeSessionStart(cwd, canonicalSessionId, { nativeSessionId: nativeRoleThreadId, pid: process.pid }); const transcriptPath = join(cwd, `rollout-self-parented-${source}.jsonl`); await writeFile(transcriptPath, `${JSON.stringify({ type: "session_meta", payload: { id: nativeRoleThreadId, source: { subagent: { thread_spawn: { parent_thread_id: nativeRoleThreadId, depth: 1, agent_nickname: "Architect", agent_role: "architect" } } } }, })}\n`); await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { [canonicalSessionId]: { session_id: canonicalSessionId, leader_thread_id: "turn-like-foreign", updated_at: "2026-07-24T00:00:00.000Z", threads: { [nativeRoleThreadId]: { thread_id: nativeRoleThreadId, kind: "subagent", status: "available", provenance_kind: "native_subagent", direct_child_root_id: nativeRoleThreadId, direct_child_parent_id: nativeRoleThreadId, resume_requested_at: "2026-07-24T00:02:00.000Z", first_seen_at: "2026-07-24T00:00:00.000Z", last_seen_at: "2026-07-24T00:00:00.000Z", turn_count: 1 }, } }, }, }); const result = await dispatchCodexNativeHook({ hook_event_name: "SessionStart", cwd, session_id: nativeRoleThreadId, source, transcript_path: transcriptPath }, { cwd, sessionOwnerPid: process.pid }); const context = String((result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? ""); assert.doesNotMatch(context, /\[Persisted subagent reopen\]/); assert.doesNotMatch(context, /resume_agent\(/); // The transcript marker names this very root as its own child, so it is // not distinct-child evidence and cannot override the authenticated // pointer identity. The root's reopen authority is quarantined without // asserting leader identity, so descriptive subagent evidence survives. const tracking = JSON.parse(await readFile(join(stateDir, "subagent-tracking.json"), "utf-8")); const rootRecord = tracking.sessions[canonicalSessionId].threads[nativeRoleThreadId]; assert.equal(rootRecord.kind, "subagent"); assert.equal(rootRecord.reopen_authority_revoked, true); assert.equal(rootRecord.reopen_authority_conflict_reason, "contradictory_root_child_evidence"); assert.equal(rootRecord.resume_requested_at, undefined); } finally { await rm(cwd, { recursive: true, force: true }); } }); } it("issue #3284 suppresses root handling only for an authenticated direct-child SessionStart", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-3284-distinct-child-suppression-")); try { const stateDir = join(cwd, ".omx", "state"); const canonicalSessionId = "omx-3284-distinct-child"; const rootNativeSessionId = "root-3284-distinct-child"; const childId = "child-3284-distinct"; await mkdir(join(stateDir, "sessions", canonicalSessionId), { recursive: true }); await writeSessionStart(cwd, canonicalSessionId, { nativeSessionId: rootNativeSessionId, pid: process.pid }); const transcriptPath = join(cwd, "rollout-distinct-child.jsonl"); await writeFile(transcriptPath, `${JSON.stringify({ type: "session_meta", payload: { id: childId, source: { subagent: { thread_spawn: { parent_thread_id: rootNativeSessionId, depth: 1 } } } }, })}\n`); await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { [canonicalSessionId]: { session_id: canonicalSessionId, leader_thread_id: "turn-like-foreign", updated_at: "2026-07-24T00:00:00.000Z", threads: { [rootNativeSessionId]: { thread_id: rootNativeSessionId, kind: "subagent", status: "available", first_seen_at: "2026-07-24T00:00:00.000Z", last_seen_at: "2026-07-24T00:00:00.000Z", turn_count: 1 }, } }, }, }); // The event's own unambiguous native id IS the marker's child, and the // marker's parent is exactly the authenticated native root, so this is a // real direct-child session start and must not act as a root observation. const result = await dispatchCodexNativeHook({ hook_event_name: "SessionStart", cwd, session_id: childId, sessionId: childId, source: "resume", transcript_path: transcriptPath }, { cwd, sessionOwnerPid: process.pid }); const context = String((result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? ""); assert.doesNotMatch(context, /resume_agent\(/); const tracking = JSON.parse(await readFile(join(stateDir, "subagent-tracking.json"), "utf-8")); assert.equal(tracking.sessions[canonicalSessionId].threads[rootNativeSessionId].kind, "subagent"); assert.equal(tracking.sessions[canonicalSessionId].threads[rootNativeSessionId].reopen_authority_revoked, undefined); } finally { await rm(cwd, { recursive: true, force: true }); } }); for (const marker of [ { name: "arbitrary-nonexistent-child", childId: "made-up-child", parentId: "made-up-parent" }, { name: "nested-grandchild", childId: "grandchild-3284", parentId: "intermediate-3284" }, { name: "foreign-root-child", childId: "foreign-child-3284", parentId: "foreign-root-3284" }, ] as const) { it(`issue #3284 does not let an unauthenticated ${marker.name} marker suppress root quarantine`, async () => { const cwd = await mkdtemp(join(tmpdir(), `omx-3284-unauth-marker-${marker.name}-`)); try { const stateDir = join(cwd, ".omx", "state"); const canonicalSessionId = `omx-3284-unauth-${marker.name}`; const rootNativeSessionId = `root-3284-unauth-${marker.name}`; await mkdir(join(stateDir, "sessions", canonicalSessionId), { recursive: true }); await writeSessionStart(cwd, canonicalSessionId, { nativeSessionId: rootNativeSessionId, pid: process.pid }); const transcriptPath = join(cwd, `unauth-${marker.name}.jsonl`); await writeFile(transcriptPath, `${JSON.stringify({ type: "session_meta", payload: { id: marker.childId, source: { subagent: { thread_spawn: { parent_thread_id: marker.parentId, depth: 1 } } } }, })}\n`); await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { [canonicalSessionId]: { session_id: canonicalSessionId, leader_thread_id: "turn-like-foreign", updated_at: "2026-07-24T00:00:00.000Z", threads: { [rootNativeSessionId]: { thread_id: rootNativeSessionId, kind: "subagent", status: "available", provenance_kind: "native_subagent", direct_child_root_id: rootNativeSessionId, direct_child_parent_id: rootNativeSessionId, resume_requested_at: "2026-07-24T00:02:00.000Z", first_seen_at: "2026-07-24T00:00:00.000Z", last_seen_at: "2026-07-24T00:00:00.000Z", turn_count: 1 }, } }, }, }); const result = await dispatchCodexNativeHook({ hook_event_name: "SessionStart", cwd, session_id: rootNativeSessionId, source: "resume", transcript_path: transcriptPath }, { cwd, sessionOwnerPid: process.pid }); const context = String((result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? ""); assert.doesNotMatch(context, /resume_agent\(/); // The marker is not bound to the authenticated event identity, so the // pointer-authoritative root never gains reopen authority: it is denied // either by the dispatcher's foreign-parent revocation or by the // contradictory-evidence quarantine. Both are fail-closed. const rootRecord = JSON.parse(await readFile(join(stateDir, "subagent-tracking.json"), "utf-8")) .sessions[canonicalSessionId].threads[rootNativeSessionId]; assert.equal(rootRecord.reopen_authority_revoked, true); assert.ok( ["contradictory_root_child_evidence", "foreign_parent", "identity_untrusted"].includes(rootRecord.reopen_authority_conflict_reason), `unexpected conflict reason: ${rootRecord.reopen_authority_conflict_reason}`, ); // Reopen eligibility is what matters: the revoked root can never be // emitted regardless of leftover descriptive bookkeeping. assert.equal(rootRecord.direct_child_root_id !== undefined && rootRecord.reopen_authority_revoked !== true, false); } finally { await rm(cwd, { recursive: true, force: true }); } }); } it("issue #3284 fails closed when SessionStart native id aliases conflict", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-3284-alias-conflict-")); try { const stateDir = join(cwd, ".omx", "state"); const canonicalSessionId = "omx-3284-alias"; const rootNativeSessionId = "codex-root-alias"; await writeSessionStart(cwd, canonicalSessionId, { nativeSessionId: rootNativeSessionId, pid: process.pid }); await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { [canonicalSessionId]: { session_id: canonicalSessionId, updated_at: "2026-07-23T00:00:00.000Z", threads: { child: { thread_id: "child", kind: "subagent", status: "available", provenance_kind: "native_subagent", direct_child_root_id: rootNativeSessionId, direct_child_parent_id: rootNativeSessionId, first_seen_at: "2026-07-23T00:00:00.000Z", last_seen_at: "2026-07-23T00:00:00.000Z", turn_count: 1 }, } }, } }); const result = await dispatchCodexNativeHook({ hook_event_name: "SessionStart", cwd, session_id: rootNativeSessionId, sessionId: "foreign-root", source: "resume" }, { cwd, sessionOwnerPid: process.pid }); const context = String((result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? ""); assert.doesNotMatch(context, /\[Persisted subagent reopen\]/); const tracking = JSON.parse(await readFile(join(stateDir, "subagent-tracking.json"), "utf-8")); assert.equal(tracking.sessions[canonicalSessionId].threads.child.resume_requested_at, undefined); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("issue #3284 enforces the native alias truth table and root-context failure precedence", () => { assert.deepEqual(readUnambiguousSessionStartNativeId(undefined), { ok: false, reason: "missing" }); assert.deepEqual(readUnambiguousSessionStartNativeId({ session_id: "root", sessionId: "root" }), { ok: true, value: "root" }); assert.deepEqual(readUnambiguousSessionStartNativeId({ session_id: "root", sessionId: "foreign" }), { ok: false, reason: "conflict" }); assert.deepEqual(readUnambiguousSessionStartNativeId({ session_id: "root", sessionId: undefined }), { ok: false, reason: "malformed" }); assert.deepEqual(readUnambiguousSessionStartNativeId({ session_id: 42, sessionId: "root" } as never), { ok: false, reason: "malformed" }); assert.deepEqual(readUnambiguousSessionStartNativeId({ session_id: "root" }), { ok: true, value: "root" }); assert.deepEqual(readUnambiguousSessionStartNativeId({ sessionId: "root" }), { ok: true, value: "root" }); assert.deepEqual(readUnambiguousSessionStartNativeId({ session_id: "foreign", sessionId: "root" }), { ok: false, reason: "conflict" }); assert.deepEqual(readUnambiguousSessionStartNativeId({ session_id: undefined, sessionId: "root" }), { ok: false, reason: "malformed" }); assert.deepEqual(readUnambiguousSessionStartNativeId({ session_id: "root", sessionId: 42 } as never), { ok: false, reason: "malformed" }); assert.deepEqual(readUnambiguousSessionStartNativeId({ session_id: "", sessionId: 42 } as never), { ok: false, reason: "malformed" }); assert.deepEqual(readUnambiguousSessionStartNativeId({ sessionId: "" }), { ok: false, reason: "malformed" }); assert.deepEqual(readUnambiguousSessionStartNativeId({ session_id: 42 } as never), { ok: false, reason: "malformed" }); assert.deepEqual(readUnambiguousSessionStartNativeId({ session_id: "", sessionId: "" }), { ok: false, reason: "malformed" }); assert.deepEqual(readUnambiguousSessionStartNativeId({ session_id: " root ", sessionId: "root" }), { ok: true, value: "root" }); const cwd = resolve("/tmp/omx-3284-root-context"); const baseState = { session_id: "canonical", native_session_id: "root", cwd, pid: process.pid, started_at: "2026-07-23T00:00:00.000Z" } as any; assert.deepEqual(resolvePersistedReopenRootContext(cwd, "canonical", { session_id: "root" }, null), { ok: false, reason: "pointer_not_usable" }); assert.deepEqual(resolvePersistedReopenRootContext(cwd, "canonical", { session_id: "root" }, { ...baseState, cwd: "" }), { ok: false, reason: "pointer_cwd_missing" }); assert.deepEqual(resolvePersistedReopenRootContext(cwd, "canonical", { session_id: "root" }, { ...baseState, session_id: "" }), { ok: false, reason: "pointer_session_missing" }); assert.deepEqual(resolvePersistedReopenRootContext(cwd, "canonical", { session_id: "root" }, { ...baseState, native_session_id: "" }), { ok: false, reason: "pointer_native_root_missing" }); assert.deepEqual(resolvePersistedReopenRootContext(cwd, "canonical", { session_id: "root" }, { ...baseState, cwd: resolve("/tmp/foreign") }), { ok: false, reason: "pointer_cwd_mismatch" }); assert.deepEqual(resolvePersistedReopenRootContext(cwd, "", { session_id: "root" }, baseState), { ok: false, reason: "selected_canonical_missing" }); assert.deepEqual(resolvePersistedReopenRootContext(cwd, "foreign", { session_id: "root" }, baseState), { ok: false, reason: "canonical_mismatch" }); assert.deepEqual(resolvePersistedReopenRootContext(cwd, "canonical", {}, baseState), { ok: false, reason: "event_native_missing" }); assert.deepEqual(resolvePersistedReopenRootContext(cwd, "canonical", { session_id: "" }, baseState), { ok: false, reason: "event_native_malformed" }); assert.deepEqual(resolvePersistedReopenRootContext(cwd, "canonical", { session_id: "other" }, baseState), { ok: false, reason: "native_root_mismatch" }); assert.deepEqual(resolvePersistedReopenRootContext(cwd, "canonical", { session_id: "root" }, baseState), { ok: true, sessionId: "canonical", rootNativeSessionId: "root" }); assert.deepEqual(resolvePersistedReopenRootContext(cwd, "canonical", { session_id: "root", sessionId: "foreign" }, baseState), { ok: false, reason: "event_native_conflict" }); assert.deepEqual(resolvePersistedReopenRootContext(cwd, "canonical", { session_id: "root", sessionId: "foreign" }, null), { ok: false, reason: "pointer_not_usable" }); assert.deepEqual(resolvePersistedReopenRootContext(cwd, "foreign", {}, baseState), { ok: false, reason: "canonical_mismatch" }); assert.deepEqual(resolvePersistedReopenRootContext(cwd, "canonical", { session_id: "root" }, { ...baseState, owner_omx_session_id: "foreign", owner_codex_session_id: "foreign", codex_session_id: "foreign", previous_native_session_id: "foreign" }), { ok: true, sessionId: "canonical", rootNativeSessionId: "root" }); }); it("issue #3284 withholds authority when transcript and hook child identities disagree", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-3284-child-mismatch-")); try { const stateDir = join(cwd, ".omx", "state"); const canonicalSessionId = "omx-3284-child-mismatch"; const rootNativeSessionId = "root-3284-child-mismatch"; const hookChildId = "hook-child-3284"; await writeSessionStart(cwd, canonicalSessionId, { nativeSessionId: rootNativeSessionId, pid: process.pid }); const cachedThread = { thread_id: hookChildId, kind: "subagent", provenance_kind: "native_subagent", direct_child_root_id: rootNativeSessionId, direct_child_parent_id: rootNativeSessionId, status: "available", first_seen_at: "2026-07-23T00:00:00.000Z", last_seen_at: "2026-07-23T00:00:00.000Z", turn_count: 1 }; await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { [canonicalSessionId]: { session_id: canonicalSessionId, updated_at: "2026-07-23T00:00:00.000Z", threads: { [hookChildId]: cachedThread } }, [rootNativeSessionId]: { session_id: rootNativeSessionId, updated_at: "2026-07-23T00:00:00.000Z", threads: { [hookChildId]: cachedThread } }, } }); const transcriptPath = join(cwd, "child-mismatch.jsonl"); await writeFile(transcriptPath, `${JSON.stringify({ type: "session_meta", payload: { id: "transcript-child-3284", source: { subagent: { thread_spawn: { parent_thread_id: rootNativeSessionId } } } } })}\n`); await dispatchCodexNativeHook({ hook_event_name: "SessionStart", cwd, session_id: hookChildId, sessionId: hookChildId, transcript_path: transcriptPath }, { cwd, sessionOwnerPid: process.pid }); const tracking = JSON.parse(await readFile(join(stateDir, "subagent-tracking.json"), "utf-8")); const child = tracking.sessions[canonicalSessionId].threads[hookChildId]; assert.equal(child.kind, "subagent"); assert.equal(child.reopen_authority_revoked, true); const rootResume = await dispatchCodexNativeHook({ hook_event_name: "SessionStart", cwd, session_id: rootNativeSessionId, source: "resume" }, { cwd, sessionOwnerPid: process.pid }); const context = String((rootResume.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? ""); assert.equal(context.includes(`resume_agent(${JSON.stringify(hookChildId)})`), false); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("issue #3284 revokes cached authority across conflicting child aliases", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-3284-child-alias-revoke-")); try { const stateDir = join(cwd, ".omx", "state"); const canonicalSessionId = "omx-3284-child-alias-revoke"; const rootNativeSessionId = "root-3284-child-alias-revoke"; const realChildId = "real-child-3284"; const fakeChildId = "fake-child-3284"; await writeSessionStart(cwd, canonicalSessionId, { nativeSessionId: rootNativeSessionId, pid: process.pid }); const cachedThread = { thread_id: realChildId, kind: "subagent", provenance_kind: "native_subagent", direct_child_root_id: rootNativeSessionId, direct_child_parent_id: rootNativeSessionId, status: "available", first_seen_at: "2026-07-23T00:00:00.000Z", last_seen_at: "2026-07-23T00:00:00.000Z", turn_count: 1 }; await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { [canonicalSessionId]: { session_id: canonicalSessionId, updated_at: "2026-07-23T00:00:00.000Z", threads: { [realChildId]: cachedThread } }, [rootNativeSessionId]: { session_id: rootNativeSessionId, updated_at: "2026-07-23T00:00:00.000Z", threads: { [realChildId]: cachedThread } }, } }); const transcriptPath = join(cwd, "child-alias-revoke.jsonl"); await writeFile(transcriptPath, `${JSON.stringify({ type: "session_meta", payload: { id: realChildId, source: { subagent: { thread_spawn: { parent_thread_id: rootNativeSessionId } } } } })}\n`); await dispatchCodexNativeHook({ hook_event_name: "SessionStart", cwd, session_id: fakeChildId, sessionId: realChildId, transcript_path: transcriptPath }, { cwd, sessionOwnerPid: process.pid }); const tracking = JSON.parse(await readFile(join(stateDir, "subagent-tracking.json"), "utf-8")); assert.equal(tracking.sessions[canonicalSessionId].threads[realChildId].reopen_authority_revoked, true); const rootResume = await dispatchCodexNativeHook({ hook_event_name: "SessionStart", cwd, session_id: rootNativeSessionId, source: "resume" }, { cwd, sessionOwnerPid: process.pid }); const context = String((rootResume.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? ""); assert.equal(context.includes(`resume_agent(${JSON.stringify(realChildId)})`), false); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("issue #3284 revokes cached authority when the child reappears under a foreign parent", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-3284-foreign-parent-revoke-")); try { const stateDir = join(cwd, ".omx", "state"); const canonicalSessionId = "omx-3284-foreign-parent-revoke"; const rootNativeSessionId = "root-3284-foreign-parent-revoke"; const childId = "child-3284-foreign-parent"; await writeSessionStart(cwd, canonicalSessionId, { nativeSessionId: rootNativeSessionId, pid: process.pid }); const cachedThread = { thread_id: childId, kind: "subagent", provenance_kind: "native_subagent", direct_child_root_id: rootNativeSessionId, direct_child_parent_id: rootNativeSessionId, status: "available", first_seen_at: "2026-07-23T00:00:00.000Z", last_seen_at: "2026-07-23T00:00:00.000Z", turn_count: 1 }; await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { [canonicalSessionId]: { session_id: canonicalSessionId, updated_at: "2026-07-23T00:00:00.000Z", threads: { [childId]: cachedThread } }, [rootNativeSessionId]: { session_id: rootNativeSessionId, updated_at: "2026-07-23T00:00:00.000Z", threads: { [childId]: cachedThread } }, } }); const transcriptPath = join(cwd, "foreign-parent-revoke.jsonl"); await writeFile(transcriptPath, `${JSON.stringify({ type: "session_meta", payload: { id: childId, source: { subagent: { thread_spawn: { parent_thread_id: "foreign-parent" } } } } })}\n`); await dispatchCodexNativeHook({ hook_event_name: "SessionStart", cwd, session_id: childId, sessionId: childId, transcript_path: transcriptPath }, { cwd, sessionOwnerPid: process.pid }); const tracking = JSON.parse(await readFile(join(stateDir, "subagent-tracking.json"), "utf-8")); assert.equal(tracking.sessions[canonicalSessionId].threads[childId].reopen_authority_revoked, true); const rootResume = await dispatchCodexNativeHook({ hook_event_name: "SessionStart", cwd, session_id: rootNativeSessionId, source: "resume" }, { cwd, sessionOwnerPid: process.pid }); const context = String((rootResume.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? ""); assert.equal(context.includes(`resume_agent(${JSON.stringify(childId)})`), false); } finally { await rm(cwd, { recursive: true, force: true }); } }); for (const malformedFirstAlias of ["", 42] as const) { it(`issue #3284 revokes cached authority when the first child alias is ${JSON.stringify(malformedFirstAlias)}`, async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-3284-child-malformed-first-")); try { const stateDir = join(cwd, ".omx", "state"); const canonicalSessionId = "omx-3284-child-malformed-first"; const rootNativeSessionId = "root-3284-child-malformed-first"; const childId = "child-3284-malformed-first"; await writeSessionStart(cwd, canonicalSessionId, { nativeSessionId: rootNativeSessionId, pid: process.pid }); const cachedThread = { thread_id: childId, kind: "subagent", provenance_kind: "native_subagent", direct_child_root_id: rootNativeSessionId, direct_child_parent_id: rootNativeSessionId, status: "available", first_seen_at: "2026-07-23T00:00:00.000Z", last_seen_at: "2026-07-23T00:00:00.000Z", turn_count: 1 }; await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { [canonicalSessionId]: { session_id: canonicalSessionId, updated_at: "2026-07-23T00:00:00.000Z", threads: { [childId]: cachedThread } }, [rootNativeSessionId]: { session_id: rootNativeSessionId, updated_at: "2026-07-23T00:00:00.000Z", threads: { [childId]: cachedThread } }, } }); const transcriptPath = join(cwd, "child-malformed-first.jsonl"); await writeFile(transcriptPath, `${JSON.stringify({ type: "session_meta", payload: { id: childId, source: { subagent: { thread_spawn: { parent_thread_id: rootNativeSessionId } } } } })}\n`); await dispatchCodexNativeHook({ hook_event_name: "SessionStart", cwd, session_id: malformedFirstAlias, sessionId: childId, transcript_path: transcriptPath } as never, { cwd, sessionOwnerPid: process.pid }); const tracking = JSON.parse(await readFile(join(stateDir, "subagent-tracking.json"), "utf-8")); assert.equal(tracking.sessions[canonicalSessionId].threads[childId].reopen_authority_revoked, true); const rootResume = await dispatchCodexNativeHook({ hook_event_name: "SessionStart", cwd, session_id: rootNativeSessionId, source: "resume" }, { cwd, sessionOwnerPid: process.pid }); const context = String((rootResume.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? ""); assert.equal(context.includes(`resume_agent(${JSON.stringify(childId)})`), false); } finally { await rm(cwd, { recursive: true, force: true }); } }); } for (const scenario of [ { name: "valid-first-blank-second", aliases: { session_id: "child-3284-no-candidate", sessionId: "" } }, { name: "both-blank", aliases: { session_id: "", sessionId: "" } }, { name: "both-non-string", aliases: { session_id: 42, sessionId: false } }, { name: "both-absent", aliases: {} }, ] as const) { it(`issue #3284 revokes transcript-identified cached authority for ${scenario.name}`, async () => { const cwd = await mkdtemp(join(tmpdir(), `omx-3284-no-candidate-${scenario.name}-`)); try { const stateDir = join(cwd, ".omx", "state"); const canonicalSessionId = `omx-3284-no-candidate-${scenario.name}`; const rootNativeSessionId = `root-3284-no-candidate-${scenario.name}`; const childId = "child-3284-no-candidate"; await writeSessionStart(cwd, canonicalSessionId, { nativeSessionId: rootNativeSessionId, pid: process.pid }); const cachedThread = { thread_id: childId, kind: "subagent", provenance_kind: "native_subagent", direct_child_root_id: rootNativeSessionId, direct_child_parent_id: rootNativeSessionId, status: "available", first_seen_at: "2026-07-23T00:00:00.000Z", last_seen_at: "2026-07-23T00:00:00.000Z", turn_count: 1 }; await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { [canonicalSessionId]: { session_id: canonicalSessionId, updated_at: "2026-07-23T00:00:00.000Z", threads: { [childId]: cachedThread } }, [rootNativeSessionId]: { session_id: rootNativeSessionId, updated_at: "2026-07-23T00:00:00.000Z", threads: { [childId]: cachedThread } }, } }); const transcriptPath = join(cwd, `no-candidate-${scenario.name}.jsonl`); await writeFile(transcriptPath, `${JSON.stringify({ type: "session_meta", payload: { id: childId, source: { subagent: { thread_spawn: { parent_thread_id: rootNativeSessionId } } } } })}\n`); await dispatchCodexNativeHook({ hook_event_name: "SessionStart", cwd, ...scenario.aliases, transcript_path: transcriptPath } as never, { cwd, sessionOwnerPid: process.pid }); const tracking = JSON.parse(await readFile(join(stateDir, "subagent-tracking.json"), "utf-8")); assert.equal(tracking.sessions[canonicalSessionId].threads[childId].reopen_authority_revoked, true); const rootResume = await dispatchCodexNativeHook({ hook_event_name: "SessionStart", cwd, session_id: rootNativeSessionId, source: "resume" }, { cwd, sessionOwnerPid: process.pid }); const context = String((rootResume.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? ""); assert.equal(context.includes(`resume_agent(${JSON.stringify(childId)})`), false); const afterRootResume = JSON.parse(await readFile(join(stateDir, "subagent-tracking.json"), "utf-8")); assert.equal(afterRootResume.sessions[canonicalSessionId].threads[childId].resume_requested_at, undefined); } finally { await rm(cwd, { recursive: true, force: true }); } }); } it("issue #3284 preserves ordinary transcript-bearing SessionStart when native aliases are unusable", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-3284-ordinary-transcript-")); try { const canonicalSessionId = "omx-3284-ordinary-transcript"; const rootNativeSessionId = "root-3284-ordinary-transcript"; await writeSessionStart(cwd, canonicalSessionId, { nativeSessionId: rootNativeSessionId, pid: process.pid }); const sessionPath = join(cwd, ".omx", "state", "session.json"); const before = await readFile(sessionPath, "utf-8"); const transcriptPath = join(cwd, "ordinary-root.jsonl"); await writeFile(transcriptPath, `${JSON.stringify({ type: "session_meta", payload: { id: rootNativeSessionId, source: "user" } })}\n`); await dispatchCodexNativeHook({ hook_event_name: "SessionStart", cwd, session_id: "", sessionId: 42, transcript_path: transcriptPath } as never, { cwd, sessionOwnerPid: process.pid }); assert.equal(await readFile(sessionPath, "utf-8"), before); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("issue #3284 fresh-reads the pointer created by root SessionStart reconciliation", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-3284-fresh-pointer-")); try { const stateDir = join(cwd, ".omx", "state"); const rootNativeSessionId = "root-3284-fresh-pointer"; const childId = "child-3284-fresh-pointer"; await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { [rootNativeSessionId]: { session_id: rootNativeSessionId, updated_at: "2026-07-23T00:00:00.000Z", threads: { [childId]: { thread_id: childId, kind: "subagent", provenance_kind: "native_subagent", direct_child_root_id: rootNativeSessionId, direct_child_parent_id: rootNativeSessionId, status: "available", first_seen_at: "2026-07-23T00:00:00.000Z", last_seen_at: "2026-07-23T00:00:00.000Z", turn_count: 1 }, } }, } }); const result = await dispatchCodexNativeHook({ hook_event_name: "SessionStart", cwd, session_id: rootNativeSessionId, source: "resume", available_tools: ["resume_agent"] }, { cwd, sessionOwnerPid: process.pid }); const context = String((result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? ""); assert.equal(context.includes(`resume_agent(${JSON.stringify(childId)})`), true); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not suggest duplicate same-role subagent spawns when reopen ids exist", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-subagent-no-duplicates-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "omx-reuse-session"; await writeSessionStart(cwd, sessionId, { nativeSessionId: "codex-leader-reuse", pid: process.pid }); await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { [sessionId]: { session_id: sessionId, leader_thread_id: "codex-leader-reuse", updated_at: "2026-07-09T00:00:00.000Z", threads: { "codex-leader-reuse": { thread_id: "codex-leader-reuse", kind: "leader", first_seen_at: "2026-07-09T00:00:00.000Z", last_seen_at: "2026-07-09T00:00:00.000Z", turn_count: 1, }, "thread-critic-reuse": { thread_id: "thread-critic-reuse", kind: "subagent", first_seen_at: "2026-07-09T00:01:00.000Z", last_seen_at: "2026-07-09T00:01:00.000Z", turn_count: 1, role: "critic", lane_id: "risk-review", status: "closed", provenance_kind: "native_subagent", direct_child_root_id: "codex-leader-reuse", direct_child_parent_id: "codex-leader-reuse", }, }, }, }, }); const result = await dispatchCodexNativeHook( { hook_event_name: "SessionStart", cwd, session_id: "codex-leader-reuse", source: "startup", available_tools: ["resume_agent"], }, { cwd, sessionOwnerPid: process.pid }, ); const additionalContext = String( (result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? "", ); assert.match(additionalContext, /resume_agent\("thread-critic-reuse"\)/); assert.match(additionalContext, /avoid duplicate same-type subagent spawns/); assert.match(additionalContext, /do not spawn a new agent solely because reopen failed/); assert.doesNotMatch(additionalContext, /spawn.*critic.*replacement/i); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("surfaces clear warnings for unavailable persisted subagents without spawning replacements", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-subagent-warning-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "omx-warning-session"; await writeSessionStart(cwd, sessionId, { nativeSessionId: "codex-leader-warning", pid: process.pid }); await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { [sessionId]: { session_id: sessionId, leader_thread_id: "codex-leader-warning", updated_at: "2026-07-09T00:00:00.000Z", threads: { "codex-leader-warning": { thread_id: "codex-leader-warning", kind: "leader", first_seen_at: "2026-07-09T00:00:00.000Z", last_seen_at: "2026-07-09T00:00:00.000Z", turn_count: 1, }, "thread-executor-unavailable": { thread_id: "thread-executor-unavailable", kind: "subagent", first_seen_at: "2026-07-09T00:01:00.000Z", last_seen_at: "2026-07-09T00:01:00.000Z", turn_count: 1, role: "executor", lane_id: "implementation", status: "unavailable", resume_failed_at: "2026-07-09T00:02:00.000Z", resume_failure_reason: "Codex reported missing thread id", provenance_kind: "native_subagent", direct_child_root_id: "codex-leader-warning", direct_child_parent_id: "codex-leader-warning", }, }, }, }, }); const result = await dispatchCodexNativeHook( { hook_event_name: "SessionStart", cwd, session_id: "codex-leader-warning", source: "resume", }, { cwd, sessionOwnerPid: process.pid }, ); const additionalContext = String( (result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? "", ); assert.match(additionalContext, /No compatible saved subagent id is currently marked reopenable/); assert.match(additionalContext, /Reopen warnings:/); assert.match(additionalContext, /thread-executor-unavailable/); assert.match(additionalContext, /last failure: Codex reported missing thread id/); assert.doesNotMatch(additionalContext, /resume_agent\("thread-executor-unavailable"\)/); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("preserves canonical OMX session scope when native SessionStart arrives with a different id", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-session-reconcile-")); try { const stateDir = join(cwd, ".omx", "state"); const canonicalSessionId = "omx-launch-1"; const nativeSessionId = "codex-native-1"; await mkdir(join(stateDir, "sessions", canonicalSessionId), { recursive: true }); await writeSessionStart(cwd, canonicalSessionId); await writeJson(join(stateDir, "sessions", canonicalSessionId, "hud-state.json"), { last_turn_at: "2026-04-10T00:00:00.000Z", turn_count: 1, }); await dispatchCodexNativeHook( { hook_event_name: "SessionStart", cwd, session_id: nativeSessionId, }, { cwd, sessionOwnerPid: process.pid, }, ); const sessionState = JSON.parse( await readFile(join(stateDir, "session.json"), "utf-8"), ) as { session_id?: string; native_session_id?: string; pid?: number }; assert.equal(sessionState.session_id, canonicalSessionId); assert.equal(sessionState.native_session_id, nativeSessionId); assert.equal(sessionState.pid, process.pid); const promptResult = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: nativeSessionId, thread_id: "thread-1", turn_id: "turn-1", prompt: "$ralplan fix hud scope drift", }, { cwd }, ); assert.equal(promptResult.omxEventName, "keyword-detector"); assert.equal(existsSync(join(stateDir, "sessions", canonicalSessionId, "skill-active-state.json")), true); assert.equal(existsSync(join(stateDir, "sessions", canonicalSessionId, "ralplan-state.json")), true); assert.equal(existsSync(join(stateDir, "sessions", nativeSessionId, "skill-active-state.json")), false); assert.equal(existsSync(join(stateDir, "sessions", nativeSessionId, "ralplan-state.json")), false); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("issue #3138 converges owner-env terminal write and native Stop on one canonical scope", async () => { const root = await mkdtemp(join(tmpdir(), "omx-native-hook-3138-")); const fakeBinDir = join(root, "fake-bin"); const tmuxPath = join(fakeBinDir, "tmux"); const previousSessionId = process.env.OMX_SESSION_ID; const previousTmux = process.env.TMUX; const previousTmuxPane = process.env.TMUX_PANE; const previousPath = process.env.PATH; const setOwnerEvidence = async (instanceId: string, sessionInstanceId = ""): Promise => { await mkdir(fakeBinDir, { recursive: true }); await writeFile(tmuxPath, buildSessionOwnerEvidenceTmux(instanceId, sessionInstanceId), "utf-8"); await chmod(tmuxPath, 0o755); process.env.TMUX = "/tmp/omx-3138"; process.env.TMUX_PANE = "%3138"; process.env.PATH = `${fakeBinDir}:${previousPath ?? ""}`; }; try { const cwd = join(root, "bound"); const canonicalSessionId = "native-canonical-3138"; const nativeSessionId = "native-canonical-3138"; const ownerSessionId = "omx-owner-3138"; const stateDir = join(cwd, ".omx", "state"); await mkdir(cwd, { recursive: true }); await writeSessionStart(cwd, canonicalSessionId, { nativeSessionId, pid: process.pid, tmuxSessionName: "omx-owner-evidence", tmuxPaneId: "%3138", }); process.env.OMX_SESSION_ID = ownerSessionId; await setOwnerEvidence(ownerSessionId, canonicalSessionId); const beforeAlias = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: nativeSessionId, thread_id: "thread-owner-3138", turn_id: "turn-before-alias-3138", prompt: "$deep-interview establish canonical scope", }, { cwd }, ); assert.equal(beforeAlias.skillState?.session_id, canonicalSessionId); assert.equal(existsSync(join(stateDir, "sessions", canonicalSessionId, "deep-interview-state.json")), true); assert.equal(existsSync(join(stateDir, "sessions", ownerSessionId, "deep-interview-state.json")), false); await dispatchCodexNativeHook( { hook_event_name: "SessionStart", cwd, session_id: nativeSessionId }, { cwd, sessionOwnerPid: process.pid }, ); await dispatchCodexNativeHook( { hook_event_name: "SessionStart", cwd, session_id: nativeSessionId, source: "resume" }, { cwd, sessionOwnerPid: process.pid }, ); const reboundPointer = JSON.parse(await readFile(join(stateDir, "session.json"), "utf-8")) as { session_id?: string; native_session_id?: string; owner_omx_session_id?: string; }; assert.equal(reboundPointer.session_id, canonicalSessionId); assert.equal(reboundPointer.native_session_id, nativeSessionId); assert.equal(reboundPointer.owner_omx_session_id, ownerSessionId); const afterAlias = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: nativeSessionId, thread_id: "thread-owner-3138", turn_id: "turn-after-alias-3138", prompt: "$deep-interview establish canonical scope", }, { cwd }, ); assert.equal(afterAlias.skillState?.session_id, canonicalSessionId); assert.equal(existsSync(join(stateDir, "sessions", canonicalSessionId, "deep-interview-state.json")), true); assert.equal(existsSync(join(stateDir, "sessions", ownerSessionId, "deep-interview-state.json")), false); const terminalWrite = await executeStateOperation("state_write", { mode: "deep-interview", active: false, current_phase: "complete", workingDirectory: cwd, }); assert.notEqual(terminalWrite.isError, true); assert.equal( (terminalWrite.payload as { path?: string }).path, join(realpathSync(stateDir), "sessions", canonicalSessionId, "deep-interview-state.json"), ); assert.equal(existsSync(join(stateDir, "sessions", ownerSessionId, "deep-interview-state.json")), false); const stop = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: nativeSessionId, thread_id: "thread-owner-3138" }, { cwd }, ); assert.equal(stop.outputJson, null); const replacementCwd = join(root, "replacement"); const replacementStateDir = join(replacementCwd, ".omx", "state"); const replacementOwner = "omx-prior-3138"; const replacementCandidate = "omx-replacement-3138"; await mkdir(replacementCwd, { recursive: true }); await writeSessionStart(replacementCwd, replacementOwner, { nativeSessionId: "native-before-new-3138", pid: process.pid, }); process.env.OMX_SESSION_ID = replacementCandidate; await setOwnerEvidence(replacementCandidate); const replacementStart = await dispatchCodexNativeHook( { hook_event_name: "SessionStart", cwd: replacementCwd, session_id: "native-after-new-3138" }, { cwd: replacementCwd, sessionOwnerPid: process.pid }, ); assert.equal(replacementStart.outputJson, null); const replacementPointer = JSON.parse(await readFile(join(replacementStateDir, "session.json"), "utf-8")) as { session_id?: string; native_session_id?: string; owner_omx_session_id?: string; }; assert.equal(replacementPointer.session_id, replacementOwner); assert.equal(replacementPointer.native_session_id, "native-before-new-3138"); assert.equal(replacementPointer.owner_omx_session_id, undefined); assert.notEqual(replacementPointer.owner_omx_session_id, replacementCandidate); const nativeOnlyCwd = join(root, "native-only"); await mkdir(nativeOnlyCwd, { recursive: true }); delete process.env.OMX_SESSION_ID; await dispatchCodexNativeHook( { hook_event_name: "SessionStart", cwd: nativeOnlyCwd, session_id: "native-only-3138" }, { cwd: nativeOnlyCwd, sessionOwnerPid: process.pid }, ); const nativeOnlyPointer = JSON.parse( await readFile(join(nativeOnlyCwd, ".omx", "state", "session.json"), "utf-8"), ) as { session_id?: string; owner_omx_session_id?: string }; assert.equal(nativeOnlyPointer.session_id, "native-only-3138"); assert.equal(nativeOnlyPointer.owner_omx_session_id, undefined); const conflictingCwd = join(root, "conflicting"); const conflictingOwner = "omx-conflicting-3138"; await mkdir(conflictingCwd, { recursive: true }); await writeSessionStart(conflictingCwd, "native-conflicting-3138", { nativeSessionId: "native-conflicting-3138", pid: process.pid, }); process.env.OMX_SESSION_ID = conflictingOwner; await setOwnerEvidence("omx-foreign-3138"); await dispatchCodexNativeHook( { hook_event_name: "SessionStart", cwd: conflictingCwd, session_id: "native-conflicting-3138" }, { cwd: conflictingCwd, sessionOwnerPid: process.pid }, ); const conflictingPointer = JSON.parse( await readFile(join(conflictingCwd, ".omx", "state", "session.json"), "utf-8"), ) as { owner_omx_session_id?: string }; assert.equal(conflictingPointer.owner_omx_session_id, undefined); const conflictingActivation = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd: conflictingCwd, session_id: "native-conflicting-3138", prompt: "$deep-interview must not create an owner scope", }, { cwd: conflictingCwd }, ); assert.equal(conflictingActivation.skillState, null); assert.equal( existsSync(join(conflictingCwd, ".omx", "state", "sessions", "native-conflicting-3138", "deep-interview-state.json")), false, ); const staleCwd = join(root, "stale"); const staleStatePath = join(staleCwd, ".omx", "state", "session.json"); await writeJson(staleStatePath, { session_id: "native-stale-3138", native_session_id: "native-stale-3138", cwd: staleCwd, started_at: "2026-01-01T00:00:00.000Z", pid: 999_999, }); process.env.OMX_SESSION_ID = "omx-stale-3138"; await setOwnerEvidence("omx-stale-3138"); await dispatchCodexNativeHook( { hook_event_name: "SessionStart", cwd: staleCwd, session_id: "native-stale-3138" }, { cwd: staleCwd, sessionOwnerPid: process.pid }, ); const stalePointer = JSON.parse(await readFile(staleStatePath, "utf-8")) as { session_id?: string; owner_omx_session_id?: string; }; assert.equal(stalePointer.session_id, "native-stale-3138"); assert.equal(stalePointer.owner_omx_session_id, "omx-stale-3138"); const foreignCwd = join(root, "foreign"); const foreignStatePath = join(foreignCwd, ".omx", "state", "session.json"); await writeJson(foreignStatePath, { session_id: "native-foreign-3138", native_session_id: "native-foreign-3138", cwd: join(root, "other-worktree"), started_at: "2026-01-01T00:00:00.000Z", pid: process.pid, }); const foreignPointerBefore = await readFile(foreignStatePath, "utf-8"); process.env.OMX_SESSION_ID = "omx-foreign-3138"; await setOwnerEvidence("omx-foreign-3138"); const foreignStart = await dispatchCodexNativeHook( { hook_event_name: "SessionStart", cwd: foreignCwd, session_id: "native-foreign-3138" }, { cwd: foreignCwd, sessionOwnerPid: process.pid }, ); assert.equal(foreignStart.outputJson, null); assert.equal(await readFile(foreignStatePath, "utf-8"), foreignPointerBefore); const foreignPointer = JSON.parse(await readFile(foreignStatePath, "utf-8")) as { owner_omx_session_id?: string }; assert.equal(foreignPointer.owner_omx_session_id, undefined); const foreignActivation = await dispatchCodexNativeHook({ hook_event_name: "UserPromptSubmit", cwd: foreignCwd, session_id: "native-unmatched-foreign-3138", prompt: "$deep-interview must not escape a foreign pointer", }, { cwd: foreignCwd }); assert.equal(foreignActivation.skillState, null); assert.equal(existsSync(join(foreignCwd, ".omx", "state", "sessions", "native-unmatched-foreign-3138")), false); assert.equal(existsSync(join(foreignCwd, ".omx", "state", "skill-active-state.json")), false); const foreignStopPayload = { hook_event_name: "Stop" as const, cwd: foreignCwd, session_id: "native-foreign-3138", }; const foreignStop = await dispatchCodexNativeHook(foreignStopPayload, { cwd: foreignCwd }); assert.equal(foreignStop.outputJson, null); const foreignReplay = await dispatchCodexNativeHook({ ...foreignStopPayload, stop_hook_active: true, }, { cwd: foreignCwd }); assert.equal(foreignReplay.outputJson, null); assert.equal(existsSync(join(foreignCwd, ".omx", "state", "native-stop-state.json")), false); const unmatchedStopPayload = { hook_event_name: "Stop" as const, cwd: conflictingCwd, session_id: "native-unmatched-stop-3138", }; const unmatchedOutputs = await Promise.all( Array.from({ length: 3 }, () => dispatchCodexNativeHook(unmatchedStopPayload, { cwd: conflictingCwd })), ); assert.deepEqual(unmatchedOutputs.map((result) => result.outputJson), [null, null, null]); const unmatchedCliResult = runNativeHookCliResult(unmatchedStopPayload, { cwd: conflictingCwd }); assert.equal(unmatchedCliResult.status, 0, unmatchedCliResult.stderr || unmatchedCliResult.stdout); assert.equal(unmatchedCliResult.stderr, ""); assert.deepEqual(parseSingleJsonStdout(unmatchedCliResult.stdout), {}); assert.equal(existsSync(join(conflictingCwd, ".omx", "state", "native-stop-state.json")), false); } finally { if (typeof previousSessionId === "string") process.env.OMX_SESSION_ID = previousSessionId; else delete process.env.OMX_SESSION_ID; if (typeof previousTmux === "string") process.env.TMUX = previousTmux; else delete process.env.TMUX; if (typeof previousTmuxPane === "string") process.env.TMUX_PANE = previousTmuxPane; else delete process.env.TMUX_PANE; if (typeof previousPath === "string") process.env.PATH = previousPath; else delete process.env.PATH; await rm(root, { recursive: true, force: true }); } }); it("authorizes a live unmatched Stop from its exact session owner sidecar without changing the selected pointer", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-sidecar-stop-")); try { const stateDir = join(cwd, ".omx", "state"); const selectedSessionId = "native-selected-owner"; const independentSessionId = "native-independent-owner"; await writeSessionStart(cwd, selectedSessionId, { nativeSessionId: selectedSessionId, pid: process.pid, }); await writeLiveNativeSessionOwnerSidecar(cwd, stateDir, independentSessionId); const pointerBefore = await readFile(join(stateDir, "session.json"), "utf-8"); const rootGoalPath = join(cwd, ".omx", "ultragoal", "goals.json"); await writeJson(rootGoalPath, { version: 1, aggregateCompletion: { status: "complete", completedAt: "2026-05-20T00:00:00.000Z", }, goals: [{ id: "G001-done", status: "complete", objective: "Done" }], }); const rootGoalBefore = await readFile(rootGoalPath, "utf-8"); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: independentSessionId, thread_id: independentSessionId, last_user_message: "get_goal reports a completed Codex goal still attached to this thread; do not call create_goal until cleanup is explicit.", last_assistant_message: "I am starting another run now; create_goal payload follows.", }, { cwd }, ); assert.equal(result.outputJson, null); assert.equal(await readFile(join(stateDir, "session.json"), "utf-8"), pointerBefore); assert.equal(await readFile(rootGoalPath, "utf-8"), rootGoalBefore); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("uses the unmatched sidecar session workflow as the Stop blocker", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-sidecar-skill-")); try { const stateDir = join(cwd, ".omx", "state"); const selectedSessionId = "native-selected-skill"; const independentSessionId = "native-independent-skill"; await writeSessionStart(cwd, selectedSessionId, { nativeSessionId: selectedSessionId, pid: process.pid, }); await writeLiveNativeSessionOwnerSidecar(cwd, stateDir, independentSessionId); await writeSessionSkillActiveState(stateDir, independentSessionId, "ralplan", "planning"); await writeJson( join(stateDir, "sessions", independentSessionId, "ralplan-state.json"), { active: true, current_phase: "planning", session_id: independentSessionId, workingDirectory: cwd, }, ); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: independentSessionId, thread_id: independentSessionId, }, { cwd }, ); assert.equal(result.outputJson?.decision, "block"); assert.match(String(result.outputJson?.stopReason ?? ""), /^skill_ralplan_planning_/); assert.notEqual(result.outputJson?.stopReason, "session_scope_unmatched"); } finally { await rm(cwd, { recursive: true, force: true }); } }); for (const mode of ["autopilot", "ultrawork", "ultraqa"] as const) { it(`uses the unmatched sidecar ${mode} state as the Stop blocker`, async () => { await withIndependentNativeSession(mode, async ({ cwd, stateDir, sessionId, pointerBefore, }) => { await writeJson( join(stateDir, "sessions", sessionId, `${mode}-state.json`), { active: true, mode, current_phase: "executing", session_id: sessionId, workingDirectory: cwd, }, ); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: sessionId, thread_id: sessionId, }, { cwd }, ); assert.equal(result.outputJson?.decision, "block"); assert.match(String(result.outputJson?.stopReason ?? ""), new RegExp(`^${mode}_`)); assert.equal(await readFile(join(stateDir, "session.json"), "utf-8"), pointerBefore); assert.equal(existsSync(join(stateDir, "native-stop-state.json")), false); }); }); } it("uses the unmatched sidecar team state as the Stop blocker", async () => { await withIndependentNativeSession("team", async ({ cwd, stateDir, sessionId, pointerBefore, }) => { await writeJson( join(stateDir, "sessions", sessionId, "team-state.json"), { active: true, mode: "team", team_name: "sidecar-team", current_phase: "team-exec", session_id: sessionId, owner_codex_thread_id: sessionId, }, ); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: sessionId, thread_id: sessionId, }, { cwd }, ); assert.equal(result.outputJson?.decision, "block"); assert.equal(result.outputJson?.stopReason, "team_team-exec"); assert.equal(await readFile(join(stateDir, "session.json"), "utf-8"), pointerBefore); assert.equal(existsSync(join(stateDir, "native-stop-state.json")), false); }); }); it("uses the unmatched sidecar deep-interview question state as the Stop blocker", async () => { await withIndependentNativeSession("deep-interview", async ({ cwd, stateDir, sessionId, pointerBefore, }) => { await writeSessionSkillActiveState( stateDir, sessionId, "deep-interview", "intent-first", ); await writeJson( join(stateDir, "sessions", sessionId, "deep-interview-state.json"), { active: true, mode: "deep-interview", current_phase: "intent-first", session_id: sessionId, thread_id: sessionId, question_enforcement: { obligation_id: "sidecar-question", source: "omx-question", status: "pending", requested_at: "2026-07-18T00:00:00.000Z", }, }, ); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: sessionId, thread_id: sessionId, }, { cwd }, ); assert.equal(result.outputJson?.decision, "block"); assert.equal(result.outputJson?.stopReason, "deep_interview_question_required"); assert.equal(await readFile(join(stateDir, "session.json"), "utf-8"), pointerBefore); assert.equal(existsSync(join(stateDir, "native-stop-state.json")), false); }); }); it("uses a live parent sidecar when a nested selected pointer is stale-dead", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-sidecar-stale-root-")); try { const stateDir = join(cwd, ".omx", "state"); const parentSessionId = "native-live-parent"; await writeSessionStart(cwd, parentSessionId, { nativeSessionId: parentSessionId, pid: process.pid, }); await writeLiveNativeSessionOwnerSidecar(cwd, stateDir, parentSessionId); await writeJson(join(stateDir, "session.json"), { session_id: "native-dead-nested", native_session_id: "native-dead-nested", started_at: "2026-01-01T00:00:00.000Z", cwd, pid: 999_999, platform: process.platform, }); const pointerBefore = await readFile(join(stateDir, "session.json"), "utf-8"); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: parentSessionId, thread_id: parentSessionId, }, { cwd }, ); assert.equal(result.outputJson, null); assert.equal(await readFile(join(stateDir, "session.json"), "utf-8"), pointerBefore); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("keeps subagent SessionStart from replacing the canonical leader session", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-subagent-session-start-")); const originalCodexHome = process.env.CODEX_HOME; try { process.env.CODEX_HOME = join(cwd, "codex-home"); await writeJson(join(process.env.CODEX_HOME, ".omx-config.json"), { notifications: { enabled: true, verbosity: "session", telegram: { enabled: true, botToken: "123:abc", chatId: "456" }, }, }); const stateDir = join(cwd, ".omx", "state"); const canonicalSessionId = "omx-leader-session"; const leaderNativeSessionId = "codex-leader-thread"; const childNativeSessionId = "codex-child-thread"; await mkdir(join(stateDir, "sessions", canonicalSessionId), { recursive: true }); await writeSessionStart(cwd, canonicalSessionId, { nativeSessionId: leaderNativeSessionId, }); await writeJson(join(stateDir, "sessions", canonicalSessionId, "ralph-state.json"), { active: true, mode: "ralph", current_phase: "executing", iteration: 1, max_iterations: 5, }); await mkdir(join(cwd, ".omx", "hooks"), { recursive: true }); await writeFile( join(cwd, ".omx", "hooks", "record-lifecycle.mjs"), [ "import { appendFileSync } from 'node:fs';", "export async function onHookEvent(event) {", " appendFileSync('hook-events.jsonl', `${JSON.stringify({ event: event.event, context: event.context })}\\n`);", "}", ].join("\n"), ); const transcriptPath = join(cwd, "subagent-rollout.jsonl"); await writeFile( transcriptPath, `${JSON.stringify({ type: "session_meta", payload: { id: childNativeSessionId, source: { subagent: { thread_spawn: { parent_thread_id: leaderNativeSessionId, depth: 1, agent_nickname: "Hegel", agent_role: "critic", }, }, }, agent_nickname: "Hegel", agent_role: "critic", }, })}\n`, ); const result = await dispatchCodexNativeHook( { hook_event_name: "SessionStart", cwd, session_id: childNativeSessionId, transcript_path: transcriptPath, available_tools: ["resume_agent"], }, { cwd, sessionOwnerPid: process.pid }, ); const sessionState = JSON.parse( await readFile(join(stateDir, "session.json"), "utf-8"), ) as { session_id?: string; native_session_id?: string }; assert.equal(sessionState.session_id, canonicalSessionId); assert.equal(sessionState.native_session_id, leaderNativeSessionId); assert.equal( existsSync(join(stateDir, "sessions", childNativeSessionId, "ralph-state.json")), false, ); assert.ok(result.outputJson); const leaderRalph = JSON.parse( await readFile(join(stateDir, "sessions", canonicalSessionId, "ralph-state.json"), "utf-8"), ) as { active?: boolean; current_phase?: string }; assert.equal(leaderRalph.active, true); assert.equal(leaderRalph.current_phase, "executing"); assert.equal( existsSync(join(cwd, "hook-events.jsonl")), false, "subagent SessionStart must not independently dispatch session-start hook notifications", ); const tracking = JSON.parse( await readFile(join(stateDir, "subagent-tracking.json"), "utf-8"), ) as { sessions?: Record; }>; }; assert.equal(tracking.sessions?.[canonicalSessionId]?.leader_thread_id, leaderNativeSessionId); assert.equal(tracking.sessions?.[canonicalSessionId]?.threads?.[childNativeSessionId]?.kind, "subagent"); assert.equal(tracking.sessions?.[canonicalSessionId]?.threads?.[childNativeSessionId]?.mode, "critic"); assert.equal(tracking.sessions?.[canonicalSessionId]?.threads?.[childNativeSessionId]?.direct_child_root_id, leaderNativeSessionId); assert.equal(tracking.sessions?.[canonicalSessionId]?.threads?.[childNativeSessionId]?.direct_child_parent_id, leaderNativeSessionId); assert.equal(tracking.sessions?.[leaderNativeSessionId]?.leader_thread_id, leaderNativeSessionId); assert.equal(tracking.sessions?.[leaderNativeSessionId]?.threads?.[childNativeSessionId]?.kind, "subagent"); assert.equal(tracking.sessions?.[leaderNativeSessionId]?.threads?.[childNativeSessionId]?.mode, "critic"); const rootResume = await dispatchCodexNativeHook( { hook_event_name: "SessionStart", cwd, session_id: leaderNativeSessionId, source: "resume", available_tools: ["resume_agent"] }, { cwd, sessionOwnerPid: process.pid }, ); const rootResumeContext = String((rootResume.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? ""); assert.equal(rootResumeContext.includes(`resume_agent(${JSON.stringify(childNativeSessionId)})`), true); await rm(join(cwd, "hook-events.jsonl"), { force: true }); await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: childNativeSessionId, thread_id: childNativeSessionId, turn_id: "child-stop-turn", }, { cwd }, ); assert.equal( existsSync(join(cwd, "hook-events.jsonl")), false, "subagent Stop must not independently dispatch stop hook notifications", ); } finally { if (originalCodexHome === undefined) { delete process.env.CODEX_HOME; } else { process.env.CODEX_HOME = originalCodexHome; } await rm(cwd, { recursive: true, force: true }); } }); it("yields Unknown tool availability guidance when SessionStart lacks available_tools", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-unknown-tool-availability-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "omx-unknown-tools-session"; await writeSessionStart(cwd, sessionId, { nativeSessionId: "codex-leader-unknown-tools", pid: process.pid }); await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { [sessionId]: { session_id: sessionId, leader_thread_id: "codex-leader-unknown-tools", updated_at: "2026-07-09T00:00:00.000Z", threads: { "codex-leader-unknown-tools": { thread_id: "codex-leader-unknown-tools", kind: "leader", first_seen_at: "2026-07-09T00:00:00.000Z", last_seen_at: "2026-07-09T00:00:00.000Z", turn_count: 1, }, "thread-executor-unknown-tools": { thread_id: "thread-executor-unknown-tools", kind: "subagent", first_seen_at: "2026-07-09T00:01:00.000Z", last_seen_at: "2026-07-09T00:01:00.000Z", turn_count: 1, role: "executor", lane_id: "work-item", status: "available", provenance_kind: "native_subagent", direct_child_root_id: "codex-leader-unknown-tools", direct_child_parent_id: "codex-leader-unknown-tools", }, }, }, }, }); // SessionStart without available_tools yields Unknown guidance const resultWithoutTools = await dispatchCodexNativeHook( { hook_event_name: "SessionStart", cwd, session_id: "codex-leader-unknown-tools", source: "resume", }, { cwd, sessionOwnerPid: process.pid }, ); const contextWithoutTools = String( (resultWithoutTools.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? "", ); assert.match(contextWithoutTools, /Unknown: resume_agent tool availability could not be determined/); assert.match(contextWithoutTools, /"thread-executor-unknown-tools"/); assert.match(contextWithoutTools, /If resume_agent is callable, use resume_agent/); assert.match(contextWithoutTools, /If resume_agent is not callable, continue in the root/); // SessionStart with partial available_tools (not including resume_agent) also yields Unknown guidance const resultWithPartialTools = await dispatchCodexNativeHook( { hook_event_name: "SessionStart", cwd, session_id: "codex-leader-unknown-tools", source: "resume", available_tools: ["read", "edit"], }, { cwd, sessionOwnerPid: process.pid }, ); const contextWithPartialTools = String( (resultWithPartialTools.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? "", ); assert.match(contextWithPartialTools, /Unknown: resume_agent tool availability could not be determined/); assert.match(contextWithPartialTools, /"thread-executor-unknown-tools"/); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("keeps an authoritative Team worker lifecycle scoped without replacing the live leader pointer", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-team-worker-pointer-")); try { const stateDir = join(cwd, ".omx", "state"); await writeSessionStart(cwd, "leader-session", { nativeSessionId: "leader-native", pid: process.pid, }); await configureAuthoritativeTeamWorker(cwd, "pointer-team"); const leaderPointerBefore = await readFile(join(stateDir, "session.json"), "utf-8"); const leaderRalphPath = join(stateDir, "sessions", "leader-session", "ralph-state.json"); await writeJson(leaderRalphPath, { active: true, mode: "ralph", current_phase: "executing", session_id: "leader-session", owner_codex_session_id: "leader-native", }); const leaderRalphBeforeWorkerStop = await readFile(leaderRalphPath, "utf-8"); const rootNativeStopPath = join(stateDir, "native-stop-state.json"); assert.equal(existsSync(rootNativeStopPath), false); await dispatchCodexNativeHook({ hook_event_name: "SessionStart", cwd, session_id: "", sessionId: "worker-native", }, { cwd, sessionOwnerPid: process.pid }); assert.equal(await readFile(join(stateDir, "session.json"), "utf-8"), leaderPointerBefore); const workerStop = await dispatchCodexNativeHook({ hook_event_name: "Stop", cwd, session_id: "worker-native", thread_id: "worker-native", turn_id: "worker-stop-turn", }, { cwd }); assert.notEqual(workerStop.outputJson?.stopReason, "session_scope_unmatched"); assert.notEqual(workerStop.outputJson?.stopReason, "session_pointer_unusable"); assert.equal(await readFile(leaderRalphPath, "utf-8"), leaderRalphBeforeWorkerStop); assert.equal(existsSync(rootNativeStopPath), false); delete process.env.OMX_TEAM_INTERNAL_WORKER; delete process.env.OMX_TEAM_WORKER; process.env.TMUX_PANE = "%42"; const leaderStop = await dispatchCodexNativeHook({ hook_event_name: "Stop", cwd, session_id: "leader-native", thread_id: "leader-native", turn_id: "leader-stop-turn", }, { cwd }); assert.notEqual(leaderStop.outputJson?.stopReason, "session_scope_unmatched"); assert.equal(await readFile(join(stateDir, "session.json"), "utf-8"), leaderPointerBefore); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("discovers leader plugins for authoritative Team worker events in an isolated worktree", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-team-worker-subagent-stop-")); const workerCwd = realpathSync(await mkdtemp(join(tmpdir(), "omx-native-hook-team-worker-worktree-"))); const originalCodexHome = process.env.CODEX_HOME; try { await writeSessionStart(cwd, "leader-session", { nativeSessionId: "leader-native", pid: process.pid, }); await configureAuthoritativeTeamWorker(cwd, "team-worker-subagent-stop", "%10", workerCwd); const stateDir = join(cwd, ".omx", "state"); const leaderPointerPath = join(stateDir, "session.json"); const leaderPointerBefore = await readFile(leaderPointerPath, "utf-8"); const codexHome = join(cwd, "codex-home"); process.env.CODEX_HOME = codexHome; await mkdir(codexHome, { recursive: true }); await writeJson(join(codexHome, ".omx-config.json"), { notifications: { enabled: true, includeChildAgents: true }, }); const eventsPath = join(cwd, "child-events.jsonl"); await mkdir(join(cwd, ".omx", "hooks"), { recursive: true }); await writeFile(join(cwd, ".omx", "hooks", "capture.mjs"), `import { appendFileSync } from 'node:fs'; export function onHookEvent(event) { appendFileSync(${JSON.stringify(eventsPath)}, JSON.stringify({ event, pluginCwd: process.cwd(), teamStateRoot: process.env.OMX_TEAM_STATE_ROOT }) + '\\n'); } `); const parentSessionId = "team-worker-native-session"; const childThreadId = "team-worker-native-child"; const turnId = "team-worker-child-turn"; await dispatchCodexNativeHook({ hook_event_name: "SubagentStop", cwd: workerCwd, session_id: parentSessionId, agent_id: childThreadId, agent_type: "executor", turn_id: turnId, }, { cwd: workerCwd }); const tracking = await readSubagentTrackingState(workerCwd); const child = tracking.sessions[parentSessionId]?.threads[childThreadId]; assert.equal(child?.completion_source, "native_stop_event"); assert.equal(child?.last_completed_turn_id, turnId); assert.equal(child?.status, "closed"); const events = (await readFile(eventsPath, "utf-8")).trim().split("\n").map((line) => JSON.parse(line)); assert.deepEqual(events.map(({ event, pluginCwd, teamStateRoot }) => ({ event: [event.event, event.session_id, event.thread_id, event.turn_id], pluginCwd, teamStateRoot, })), [ { event: ["subagent-stop", parentSessionId, childThreadId, turnId], pluginCwd: workerCwd, teamStateRoot: stateDir, }, ]); await dispatchCodexNativeHook({ hook_event_name: "SessionStart", cwd: workerCwd, session_id: parentSessionId, }, { cwd: workerCwd, sessionOwnerPid: process.pid }); const workerEvents = (await readFile(eventsPath, "utf-8")).trim().split("\n").map((line) => JSON.parse(line)).slice(1); assert.deepEqual(workerEvents.map(({ event, pluginCwd, teamStateRoot }) => ({ event: event.event, pluginCwd, teamStateRoot, })), [{ event: "session-start", pluginCwd: workerCwd, teamStateRoot: stateDir, }]); assert.equal(await readFile(leaderPointerPath, "utf-8"), leaderPointerBefore); } finally { if (originalCodexHome === undefined) delete process.env.CODEX_HOME; else process.env.CODEX_HOME = originalCodexHome; await rm(workerCwd, { recursive: true, force: true }); await rm(cwd, { recursive: true, force: true }); } }); it("tracks native child SessionStart events under verified Team worker parents", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-team-worker-child-start-")); const workerCwd = realpathSync(await mkdtemp(join(tmpdir(), "omx-native-hook-team-worker-child-worktree-"))); const originalCodexHome = process.env.CODEX_HOME; try { await writeSessionStart(cwd, "leader-session", { nativeSessionId: "leader-native", pid: process.pid, }); await configureAuthoritativeTeamWorker(cwd, "team-worker-child-start", "%10", workerCwd); const stateDir = join(cwd, ".omx", "state"); const pointerPath = join(stateDir, "session.json"); const pointerBefore = await readFile(pointerPath, "utf-8"); const codexHome = join(cwd, "codex-home"); process.env.CODEX_HOME = codexHome; await writeJson(join(codexHome, ".omx-config.json"), { notifications: { enabled: true, includeChildAgents: true }, }); const eventsPath = join(cwd, "team-worker-child-events.jsonl"); await mkdir(join(cwd, ".omx", "hooks"), { recursive: true }); await writeFile(join(cwd, ".omx", "hooks", "capture.mjs"), `import { appendFileSync } from 'node:fs'; export function onHookEvent(event) { appendFileSync(${JSON.stringify(eventsPath)}, JSON.stringify({ event: event.event, session_id: event.session_id, omx_session_id: event.omx_session_id }) + '\\n'); } `); const workerSessionId = "team-worker-native-session"; const childSessionId = "team-worker-child-session"; const grandchildSessionId = "team-worker-grandchild-session"; for (const [sessionId, parentThreadId] of [ [childSessionId, workerSessionId], [grandchildSessionId, childSessionId], ]) { const transcriptPath = join(workerCwd, `${sessionId}.jsonl`); await writeFile(transcriptPath, `${JSON.stringify({ type: "session_meta", payload: { id: sessionId, source: { subagent: { thread_spawn: { parent_thread_id: parentThreadId } } }, }, })}\n`); await dispatchCodexNativeHook({ hook_event_name: "SessionStart", cwd: workerCwd, session_id: sessionId, transcript_path: transcriptPath, }, { cwd: workerCwd, sessionOwnerPid: process.pid }); } assert.equal(existsSync(eventsPath), false, "Team worker child starts must not dispatch in the leader session scope"); const trackingAfterStarts = await readSubagentTrackingState(workerCwd); assert.equal(trackingAfterStarts.sessions[workerSessionId]?.threads[childSessionId]?.kind, "subagent"); assert.equal(trackingAfterStarts.sessions[childSessionId]?.threads[grandchildSessionId]?.kind, "subagent"); const leafSessionId = "team-worker-leaf-session"; const turnId = "team-worker-leaf-turn"; await dispatchCodexNativeHook({ hook_event_name: "SubagentStop", cwd: workerCwd, session_id: grandchildSessionId, agent_id: leafSessionId, turn_id: turnId, }, { cwd: workerCwd }); const trackingAfterStop = await readSubagentTrackingState(workerCwd); const leaf = trackingAfterStop.sessions[grandchildSessionId]?.threads[leafSessionId]; assert.equal(leaf?.completion_source, "native_stop_event"); assert.equal(leaf?.last_completed_turn_id, turnId); assert.equal(leaf?.status, "closed"); assert.equal(await readFile(pointerPath, "utf-8"), pointerBefore); } finally { if (originalCodexHome === undefined) delete process.env.CODEX_HOME; else process.env.CODEX_HOME = originalCodexHome; await rm(workerCwd, { recursive: true, force: true }); await rm(cwd, { recursive: true, force: true }); } }); it("keeps native SubagentStop best-effort when opted-in hook dispatch fails", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-subagent-dispatch-failure-")); const originalCodexHome = process.env.CODEX_HOME; try { const codexHome = join(cwd, "codex-home"); process.env.CODEX_HOME = codexHome; await writeJson(join(codexHome, ".omx-config.json"), { notifications: { enabled: true, includeChildAgents: true }, }); const parentSessionId = "dispatch-failure-parent"; const childSessionId = "dispatch-failure-child"; await writeSessionStart(cwd, "dispatch-failure-session", { nativeSessionId: parentSessionId, pid: process.pid, }); const eventsPath = join(cwd, "subagent-events.jsonl"); await mkdir(join(cwd, ".omx", "hooks"), { recursive: true }); await writeFile(join(cwd, ".omx", "hooks", "capture.mjs"), `import { appendFileSync } from 'node:fs'; export function onHookEvent(event) { appendFileSync(${JSON.stringify(eventsPath)}, JSON.stringify({ event: event.event, thread_id: event.thread_id }) + '\\n'); } `); const logsPath = join(cwd, ".omx", "logs"); await rm(logsPath, { recursive: true, force: true }); await writeFile(logsPath, "not a directory"); const payload = { hook_event_name: "SubagentStop", cwd, session_id: parentSessionId, agent_id: childSessionId, turn_id: "dispatch-failure-turn", }; for (let attempt = 0; attempt < 2; attempt += 1) { const result = runNativeHookCliResult(payload, { cwd, env: process.env }); assert.equal(result.status, 0, result.stderr || result.stdout); assert.deepEqual(parseSingleJsonStdout(result.stdout), {}); } const events = (await readFile(eventsPath, "utf-8")).trim().split("\n").map((line) => JSON.parse(line)); assert.deepEqual(events, [{ event: "subagent-stop", thread_id: childSessionId }]); const tracking = await readSubagentTrackingState(cwd); assert.equal(tracking.sessions["dispatch-failure-session"]?.threads[childSessionId]?.completion_source, "native_stop_event"); } finally { if (originalCodexHome === undefined) delete process.env.CODEX_HOME; else process.env.CODEX_HOME = originalCodexHome; await rm(cwd, { recursive: true, force: true }); } }); it("rejects leader aliases from foreign-cwd pointer evidence in a worker worktree", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-team-worker-foreign-alias-")); try { await writeSessionStart(cwd, "foreign-alias-leader", { nativeSessionId: "foreign-alias-leader-native", pid: process.pid, }); await configureAuthoritativeTeamWorker(cwd, "foreign-alias-team"); const pointerPath = join(cwd, ".omx", "state", "session.json"); const pointer = JSON.parse(await readFile(pointerPath, "utf-8")) as Record; pointer.cwd = join(cwd, "leader-worktree"); await writeJson(pointerPath, pointer); const pointerBefore = await readFile(pointerPath, "utf-8"); for (const sessionId of ["foreign-alias-leader", "foreign-alias-leader-native"]) { await dispatchCodexNativeHook({ hook_event_name: "SessionStart", cwd, session_id: sessionId, }, { cwd, sessionOwnerPid: process.pid }); assert.equal(await readFile(pointerPath, "utf-8"), pointerBefore); assert.equal(existsSync(join(cwd, ".omx", "state", "sessions", sessionId)), false); } } finally { await rm(cwd, { recursive: true, force: true }); } }); it("keeps declared Team workers fail closed before startup authority is materialized", async () => { for (const pointerKind of ["absent", "live"] as const) { const cwd = await mkdtemp(join(tmpdir(), `omx-native-hook-team-worker-race-${pointerKind}-`)); try { const pointerPath = join(cwd, ".omx", "state", "session.json"); if (pointerKind === "live") { await writeSessionStart(cwd, "race-leader-session", { nativeSessionId: "race-leader-native", pid: process.pid, }); } const pointerBefore = existsSync(pointerPath) ? await readFile(pointerPath, "utf-8") : null; process.env.OMX_TEAM_WORKER = "race-team/worker-1"; process.env.OMX_TEAM_INTERNAL_WORKER = "race-team/worker-1"; process.env.TMUX = "1"; process.env.TMUX_PANE = "%10"; await dispatchCodexNativeHook({ hook_event_name: "SessionStart", cwd, session_id: "race-worker-native", }, { cwd, sessionOwnerPid: process.pid }); assert.equal(existsSync(pointerPath), pointerBefore !== null); if (pointerBefore !== null) assert.equal(await readFile(pointerPath, "utf-8"), pointerBefore); for (const declaration of [ { internal: "malformed", external: "" }, { internal: "race-team/worker-1", external: "other-team/worker-1" }, ]) { process.env.OMX_TEAM_INTERNAL_WORKER = declaration.internal; process.env.OMX_TEAM_WORKER = declaration.external; await dispatchCodexNativeHook({ hook_event_name: "SessionStart", cwd, session_id: "race-worker-malformed-native", }, { cwd, sessionOwnerPid: process.pid }); assert.equal(existsSync(pointerPath), pointerBefore !== null); if (pointerBefore !== null) assert.equal(await readFile(pointerPath, "utf-8"), pointerBefore); } const stop = await dispatchCodexNativeHook({ hook_event_name: "Stop", cwd, session_id: "race-worker-native", thread_id: "race-worker-native", turn_id: "race-worker-stop", }, { cwd }); assert.match(String(stop.outputJson?.stopReason ?? ""), /^team_worker_/); } finally { await rm(cwd, { recursive: true, force: true }); } } }); it("suppresses malformed or public-only Team identity across SessionStart and PreToolUse", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-team-worker-identity-gate-")); try { await configureAuthoritativeTeamWorker(cwd, "identity-gate-team"); const pointerPath = join(cwd, ".omx", "state", "session.json"); assert.equal(existsSync(pointerPath), false); const declarations = [ { internal: "", external: "identity-gate-team/worker-1" }, { internal: "malformed-internal", external: "identity-gate-team/worker-1" }, { internal: "identity-gate-team/worker-1", external: "other-team/worker-2" }, ] as const; for (const [index, declaration] of declarations.entries()) { process.env.OMX_TEAM_INTERNAL_WORKER = declaration.internal; process.env.OMX_TEAM_WORKER = declaration.external; const sessionId = `untrusted-worker-session-${index}`; const sessionStart = await dispatchCodexNativeHook({ hook_event_name: "SessionStart", cwd, session_id: sessionId, }, { cwd, sessionOwnerPid: process.pid }); assert.equal(sessionStart.outputJson, null); assert.equal(existsSync(pointerPath), false); assert.equal(existsSync(join(cwd, ".omx", "state", "sessions", sessionId)), false); const preToolUse = await dispatchCodexNativeHook({ hook_event_name: "PreToolUse", cwd, session_id: sessionId, tool_name: "Write", tool_use_id: `untrusted-worker-write-${index}`, tool_input: { file_path: "src/untrusted-worker.ts", content: "export {}\\n" }, }, { cwd }); assert.equal(preToolUse.outputJson, null); assert.equal(existsSync(pointerPath), false); assert.equal(existsSync(join(cwd, ".omx", "state", "sessions", sessionId)), false); } } finally { await rm(cwd, { recursive: true, force: true }); } }); it("fails closed for invalid Team worker lifecycle identity across pointer states", async () => { for (const pointerKind of ["absent", "live", "stale", "malformed"] as const) { const cwd = await mkdtemp(join(tmpdir(), `omx-native-hook-team-worker-invalid-${pointerKind}-`)); try { delete process.env.OMX_TEAM_INTERNAL_WORKER; delete process.env.OMX_TEAM_WORKER; delete process.env.OMX_TEAM_STATE_ROOT; delete process.env.OMX_TEAM_LEADER_CWD; delete process.env.OMX_SESSION_ID; delete process.env.TMUX; delete process.env.TMUX_PANE; const pointerPath = join(cwd, ".omx", "state", "session.json"); await configureAuthoritativeTeamWorker(cwd, "invalid-identity-team"); if (pointerKind === "live" || pointerKind === "stale") { await writeSessionStart(cwd, "leader-session", { nativeSessionId: "invalid-test-leader-native", pid: pointerKind === "live" ? process.pid : 2_147_483_647, }); } else if (pointerKind === "malformed") { await mkdir(dirname(pointerPath), { recursive: true }); await writeFile(pointerPath, "{ malformed missing-identity evidence", "utf-8"); } if (pointerKind === "live") { await writeJson(join(cwd, ".omx", "state", "subagent-tracking.json"), { schemaVersion: 1, sessions: { "leader-session": { session_id: "leader-session", leader_thread_id: "invalid-test-leader-native", threads: { "invalid-test-leader-native": { thread_id: "invalid-test-leader-native", kind: "leader" }, "invalid-worker-thread": { thread_id: "invalid-worker-thread", kind: "subagent" }, }, }, }, }); } const pointerBefore = existsSync(pointerPath) ? await readFile(pointerPath, "utf-8") : null; const payloads: Array> = [ {}, { session_id: "" }, { session_id: "../escape" }, { session_id: "worker-one", sessionId: "worker-two" }, ...(pointerKind === "live" ? [{ session_id: "leader-session" }, { session_id: "invalid-test-leader-native" }] : []), ]; for (const payload of payloads) { await dispatchCodexNativeHook({ hook_event_name: "SessionStart", cwd, ...payload, }, { cwd, sessionOwnerPid: process.pid }); assert.equal(existsSync(pointerPath), pointerBefore !== null); if (pointerBefore !== null) assert.equal(await readFile(pointerPath, "utf-8"), pointerBefore); const stop = await dispatchCodexNativeHook({ hook_event_name: "Stop", cwd, ...payload, thread_id: "invalid-worker-thread", turn_id: `invalid-worker-${pointerKind}-stop`, }, { cwd }); assert.match(String(stop.outputJson?.stopReason ?? ""), /^team_worker_/); assert.equal(existsSync(pointerPath), pointerBefore !== null); if (pointerBefore !== null) assert.equal(await readFile(pointerPath, "utf-8"), pointerBefore); } } finally { await rm(cwd, { recursive: true, force: true }); } } }); it("keeps authoritative worker payload scope independent of malformed selected-pointer evidence", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-team-worker-malformed-pointer-")); try { await configureAuthoritativeTeamWorker(cwd, "malformed-team"); const pointerPath = join(cwd, ".omx", "state", "session.json"); await writeFile(pointerPath, "{ malformed leader evidence", "utf-8"); await dispatchCodexNativeHook({ hook_event_name: "SessionStart", cwd, session_id: "worker-malformed-native", }, { cwd, sessionOwnerPid: process.pid }); assert.equal(await readFile(pointerPath, "utf-8"), "{ malformed leader evidence"); const stop = await dispatchCodexNativeHook({ hook_event_name: "Stop", cwd, session_id: "worker-malformed-native", thread_id: "worker-malformed-native", turn_id: "worker-malformed-stop", }, { cwd }); assert.notEqual(stop.outputJson?.stopReason, "session_pointer_unusable"); assert.notEqual(stop.outputJson?.stopReason, "session_scope_unmatched"); assert.equal(await readFile(pointerPath, "utf-8"), "{ malformed leader evidence"); for (const payload of [ { session_id: "../escape" }, { session_id: "worker-one", sessionId: "worker-two" }, ]) { const rejected = await dispatchCodexNativeHook({ hook_event_name: "SessionStart", cwd, ...payload, }, { cwd, sessionOwnerPid: process.pid }); assert.equal(rejected.outputJson, null); assert.equal(await readFile(pointerPath, "utf-8"), "{ malformed leader evidence"); assert.equal(existsSync(join(cwd, ".omx", "state", "sessions", "escape")), false); assert.equal(existsSync(join(cwd, ".omx", "state", "sessions", "worker-one")), false); assert.equal(existsSync(join(cwd, ".omx", "state", "sessions", "worker-two")), false); } } finally { await rm(cwd, { recursive: true, force: true }); } }); for (const [name, mutate] of [ ["foreign", async (cwd: string) => { process.env.OMX_TEAM_STATE_ROOT = join(cwd, "foreign-state"); }], ["nested", async (cwd: string) => { process.env.OMX_TEAM_INTERNAL_WORKER = "other-team/worker-1"; }], ["non-Team", async () => { delete process.env.OMX_TEAM_INTERNAL_WORKER; delete process.env.OMX_TEAM_WORKER; }], ] as const) { it(`does not grant the Team worker pointer bypass to ${name} hook context`, async () => { const cwd = await mkdtemp(join(tmpdir(), `omx-native-hook-${name}-pointer-`)); try { await configureAuthoritativeTeamWorker(cwd, "boundary-team"); await mutate(cwd); const pointerPath = join(cwd, ".omx", "state", "session.json"); await writeFile(pointerPath, "{ malformed boundary evidence", "utf-8"); await dispatchCodexNativeHook({ hook_event_name: "SessionStart", cwd, session_id: "boundary-native", }, { cwd, sessionOwnerPid: process.pid }); assert.equal(await readFile(pointerPath, "utf-8"), "{ malformed boundary evidence"); } finally { await rm(cwd, { recursive: true, force: true }); } }); } it("does not let a Team worker adopt a stale leader-selected pointer", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-team-worker-stale-leader-")); try { await writeSessionStart(cwd, "stale-leader-session", { nativeSessionId: "stale-leader-native", pid: 2_147_483_647, }); await configureAuthoritativeTeamWorker(cwd, "stale-leader-team"); const pointerPath = join(cwd, ".omx", "state", "session.json"); const pointerBefore = await readFile(pointerPath, "utf-8"); await dispatchCodexNativeHook({ hook_event_name: "SessionStart", cwd, session_id: "worker-after-stale-leader", }, { cwd, sessionOwnerPid: process.pid }); assert.equal(await readFile(pointerPath, "utf-8"), pointerBefore); const stop = await dispatchCodexNativeHook({ hook_event_name: "Stop", cwd, session_id: "worker-after-stale-leader", thread_id: "worker-after-stale-leader", turn_id: "worker-after-stale-stop", }, { cwd }); assert.notEqual(stop.outputJson?.stopReason, "session_pointer_unusable"); assert.notEqual(stop.outputJson?.stopReason, "session_scope_unmatched"); assert.equal(await readFile(pointerPath, "utf-8"), pointerBefore); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("preserves leader pointer ownership through the packed native-hook entrypoint", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-team-worker-packed-")); try { await writeSessionStart(cwd, "leader-session", { nativeSessionId: "packed-leader-native", pid: process.pid, }); await configureAuthoritativeTeamWorker(cwd, "packed-team"); const pointerPath = join(cwd, ".omx", "state", "session.json"); const pointerBefore = await readFile(pointerPath, "utf-8"); const env = { ...process.env }; parseSingleJsonStdout(runNativeHookCli({ hook_event_name: "SessionStart", cwd, session_id: "packed-worker-native", }, { cwd, env })); assert.equal(await readFile(pointerPath, "utf-8"), pointerBefore); const stopOutput = parseSingleJsonStdout(runNativeHookCli({ hook_event_name: "Stop", cwd, session_id: "packed-worker-native", thread_id: "packed-worker-native", turn_id: "packed-worker-stop", }, { cwd, env })); assert.notEqual(stopOutput.stopReason, "session_scope_unmatched"); assert.notEqual(stopOutput.stopReason, "session_pointer_unusable"); assert.equal(await readFile(pointerPath, "utf-8"), pointerBefore); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("suppresses child-agent SessionStart hook dispatch at minimal verbosity", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-subagent-session-minimal-")); const originalCodexHome = process.env.CODEX_HOME; try { process.env.CODEX_HOME = join(cwd, "codex-home"); await writeJson(join(process.env.CODEX_HOME, ".omx-config.json"), { notifications: { enabled: true, verbosity: "minimal", telegram: { enabled: true, botToken: "123:abc", chatId: "456" }, }, }); const stateDir = join(cwd, ".omx", "state"); const canonicalSessionId = "omx-leader-session-minimal"; const leaderNativeSessionId = "codex-leader-thread-minimal"; const childNativeSessionId = "codex-child-thread-minimal"; await mkdir(join(stateDir, "sessions", canonicalSessionId), { recursive: true }); await writeSessionStart(cwd, canonicalSessionId, { nativeSessionId: leaderNativeSessionId, }); await mkdir(join(cwd, ".omx", "hooks"), { recursive: true }); await writeFile( join(cwd, ".omx", "hooks", "record-lifecycle.mjs"), [ "import { appendFileSync } from 'node:fs';", "export async function onHookEvent(event) {", " appendFileSync('hook-events.jsonl', `${JSON.stringify({ event: event.event })}\\n`);", "}", ].join("\n"), ); const transcriptPath = join(cwd, "minimal-subagent-rollout.jsonl"); await writeFile( transcriptPath, `${JSON.stringify({ type: "session_meta", payload: { id: childNativeSessionId, source: { subagent: { thread_spawn: { parent_thread_id: leaderNativeSessionId, agent_role: "verifier", }, }, }, }, })}\n`, ); await dispatchCodexNativeHook( { hook_event_name: "SessionStart", cwd, session_id: childNativeSessionId, transcript_path: transcriptPath, }, { cwd, sessionOwnerPid: process.pid }, ); assert.equal( existsSync(join(cwd, "hook-events.jsonl")), false, "subagent SessionStart must be suppressed at minimal verbosity", ); } finally { if (originalCodexHome === undefined) { delete process.env.CODEX_HOME; } else { process.env.CODEX_HOME = originalCodexHome; } await rm(cwd, { recursive: true, force: true }); } }); it("allows explicit child-agent lifecycle hook dispatch when includeChildAgents is enabled", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-subagent-session-include-")); const originalCodexHome = process.env.CODEX_HOME; try { process.env.CODEX_HOME = join(cwd, "codex-home"); await writeJson(join(process.env.CODEX_HOME, ".omx-config.json"), { notifications: { enabled: true, verbosity: "session", includeChildAgents: true, telegram: { enabled: true, botToken: "123:abc", chatId: "456" }, }, }); const stateDir = join(cwd, ".omx", "state"); const canonicalSessionId = "omx-leader-session-include"; const leaderNativeSessionId = "codex-leader-thread-include"; const childNativeSessionId = "codex-child-thread-include"; await mkdir(join(stateDir, "sessions", canonicalSessionId), { recursive: true }); await writeSessionStart(cwd, canonicalSessionId, { nativeSessionId: leaderNativeSessionId, }); await mkdir(join(cwd, ".omx", "hooks"), { recursive: true }); await writeFile( join(cwd, ".omx", "hooks", "record-lifecycle.mjs"), [ "import { appendFileSync } from 'node:fs';", "export async function onHookEvent(event) {", " appendFileSync('hook-events.jsonl', `${JSON.stringify({ event: event.event })}\\n`);", "}", ].join("\n"), ); const transcriptPath = join(cwd, "included-subagent-rollout.jsonl"); await writeFile( transcriptPath, `${JSON.stringify({ type: "session_meta", payload: { id: childNativeSessionId, source: { subagent: { thread_spawn: { parent_thread_id: leaderNativeSessionId, agent_role: "verifier", }, }, }, }, })}\n`, ); await dispatchCodexNativeHook( { hook_event_name: "SessionStart", cwd, session_id: childNativeSessionId, transcript_path: transcriptPath, }, { cwd, sessionOwnerPid: process.pid }, ); await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: childNativeSessionId, thread_id: childNativeSessionId, turn_id: "included-child-stop-turn", }, { cwd }, ); const hookEvents = await readFile(join(cwd, "hook-events.jsonl"), "utf-8"); assert.match(hookEvents, /"event":"session-start"/); assert.match(hookEvents, /"event":"stop"/); } finally { if (originalCodexHome === undefined) { delete process.env.CODEX_HOME; } else { process.env.CODEX_HOME = originalCodexHome; } await rm(cwd, { recursive: true, force: true }); } }); it("allows child-agent lifecycle hook dispatch at agent verbosity", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-subagent-session-agent-")); const originalCodexHome = process.env.CODEX_HOME; try { process.env.CODEX_HOME = join(cwd, "codex-home"); await writeJson(join(process.env.CODEX_HOME, ".omx-config.json"), { notifications: { enabled: true, verbosity: "agent", telegram: { enabled: true, botToken: "123:abc", chatId: "456" }, }, }); const stateDir = join(cwd, ".omx", "state"); const canonicalSessionId = "omx-leader-session-agent"; const leaderNativeSessionId = "codex-leader-thread-agent"; const childNativeSessionId = "codex-child-thread-agent"; await mkdir(join(stateDir, "sessions", canonicalSessionId), { recursive: true }); await writeSessionStart(cwd, canonicalSessionId, { nativeSessionId: leaderNativeSessionId, }); await mkdir(join(cwd, ".omx", "hooks"), { recursive: true }); await writeFile( join(cwd, ".omx", "hooks", "record-lifecycle.mjs"), [ "import { appendFileSync } from 'node:fs';", "export async function onHookEvent(event) {", " appendFileSync('hook-events.jsonl', `${JSON.stringify({ event: event.event })}\\n`);", "}", ].join("\n"), ); const transcriptPath = join(cwd, "agent-verbosity-subagent-rollout.jsonl"); await writeFile( transcriptPath, `${JSON.stringify({ type: "session_meta", payload: { id: childNativeSessionId, source: { subagent: { thread_spawn: { parent_thread_id: leaderNativeSessionId, agent_role: "verifier", }, }, }, }, })}\n`, ); await dispatchCodexNativeHook( { hook_event_name: "SessionStart", cwd, session_id: childNativeSessionId, transcript_path: transcriptPath, }, { cwd, sessionOwnerPid: process.pid }, ); const hookEvents = await readFile(join(cwd, "hook-events.jsonl"), "utf-8"); assert.match(hookEvents, /"event":"session-start"/); } finally { if (originalCodexHome === undefined) { delete process.env.CODEX_HOME; } else { process.env.CODEX_HOME = originalCodexHome; } await rm(cwd, { recursive: true, force: true }); } }); it("suppresses child-agent SessionStart and Stop before the canonical leader session is reconciled (#2831)", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-subagent-no-canonical-")); const originalCodexHome = process.env.CODEX_HOME; try { process.env.CODEX_HOME = join(cwd, "codex-home"); await writeJson(join(process.env.CODEX_HOME, ".omx-config.json"), { notifications: { enabled: true, verbosity: "session", telegram: { enabled: true, botToken: "123:abc", chatId: "456" }, }, }); const stateDir = join(cwd, ".omx", "state"); const leaderNativeSessionId = "codex-leader-thread-no-canonical"; const childNativeSessionId = "codex-child-thread-no-canonical"; await mkdir(join(cwd, ".omx", "hooks"), { recursive: true }); await writeFile( join(cwd, ".omx", "hooks", "record-lifecycle.mjs"), [ "import { appendFileSync } from 'node:fs';", "export async function onHookEvent(event) {", " appendFileSync('hook-events.jsonl', `${JSON.stringify({ event: event.event })}\\n`);", "}", ].join("\n"), ); const transcriptPath = join(cwd, "no-canonical-subagent-rollout.jsonl"); await writeFile( transcriptPath, `${JSON.stringify({ type: "session_meta", payload: { id: childNativeSessionId, source: { subagent: { thread_spawn: { parent_thread_id: leaderNativeSessionId, agent_role: "explorer", }, }, }, }, })}\n`, ); await dispatchCodexNativeHook( { hook_event_name: "SessionStart", cwd, session_id: childNativeSessionId, transcript_path: transcriptPath, }, { cwd, sessionOwnerPid: process.pid }, ); assert.equal( existsSync(join(cwd, "hook-events.jsonl")), false, "child SessionStart must be suppressed even before the canonical leader session is reconciled", ); assert.equal( existsSync(join(stateDir, "session.json")), false, "child SessionStart must not be promoted into a root/leader session", ); const tracking = JSON.parse( await readFile(join(stateDir, "subagent-tracking.json"), "utf-8"), ) as { sessions?: Record; }>; }; assert.equal(tracking.sessions?.[leaderNativeSessionId]?.leader_thread_id, leaderNativeSessionId); assert.equal(tracking.sessions?.[leaderNativeSessionId]?.threads?.[childNativeSessionId]?.kind, "subagent"); await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: childNativeSessionId, thread_id: childNativeSessionId, turn_id: "no-canonical-child-stop-turn", }, { cwd }, ); assert.equal( existsSync(join(cwd, "hook-events.jsonl")), false, "child Stop must be suppressed when the start was recognized as subagent-scoped", ); } finally { if (originalCodexHome === undefined) { delete process.env.CODEX_HOME; } else { process.env.CODEX_HOME = originalCodexHome; } await rm(cwd, { recursive: true, force: true }); } }); it("preserves root/leader SessionStart dispatch at session verbosity (#2831)", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-root-session-start-preserved-")); const originalCodexHome = process.env.CODEX_HOME; try { process.env.CODEX_HOME = join(cwd, "codex-home"); await writeJson(join(process.env.CODEX_HOME, ".omx-config.json"), { notifications: { enabled: true, verbosity: "session", telegram: { enabled: true, botToken: "123:abc", chatId: "456" }, }, }); await mkdir(join(cwd, ".omx", "hooks"), { recursive: true }); await writeFile( join(cwd, ".omx", "hooks", "record-lifecycle.mjs"), [ "import { appendFileSync } from 'node:fs';", "export async function onHookEvent(event) {", " appendFileSync('hook-events.jsonl', `${JSON.stringify({ event: event.event })}\\n`);", "}", ].join("\n"), ); await dispatchCodexNativeHook( { hook_event_name: "SessionStart", cwd, session_id: "codex-root-thread-preserved", }, { cwd, sessionOwnerPid: process.pid }, ); const hookEvents = await readFile(join(cwd, "hook-events.jsonl"), "utf-8"); assert.match( hookEvents, /"event":"session-start"/, "root/leader SessionStart must still dispatch at session verbosity", ); } finally { if (originalCodexHome === undefined) { delete process.env.CODEX_HOME; } else { process.env.CODEX_HOME = originalCodexHome; } await rm(cwd, { recursive: true, force: true }); } }); it("keeps a self-parented native role thread as subagent evidence", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-self-parented-subagent-")); try { const stateDir = join(cwd, ".omx", "state"); const canonicalSessionId = "omx-autopilot-session"; const nativeRoleThreadId = "codex-architect-thread"; await mkdir(join(stateDir, "sessions", canonicalSessionId), { recursive: true }); await writeSessionStart(cwd, canonicalSessionId, { nativeSessionId: nativeRoleThreadId, }); const transcriptPath = join(cwd, "architect-subagent-rollout.jsonl"); await writeFile( transcriptPath, `${JSON.stringify({ type: "session_meta", payload: { id: nativeRoleThreadId, source: { subagent: { thread_spawn: { parent_thread_id: nativeRoleThreadId, depth: 1, agent_nickname: "Architect", agent_role: "architect", }, }, }, agent_nickname: "Architect", agent_role: "architect", }, })}\n`, ); await dispatchCodexNativeHook( { hook_event_name: "SessionStart", cwd, session_id: nativeRoleThreadId, transcript_path: transcriptPath, }, { cwd, sessionOwnerPid: process.pid }, ); const tracking = JSON.parse( await readFile(join(stateDir, "subagent-tracking.json"), "utf-8"), ) as { sessions?: Record; }>; }; assert.equal(tracking.sessions?.[canonicalSessionId]?.leader_thread_id, undefined); assert.equal(tracking.sessions?.[canonicalSessionId]?.threads?.[nativeRoleThreadId]?.kind, "subagent"); assert.equal(tracking.sessions?.[canonicalSessionId]?.threads?.[nativeRoleThreadId]?.mode, "architect"); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not attach a subagent SessionStart to an unrelated canonical leader", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-subagent-session-start-mismatch-")); try { const stateDir = join(cwd, ".omx", "state"); const canonicalSessionId = "omx-leader-session-a"; const leaderNativeSessionId = "codex-leader-thread-a"; const unrelatedParentNativeSessionId = "codex-leader-thread-b"; const childNativeSessionId = "codex-child-thread-b"; await mkdir(join(stateDir, "sessions", canonicalSessionId), { recursive: true }); await writeSessionStart(cwd, canonicalSessionId, { nativeSessionId: leaderNativeSessionId, }); await writeJson(join(stateDir, "sessions", canonicalSessionId, "ralph-state.json"), { active: true, mode: "ralph", current_phase: "executing", iteration: 1, max_iterations: 5, }); const transcriptPath = join(cwd, "unrelated-subagent-rollout.jsonl"); await writeFile( transcriptPath, `${JSON.stringify({ type: "session_meta", payload: { id: childNativeSessionId, source: { subagent: { thread_spawn: { parent_thread_id: unrelatedParentNativeSessionId, depth: 1, agent_nickname: "Spinoza", agent_role: "critic", }, }, }, agent_nickname: "Spinoza", agent_role: "critic", }, })}\n`, ); const result = await dispatchCodexNativeHook( { hook_event_name: "SessionStart", cwd, session_id: childNativeSessionId, transcript_path: transcriptPath, }, { cwd, sessionOwnerPid: process.pid }, ); const sessionState = JSON.parse( await readFile(join(stateDir, "session.json"), "utf-8"), ) as { session_id?: string; native_session_id?: string }; assert.equal(sessionState.session_id, canonicalSessionId); assert.equal(sessionState.native_session_id, leaderNativeSessionId); assert.equal(existsSync(join(stateDir, "subagent-tracking.json")), false); assert.equal(existsSync(join(stateDir, "sessions", childNativeSessionId)), false); assert.equal(result.outputJson, null); const leaderRalph = JSON.parse( await readFile(join(stateDir, "sessions", canonicalSessionId, "ralph-state.json"), "utf-8"), ) as { active?: boolean; current_phase?: string }; assert.equal(leaderRalph.active, true); assert.equal(leaderRalph.current_phase, "executing"); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("describes attached tmux runtime in SessionStart context when TMUX is present", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-session-start-tmux-")); process.env.TMUX = "/tmp/tmux-attached"; process.env.TMUX_PANE = "%11"; try { const result = await dispatchCodexNativeHook( { hook_event_name: "SessionStart", cwd, session_id: "sess-start-tmux-1", }, { cwd, sessionOwnerPid: process.pid, }, ); const additionalContext = String( (result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? "", ); assert.match(additionalContext, /\[Execution environment\]/); assert.match(additionalContext, /attached tmux runtime/); assert.match(additionalContext, /omx team, omx hud, and omx quest(?:ion) are directly usable in this session/); assert.match(additionalContext, /visible temporary renderer available from the current pane; primary success JSON is answers\[\]/); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("describes direct CLI outside tmux in SessionStart context when the launch source is cli", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-session-start-cli-")); try { const result = await dispatchCodexNativeHook( { hook_event_name: "SessionStart", cwd, session_id: "sess-start-cli-1", source: "cli", }, { cwd, sessionOwnerPid: process.pid, }, ); const additionalContext = String( (result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? "", ); assert.match(additionalContext, /\[Execution environment\]/); assert.match(additionalContext, /direct CLI outside tmux/); assert.doesNotMatch(additionalContext, /native-hook \/ Codex App outside tmux/); assert.match(additionalContext, /omx team, omx hud, and omx quest(?:ion) need an attached tmux OMX CLI shell|omx team and omx hud need an attached tmux OMX CLI shell/); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("preserves the OMX owner HUD session when an unrelated native SessionStart is rejected", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-hud-owner-session-revive-")); try { const stateDir = join(cwd, ".omx", "state"); const ownerSessionId = "omx-launch-owner-hud"; const oldNativeSessionId = "codex-native-hud-old"; const nativeSessionId = "codex-native-hud-new"; await mkdir(stateDir, { recursive: true }); await writeSessionStart(cwd, ownerSessionId, { nativeSessionId: oldNativeSessionId, pid: process.pid, }); await dispatchCodexNativeHook( { hook_event_name: "SessionStart", cwd, session_id: nativeSessionId, }, { cwd, sessionOwnerPid: process.pid, }, ); const sessionState = JSON.parse(await readFile(join(stateDir, "session.json"), "utf-8")) as { session_id?: string; native_session_id?: string; previous_native_session_id?: string; owner_omx_session_id?: string; }; assert.equal(sessionState.session_id, ownerSessionId); assert.equal(sessionState.native_session_id, oldNativeSessionId); assert.equal(sessionState.previous_native_session_id, undefined); assert.equal(sessionState.owner_omx_session_id, undefined); let reconcileCall: { cwd: string; sessionId?: string; sessionIds?: string[] } | null = null; const promptResult = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: ownerSessionId, thread_id: "thread-hud-owner", turn_id: "turn-hud-owner", prompt: "$ralplan fix native new hud owner handoff", }, { cwd, reconcileHudForPromptSubmitFn: async (hookCwd, deps = {}) => { reconcileCall = { cwd: hookCwd, sessionId: deps.sessionId, sessionIds: deps.sessionIds }; return { status: "recreated", paneId: "%9", desiredHeight: 3, duplicateCount: 0 }; }, }, ); assert.equal(promptResult.omxEventName, "keyword-detector"); assert.deepEqual(reconcileCall, { cwd, sessionId: ownerSessionId, sessionIds: [ownerSessionId, oldNativeSessionId], }); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("falls back to the canonical session id for malformed HUD owner ids", async () => { for (const [index, invalidOwnerSessionId] of ["codex-native-hud-owner", "omx-../../stale"].entries()) { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-hud-invalid-owner-revive-")); try { const stateDir = join(cwd, ".omx", "state"); const canonicalSessionId = "omx-launch-hud-safe"; const nativeSessionId = "codex-native-hud-safe"; await mkdir(join(stateDir, "sessions", canonicalSessionId), { recursive: true }); await writeSessionStart(cwd, canonicalSessionId, { nativeSessionId }); const sessionStatePath = join(stateDir, "session.json"); const sessionState = JSON.parse(await readFile(sessionStatePath, "utf-8")) as Record; sessionState.owner_omx_session_id = invalidOwnerSessionId; await writeJson(sessionStatePath, sessionState); let reconcileCall: { cwd: string; sessionId?: string; sessionIds?: string[] } | null = null; const promptResult = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: nativeSessionId, thread_id: `thread-hud-invalid-owner-${index}`, turn_id: "turn-hud-invalid-owner", prompt: "$ralplan fix malformed hud owner handoff", }, { cwd, reconcileHudForPromptSubmitFn: async (hookCwd, deps = {}) => { reconcileCall = { cwd: hookCwd, sessionId: deps.sessionId, sessionIds: deps.sessionIds }; return { status: "recreated", paneId: "%9", desiredHeight: 3, duplicateCount: 0 }; }, }, ); assert.equal(promptResult.omxEventName, "keyword-detector"); assert.deepEqual(reconcileCall, { cwd, sessionId: canonicalSessionId, sessionIds: [canonicalSessionId, nativeSessionId], }); } finally { await rm(cwd, { recursive: true, force: true }); } } }); it("passes the canonical OMX session id when UserPromptSubmit revives HUD", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-hud-session-revive-")); try { const stateDir = join(cwd, ".omx", "state"); const canonicalSessionId = "omx-launch-hud"; const nativeSessionId = "codex-native-hud"; await mkdir(join(stateDir, "sessions", canonicalSessionId), { recursive: true }); await writeSessionStart(cwd, canonicalSessionId, { nativeSessionId }); let reconcileCall: { cwd: string; sessionId?: string } | null = null; const promptResult = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: nativeSessionId, thread_id: "thread-hud", turn_id: "turn-hud", prompt: "$ralplan fix orphaned hud session handoff", }, { cwd, reconcileHudForPromptSubmitFn: async (hookCwd, deps = {}) => { reconcileCall = { cwd: hookCwd, sessionId: deps.sessionId }; return { status: 'recreated', paneId: '%9', desiredHeight: 3, duplicateCount: 0 }; }, }, ); assert.equal(promptResult.omxEventName, "keyword-detector"); assert.deepEqual(reconcileCall, { cwd, sessionId: canonicalSessionId }); assert.equal(existsSync(join(stateDir, "sessions", canonicalSessionId, "skill-active-state.json")), true); assert.equal(existsSync(join(stateDir, "sessions", canonicalSessionId, "ralplan-state.json")), true); assert.equal(existsSync(join(stateDir, "sessions", nativeSessionId, "skill-active-state.json")), false); assert.equal(existsSync(join(stateDir, "sessions", nativeSessionId, "ralplan-state.json")), false); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("adds .omx/ to git info/exclude during SessionStart instead of mutating repo .gitignore", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-session-gitignore-")); try { await writeFile(join(cwd, ".gitignore"), "node_modules/\n"); execFileSync("git", ["init"], { cwd, stdio: "pipe" }); const result = await dispatchCodexNativeHook( { hook_event_name: "SessionStart", cwd, session_id: "sess-gitignore-1", }, { cwd, sessionOwnerPid: 43210 }, ); assert.equal(result.omxEventName, "session-start"); const gitignore = await readFile(join(cwd, ".gitignore"), "utf-8"); assert.equal(gitignore, "node_modules/\n"); const exclude = await readFile(join(cwd, ".git", "info", "exclude"), "utf-8"); assert.match(exclude, /(?:^|\n)\.omx\/\n/); assert.match( JSON.stringify(result.outputJson), /Added \.omx\/ to .*\.git[\/]info[\/]exclude/, ); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("keeps SessionStart quiet when .omx/ is already ignored by repo-level gitignore", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-session-existing-ignore-")); try { await writeFile(join(cwd, ".gitignore"), "node_modules/\n.omx/\n"); execFileSync("git", ["init"], { cwd, stdio: "pipe" }); const result = await dispatchCodexNativeHook( { hook_event_name: "SessionStart", cwd, session_id: "sess-gitignore-existing", }, { cwd, sessionOwnerPid: 43210 }, ); assert.equal(result.omxEventName, "session-start"); const gitignore = await readFile(join(cwd, ".gitignore"), "utf-8"); assert.equal(gitignore, "node_modules/\n.omx/\n"); const exclude = await readFile(join(cwd, ".git", "info", "exclude"), "utf-8"); assert.doesNotMatch(exclude, /(?:^|\n)\.omx\/\n/); assert.doesNotMatch(JSON.stringify(result.outputJson), /Added \.omx\//); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("respects existing Git ignore resolution before writing local excludes", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-session-global-ignore-")); const excludesFile = join(cwd, "global-ignore"); try { await writeFile(join(cwd, ".gitignore"), "node_modules/\n"); await writeFile(excludesFile, ".omx/\n"); execFileSync("git", ["init"], { cwd, stdio: "pipe" }); execFileSync("git", ["config", "core.excludesfile", excludesFile], { cwd, stdio: "pipe" }); const result = await dispatchCodexNativeHook( { hook_event_name: "SessionStart", cwd, session_id: "sess-gitignore-global", }, { cwd, sessionOwnerPid: 43210 }, ); assert.equal(result.omxEventName, "session-start"); const gitignore = await readFile(join(cwd, ".gitignore"), "utf-8"); assert.equal(gitignore, "node_modules/\n"); const exclude = await readFile(join(cwd, ".git", "info", "exclude"), "utf-8"); assert.doesNotMatch(exclude, /(?:^|\n)\.omx\/\n/); assert.doesNotMatch(JSON.stringify(result.outputJson), /Added \.omx\//); } finally { await rm(cwd, { recursive: true, force: true }); } }); for (const scenario of ["startup", "direct-codex", "resume", "unknown-source", "missing-file", "foreign-session", "omitted-sections", "changed-memory", "shortened-memory"] as const) { it(`preserves startup memory delivery for ${scenario}`, async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-startup-context-")); const previous = process.env.OMX_STARTUP_MODEL_INSTRUCTIONS_FILE; const sessionId = "sess-startup-context"; const stack = "UNIQUE_STACK_MARKER TypeScript"; const priority = "UNIQUE_PRIORITY_MARKER Save a checkpoint. Keep review evidence."; try { await writeJson(join(cwd, ".omx", "project-memory.json"), { techStack: stack, build: "UNIQUE_BUILD_MARKER npm test", conventions: "UNIQUE_CONVENTIONS_MARKER small diffs", directives: [{ directive: "UNIQUE_DIRECTIVE_MARKER preserve evidence", priority: "high" }], notes: [{ content: "HOOK_ONLY_MEMORY_NOTE" }], }); await writeFile(join(cwd, ".omx", "notepad.md"), `## PRIORITY\n${priority.replace(" Keep", "\nKeep")}\n`); const overlay = scenario === "omitted-sections" ? "Runtime instructions without memory." : await generateOverlay(cwd, sessionId); const instructionsPath = await writeSessionModelInstructionsFile(cwd, sessionId, overlay); process.env.OMX_STARTUP_MODEL_INSTRUCTIONS_FILE = scenario === "foreign-session" ? await writeSessionModelInstructionsFile(cwd, "another-session", overlay) : instructionsPath; if (scenario === "direct-codex") delete process.env.OMX_STARTUP_MODEL_INSTRUCTIONS_FILE; if (scenario === "missing-file") await rm(instructionsPath); if (scenario === "changed-memory") { await writeJson(join(cwd, ".omx", "project-memory.json"), { techStack: "UPDATED_STACK_MARKER" }); await writeFile(join(cwd, ".omx", "notepad.md"), "## PRIORITY\nUPDATED_PRIORITY_MARKER\n"); } if (scenario === "shortened-memory") { await writeJson(join(cwd, ".omx", "project-memory.json"), { techStack: "UNIQUE_STACK_MARKER" }); await writeFile(join(cwd, ".omx", "notepad.md"), "## PRIORITY\nUNIQUE_PRIORITY_MARKER\n"); } const result = await dispatchCodexNativeHook({ hook_event_name: "SessionStart", cwd, session_id: sessionId, ...(scenario === "unknown-source" ? {} : { source: scenario === "resume" ? "resume" : "startup" }), }, { cwd, sessionOwnerPid: process.pid }); const context = JSON.stringify(result.outputJson); if (scenario === "changed-memory") { assert.match(context, /UPDATED_STACK_MARKER/); assert.match(context, /UPDATED_PRIORITY_MARKER/); } else if (scenario === "shortened-memory") { assert.match(context, /UNIQUE_STACK_MARKER/); assert.match(context, /UNIQUE_PRIORITY_MARKER/); } else { assert.equal(context.includes(stack), scenario !== "startup"); assert.equal(context.includes(priority), scenario !== "startup"); assert.equal(context.includes("UNIQUE_BUILD_MARKER"), scenario !== "startup"); assert.equal(context.includes("UNIQUE_CONVENTIONS_MARKER"), scenario !== "startup"); assert.equal(context.includes("UNIQUE_DIRECTIVE_MARKER"), scenario !== "startup"); assert.match(context, /HOOK_ONLY_MEMORY_NOTE/); } } finally { if (previous === undefined) delete process.env.OMX_STARTUP_MODEL_INSTRUCTIONS_FILE; else process.env.OMX_STARTUP_MODEL_INSTRUCTIONS_FILE = previous; await rm(cwd, { recursive: true, force: true }); } }); } it("includes persisted project-memory summary in SessionStart context", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-session-memory-")); try { await writeJson(join(cwd, ".omx", "project-memory.json"), { techStack: "TypeScript + Node.js", build: "npm test", conventions: "small diffs, verify before claim", directives: [ { directive: "Keep native Stop bounded to real continuation decisions.", priority: "high" }, ], notes: [ { category: "env", content: "Requires LOCAL_API_BASE for smoke tests", timestamp: new Date().toISOString() }, ], }); const result = await dispatchCodexNativeHook( { hook_event_name: "SessionStart", cwd, session_id: "sess-memory-1", }, { cwd, sessionOwnerPid: 43210 }, ); const serialized = JSON.stringify(result.outputJson); assert.match(serialized, /\[Project memory\]/); assert.match(serialized, /TypeScript \+ Node\.js/); assert.match(serialized, /small diffs, verify before claim/); assert.match(serialized, /Keep native Stop bounded to real continuation decisions\./); assert.match(serialized, /Requires LOCAL_API_BASE for smoke tests/); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("includes repo-local .omx project-memory during SessionStart when OMX_ROOT is boxed", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-session-boxed-memory-")); const boxedRoot = await mkdtemp(join(tmpdir(), "omx-native-hook-boxed-root-")); const previousOmxRoot = process.env.OMX_ROOT; try { process.env.OMX_ROOT = boxedRoot; await writeJson(join(cwd, ".omx", "project-memory.json"), { techStack: "Repo-local CLI memory", conventions: "SessionStart should load CLI-written project memory", directives: [ { directive: "Prefer repo-local .omx project memory over boxed runtime fallback.", priority: "high" }, ], }); await writeJson(join(boxedRoot, ".omx", "project-memory.json"), { techStack: "Boxed runtime memory should not win", notes: [{ category: "runtime", content: "stale boxed runtime note", timestamp: new Date().toISOString() }], }); const result = await dispatchCodexNativeHook( { hook_event_name: "SessionStart", cwd, session_id: "sess-boxed-memory-1", }, { cwd, sessionOwnerPid: 43210 }, ); const additionalContext = String( (result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? "", ); assert.match(additionalContext, /\[Project memory\]/); assert.match(additionalContext, /source: \.omx\/project-memory\.json/); assert.match(additionalContext, /Repo-local CLI memory/); assert.match(additionalContext, /SessionStart should load CLI-written project memory/); assert.match(additionalContext, /Prefer repo-local \.omx project memory over boxed runtime fallback\./); assert.doesNotMatch(additionalContext, /Boxed runtime memory should not win/); assert.doesNotMatch(additionalContext, /stale boxed runtime note/); } finally { if (previousOmxRoot === undefined) delete process.env.OMX_ROOT; else process.env.OMX_ROOT = previousOmxRoot; await rm(cwd, { recursive: true, force: true }); await rm(boxedRoot, { recursive: true, force: true }); } }); it("prefers repository project-memory.json during SessionStart while preserving legacy wiki guidance", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-session-root-memory-legacy-wiki-")); try { const now = new Date().toISOString(); const legacyWikiDir = getLegacyWikiDir(cwd); await mkdir(legacyWikiDir, { recursive: true }); await writeFile(join(legacyWikiDir, "legacy.md"), serializePage({ filename: "legacy.md", frontmatter: { title: "Legacy", tags: ["legacy"], created: now, updated: now, sources: [], links: [], category: "reference", confidence: "medium", schemaVersion: WIKI_SCHEMA_VERSION, }, content: "\n# Legacy\n\nLegacy wiki context must remain visible.\n", })); await writeJson(join(cwd, ".omx", "project-memory.json"), { techStack: "Legacy runtime memory should not win", notes: [{ category: "legacy", content: "stale legacy note", timestamp: now }], }); await writeJson(join(cwd, "project-memory.json"), { techStack: "Canonical root memory", build: "npm run build && node --test dist/scripts/__tests__/codex-native-hook.test.js", conventions: "prefer repository-visible project memory at startup", directives: [ { directive: "Load root project-memory.json before legacy .omx memory.", priority: "high", timestamp: now }, ], notes: [ { category: "issue", content: "Regression fixture for issue #2273.", timestamp: now }, ], }); const result = await dispatchCodexNativeHook( { hook_event_name: "SessionStart", cwd, session_id: "sess-root-memory-legacy-wiki", }, { cwd, sessionOwnerPid: 43210 }, ); const additionalContext = String( (result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? "", ); assert.match(additionalContext, /\[Project memory\]/); assert.match(additionalContext, /source: project-memory\.json/); assert.match(additionalContext, /Canonical root memory/); assert.match(additionalContext, /Load root project-memory\.json before legacy \.omx memory\./); assert.match(additionalContext, /Regression fixture for issue #2273\./); assert.doesNotMatch(additionalContext, /Legacy runtime memory should not win/); assert.match(additionalContext, /legacy pages at \.omx\/wiki\//); assert.match(additionalContext, /Legacy wiki fallback is read-only/); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("starts a fresh native session without inheriting stale task-scoped context", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-session-isolation-")); try { const stateDir = join(cwd, ".omx", "state"); const priorSessionId = "omx-old-session"; await mkdir(join(stateDir, "sessions", priorSessionId), { recursive: true }); await writeSessionStart(cwd, priorSessionId, { nativeSessionId: "codex-native-old", pid: 999_999_999, }); await writeJson(join(stateDir, "sessions", priorSessionId, "ralph-state.json"), { active: true, current_phase: "executing", }); await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { [priorSessionId]: { session_id: priorSessionId, leader_thread_id: "leader-1", updated_at: new Date().toISOString(), threads: { "leader-1": { thread_id: "leader-1", kind: "leader", first_seen_at: new Date().toISOString(), last_seen_at: new Date().toISOString(), turn_count: 1, }, "sub-1": { thread_id: "sub-1", kind: "subagent", first_seen_at: new Date().toISOString(), last_seen_at: new Date().toISOString(), turn_count: 1, }, }, }, }, }); await writeFile( join(cwd, ".omx", "notepad.md"), [ "# OMX Notepad", "", "## PRIORITY", "Preserve durable project guidance.", "", "## WORKING MEMORY", "[2026-04-06T00:33:44Z] stale UI rework context snapshot .omx/context/ui-rework-plan-01-20260406T003344Z.md", ].join("\n"), ); const result = await dispatchCodexNativeHook( { hook_event_name: "SessionStart", cwd, session_id: "codex-native-new", }, { cwd, sessionOwnerPid: process.pid, }, ); const sessionState = JSON.parse( await readFile(join(stateDir, "session.json"), "utf-8"), ) as { session_id?: string; native_session_id?: string }; assert.equal(sessionState.session_id, "codex-native-new"); assert.equal(sessionState.native_session_id, "codex-native-new"); const additionalContext = String( (result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? "", ); assert.match(additionalContext, /\[Execution environment\]/); assert.match(additionalContext, /native-hook \/ Codex App outside tmux/); assert.match(additionalContext, /\[Priority notes\]/); assert.match(additionalContext, /Preserve durable project guidance/); assert.doesNotMatch(additionalContext, /stale UI rework context snapshot/); assert.doesNotMatch(additionalContext, /\[Subagents\]/); assert.doesNotMatch(additionalContext, /ralph phase: executing/); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("allows same-session/current-thread Ralph Stop continuation", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ralph-stop-current-session-")); try { const stateDir = join(cwd, ".omx", "state"); await writeNativeMappedSessionState(cwd, stateDir, "omx-current-ralph", "codex-current-ralph"); await writeJson(join(stateDir, "sessions", "omx-current-ralph", "ralph-state.json"), { active: true, mode: "ralph", current_phase: "executing", owner_omx_session_id: "omx-current-ralph", owner_codex_session_id: "codex-current-ralph", owner_codex_thread_id: "thread-current-ralph", task_description: "Finish issue 2974 stale Ralph Stop-hook guard", }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "codex-current-ralph", thread_id: "thread-current-ralph", last_user_message: "continue the current ralph issue 2974 task", }, { cwd }, ); assert.equal(result.outputJson?.decision, "block"); assert.equal(result.outputJson?.stopReason, "ralph_executing"); assert.match(String(result.outputJson?.systemMessage), /Ralph is still active/); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not resume stale different-session global Ralph Stop state", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ralph-stop-stale-global-")); try { await writeJson(join(cwd, ".omx", "state", "ralph-state.json"), { active: true, mode: "ralph", current_phase: "executing", owner_codex_session_id: "codex-old-ralph", owner_codex_thread_id: "thread-old-ralph", task_description: "Finish issue 2974 stale Ralph Stop-hook guard", }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, thread_id: "thread-new-ralph", last_user_message: "continue the current ralph issue 2974 task", }, { cwd }, ); assert.equal(result.outputJson?.stopReason, undefined); assert.notEqual(result.outputJson?.decision, "block"); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not resume global Ralph Stop state for low-overlap unrelated tasks", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ralph-stop-low-overlap-")); try { await writeJson(join(cwd, ".omx", "state", "ralph-state.json"), { active: true, mode: "ralph", current_phase: "executing", task_description: "Refactor documentation navigation and README examples", }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, last_user_message: "continue auditing billing webhooks and payment retries", }, { cwd }, ); assert.equal(result.outputJson?.stopReason, undefined); assert.notEqual(result.outputJson?.decision, "block"); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("allows explicit current user continuation for live-risk Ralph tasks when owner context is current", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ralph-stop-live-current-")); try { await writeJson(join(cwd, ".omx", "state", "ralph-state.json"), { active: true, mode: "ralph", current_phase: "executing", owner_codex_thread_id: "thread-live-ralph", task_description: "Deploy billing migration rollback guard for production payments", }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, thread_id: "thread-live-ralph", last_user_message: "continue the current ralph deploy billing migration rollback guard", }, { cwd }, ); assert.equal(result.outputJson?.decision, "block"); assert.equal(result.outputJson?.stopReason, "ralph_executing"); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("allows current-owner global Ralph Stop continuation with generic current-task wording", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ralph-stop-current-owner-generic-")); try { await writeJson(join(cwd, ".omx", "state", "ralph-state.json"), { active: true, mode: "ralph", current_phase: "executing", owner_codex_thread_id: "thread-current-owner-generic", task_description: "Refactor documentation navigation and README examples", }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, thread_id: "thread-current-owner-generic", last_user_message: "continue the current ralph task", }, { cwd }, ); assert.equal(result.outputJson?.decision, "block"); assert.equal(result.outputJson?.stopReason, "ralph_executing"); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("allows same-thread global Ralph Stop continuation without payload text for non-live tasks", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ralph-stop-same-thread-no-text-")); try { await writeJson(join(cwd, ".omx", "state", "ralph-state.json"), { active: true, mode: "ralph", current_phase: "executing", owner_codex_thread_id: "thread-same-thread-no-text", task_description: "Refactor documentation navigation and README examples", }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, thread_id: "thread-same-thread-no-text", }, { cwd }, ); assert.equal(result.outputJson?.decision, "block"); assert.equal(result.outputJson?.stopReason, "ralph_executing"); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks no-text global Ralph Stop continuation for live-risk tasks", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ralph-stop-live-no-text-")); try { await writeJson(join(cwd, ".omx", "state", "ralph-state.json"), { active: true, mode: "ralph", current_phase: "executing", owner_codex_thread_id: "thread-live-no-text", task_description: "Deploy billing migration rollback guard for production payments", }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, thread_id: "thread-live-no-text", }, { cwd }, ); assert.equal(result.outputJson?.stopReason, undefined); assert.notEqual(result.outputJson?.decision, "block"); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks no-text global Ralph Stop continuation for issue 2974 operational live-risk tasks", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ralph-stop-issue-2974-live-no-text-")); try { await writeJson(join(cwd, ".omx", "state", "ralph-state.json"), { active: true, mode: "ralph", current_phase: "executing", task_description: "Implement Telegram assistant GPT switch plan on VPS service with cron restart send notify workflow", }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, }, { cwd }, ); assert.equal(result.outputJson?.stopReason, undefined); assert.notEqual(result.outputJson?.decision, "block"); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("resolves the Codex owner from ancestry without mistaking codex-native-hook wrappers for Codex", () => { const commands = new Map([ [2100, 'sh -c node "/repo/dist/scripts/codex-native-hook.js"'], [1100, 'node /usr/local/bin/codex.js'], [900, 'bash'], ]); const parents = new Map([ [2100, 1100], [1100, 900], [900, 1], ]); const resolved = resolveSessionOwnerPidFromAncestry(2100, { readParentPid: (pid) => parents.get(pid) ?? null, readProcessCommand: (pid) => commands.get(pid) ?? "", }); assert.equal(resolved, 1100); }); it("does not pin an ephemeral hook owner when no Codex ancestor is present", () => { const commands = new Map([ [2100, "node /repo/plugins/oh-my-codex/hooks/codex-native-hook.mjs"], [1100, "sh -c node codex-native-hook"], [900, "bash"], ]); const parents = new Map([ [2100, 1100], [1100, 900], [900, 1], ]); const resolved = resolveSessionOwnerPidFromAncestry(2100, { requireCodexAncestor: true, readParentPid: (pid) => parents.get(pid) ?? null, readProcessCommand: (pid) => commands.get(pid) ?? "", }); assert.equal(resolved, null); }); it("resolves the Codex owner from Windows process lineage without Unix ps", () => { const resolved = resolveSessionOwnerPidFromAncestry(1700, { platform: "win32", readProcessLineage: () => [ { pid: 1700, name: "powershell.exe", command: 'powershell.exe -File "C:\\Users\\user\\.codex\\hooks\\omx-native-hook-windows-shim.ps1"', }, { pid: 19072, name: "pwsh.exe", command: 'pwsh.exe -Command "verify expected codex.exe owner"', }, { pid: 14440, name: "codex.exe", command: '"C:\\Users\\user\\AppData\\Roaming\\npm\\node_modules\\@openai\\codex\\codex.exe"', }, { pid: 14400, name: "node.exe", command: "node.exe codex.js" }, ], }); assert.equal(resolved, 14440); }); it("records keyword activation from UserPromptSubmit payloads", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-")); try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "sess-1", thread_id: "thread-1", turn_id: "turn-1", prompt: "$ralplan implement issue #1307", }, { cwd }, ); assert.equal(result.omxEventName, "keyword-detector"); assert.equal(result.skillState?.skill, "ralplan"); assert.ok(result.outputJson, "UserPromptSubmit should emit developer context"); assert.match(JSON.stringify(result.outputJson), /use CLI-first state updates via `omx state write\/read\/clear --input '' --json`/); assert.equal( existsSync(join(cwd, ".omx", "state", "skill-active-state.json")), false, "session-scoped keyword activation should not write root skill-active-state.json", ); const statePath = join(cwd, ".omx", "state", "sessions", "sess-1", "skill-active-state.json"); assert.equal(existsSync(statePath), true); const state = JSON.parse(await readFile(statePath, "utf-8")) as { skill?: string; active?: boolean; initialized_mode?: string; }; assert.equal(state.skill, "ralplan"); assert.equal(state.active, true); assert.equal(state.initialized_mode, "ralplan"); assert.equal(existsSync(join(cwd, ".omx", "state", "sessions", "sess-1", "ralplan-state.json")), true); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("injects deep-interview config overrides into UserPromptSubmit developer context", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-deep-interview-config-")); try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); await writeFile( join(cwd, ".omx", "config.toml"), `[omx.deepInterview] defaultProfile = "standard" standardThreshold = 0.05 standardMaxRounds = 15 enableChallengeModes = false `, ); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "sess-deep-interview-config", thread_id: "thread-1", turn_id: "turn-1", prompt: "$deep-interview prove config reflection", }, { cwd }, ); assert.equal(result.omxEventName, "keyword-detector"); assert.equal(result.skillState?.skill, "deep-interview"); const serializedOutput = JSON.stringify(result.outputJson); assert.match(serializedOutput, /Deep-interview config override active/); assert.match(serializedOutput, /threshold=0\.05/); assert.match(serializedOutput, /max_rounds=15/); assert.match(serializedOutput, /enableChallengeModes=false/); const modeState = JSON.parse( await readFile( join( cwd, ".omx", "state", "sessions", "sess-deep-interview-config", "deep-interview-state.json", ), "utf-8", ), ) as { threshold?: number; max_rounds?: number; profile?: string }; assert.equal(modeState.profile, "standard"); assert.equal(modeState.threshold, 0.05); assert.equal(modeState.max_rounds, 15); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("proves UserPromptSubmit context changes before and after adding deep-interview config", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-deep-interview-config-before-after-"), ); const sessionId = "sess-deep-interview-config-before-after"; try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); const before = await withIsolatedHome( "deep-interview-config-before-after", async () => dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: sessionId, thread_id: "thread-before-after", turn_id: "turn-before", prompt: "$deep-interview prove before config context", }, { cwd }, ), ); const beforeOutput = JSON.stringify(before.outputJson); const beforeState = JSON.parse( await readFile( join( cwd, ".omx", "state", "sessions", sessionId, "deep-interview-state.json", ), "utf-8", ), ) as { deep_interview_config?: unknown; threshold?: number; max_rounds?: number; }; assert.equal(before.skillState?.skill, "deep-interview"); assert.doesNotMatch( beforeOutput, /Deep-interview config override active/, ); assert.equal(before.skillState?.deep_interview_config, undefined); assert.equal(beforeState.deep_interview_config, undefined); assert.equal(beforeState.threshold, undefined); assert.equal(beforeState.max_rounds, undefined); await writeFile( join(cwd, ".omx", "config.toml"), `[omx.deepInterview] defaultProfile = "standard" standardThreshold = 0.05 standardMaxRounds = 15 `, ); const after = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: sessionId, thread_id: "thread-before-after", turn_id: "turn-after", prompt: "$deep-interview prove after config context", }, { cwd }, ); const afterOutput = JSON.stringify(after.outputJson); const afterState = JSON.parse( await readFile( join( cwd, ".omx", "state", "sessions", sessionId, "deep-interview-state.json", ), "utf-8", ), ) as { deep_interview_config?: { profile?: string; threshold?: number; maxRounds?: number; }; threshold?: number; max_rounds?: number; }; assert.equal( after.skillState?.deep_interview_config?.profile, "standard", ); assert.match(afterOutput, /Deep-interview config override active/); assert.match(afterOutput, /threshold=0\.05/); assert.match(afterOutput, /max_rounds=15/); assert.equal(afterState.deep_interview_config?.profile, "standard"); assert.equal(afterState.threshold, 0.05); assert.equal(afterState.max_rounds, 15); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("injects deep-interview config for mixed workflow prompts that defer execution modes", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-deep-interview-config-mixed-"), ); const sessionId = "sess-deep-interview-config-mixed"; try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); await writeFile( join(cwd, ".omx", "config.toml"), `[omx.deepInterview] defaultProfile = "deep" deepThreshold = 0.13 deepMaxRounds = 21 enableChallengeModes = false `, ); const result = await withIsolatedHome( "deep-interview-config-mixed", async () => dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: sessionId, thread_id: "thread-mixed-config", turn_id: "turn-mixed-config", prompt: "$autopilot $deep-interview prove mixed config context", }, { cwd }, ), ); const serializedOutput = JSON.stringify(result.outputJson); const modeState = JSON.parse( await readFile( join( cwd, ".omx", "state", "sessions", sessionId, "deep-interview-state.json", ), "utf-8", ), ) as { deep_interview_config?: { profile?: string; threshold?: number; maxRounds?: number; enableChallengeModes?: boolean; }; profile?: string; threshold?: number; max_rounds?: number; enable_challenge_modes?: boolean; }; assert.equal(result.skillState?.skill, "deep-interview"); assert.deepEqual(result.skillState?.deferred_skills, ["autopilot"]); assert.equal(result.skillState?.deep_interview_config?.profile, "deep"); assert.equal(result.skillState?.deep_interview_config?.threshold, 0.13); assert.equal(result.skillState?.deep_interview_config?.maxRounds, 21); assert.equal( result.skillState?.deep_interview_config?.enableChallengeModes, false, ); assert.match(serializedOutput, /Deep-interview config override active/); assert.match(serializedOutput, /profile=deep/); assert.match(serializedOutput, /threshold=0\.13/); assert.match(serializedOutput, /max_rounds=21/); assert.match(serializedOutput, /enableChallengeModes=false/); assert.equal(modeState.deep_interview_config?.profile, "deep"); assert.equal(modeState.profile, "deep"); assert.equal(modeState.threshold, 0.13); assert.equal(modeState.max_rounds, 21); assert.equal(modeState.enable_challenge_modes, false); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("keeps deep-interview config override context on continuation prompts", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-deep-interview-config-continuation-"), ); const sessionId = "sess-deep-interview-config-continuation"; try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); await writeFile( join(cwd, ".omx", "config.toml"), `[omx.deepInterview] defaultProfile = "standard" standardThreshold = 0.05 standardMaxRounds = 15 `, ); await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: sessionId, thread_id: "thread-continuation", turn_id: "turn-start", prompt: "$deep-interview prove config continuation", }, { cwd }, ); const continued = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: sessionId, thread_id: "thread-continuation", turn_id: "turn-continue", prompt: "continue", }, { cwd }, ); const serializedOutput = JSON.stringify(continued.outputJson); const modeState = JSON.parse( await readFile( join( cwd, ".omx", "state", "sessions", sessionId, "deep-interview-state.json", ), "utf-8", ), ) as { threshold?: number; max_rounds?: number; profile?: string }; assert.equal(continued.skillState?.skill, "deep-interview"); assert.match(serializedOutput, /Deep-interview config override active/); assert.match(serializedOutput, /threshold=0\.05/); assert.match(serializedOutput, /max_rounds=15/); assert.equal(modeState.profile, "standard"); assert.equal(modeState.threshold, 0.05); assert.equal(modeState.max_rounds, 15); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("keeps explicit deep-interview profile flags reflected on continuation prompts", async () => { const cwd = await mkdtemp( join( tmpdir(), "omx-native-hook-deep-interview-config-profile-continuation-", ), ); const sessionId = "sess-deep-interview-config-profile-continuation"; try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); await writeFile( join(cwd, ".omx", "config.toml"), `[omx.deepInterview] defaultProfile = "standard" standardThreshold = 0.22 standardMaxRounds = 13 deepThreshold = 0.13 deepMaxRounds = 21 `, ); await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: sessionId, thread_id: "thread-profile-continuation", turn_id: "turn-start", prompt: "$deep-interview --deep prove explicit profile continuation", }, { cwd }, ); const continued = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: sessionId, thread_id: "thread-profile-continuation", turn_id: "turn-continue", prompt: "continue", }, { cwd }, ); const serializedOutput = JSON.stringify(continued.outputJson); const modeState = JSON.parse( await readFile( join( cwd, ".omx", "state", "sessions", sessionId, "deep-interview-state.json", ), "utf-8", ), ) as { threshold?: number; max_rounds?: number; profile?: string; deep_interview_config?: { profile?: string }; }; assert.equal(continued.skillState?.skill, "deep-interview"); assert.equal( continued.skillState?.deep_interview_config?.profile, "deep", ); assert.match(serializedOutput, /Deep-interview config override active/); assert.match(serializedOutput, /profile=deep/); assert.match(serializedOutput, /threshold=0\.13/); assert.match(serializedOutput, /max_rounds=21/); assert.equal(modeState.deep_interview_config?.profile, "deep"); assert.equal(modeState.profile, "deep"); assert.equal(modeState.threshold, 0.13); assert.equal(modeState.max_rounds, 21); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("keeps the documented deep-interview Suggested Config reflected in UserPromptSubmit context", async () => { const skillDoc = await readFile( join(process.cwd(), "skills", "deep-interview", "SKILL.md"), "utf-8", ); assert.match(skillDoc, /Socratic deep interview/i); assert.match(skillDoc, /Suggested Config/i); const documentedConfig = `[omx.deepInterview] defaultProfile = "standard" quickThreshold = 0.30 standardThreshold = 0.20 deepThreshold = 0.15 quickMaxRounds = 5 standardMaxRounds = 12 deepMaxRounds = 20 enableChallengeModes = true`; const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-deep-interview-doc-config-"), ); const sessionId = "sess-deep-interview-doc-config"; try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); await writeFile( join(cwd, ".omx", "config.toml"), `${documentedConfig}\n`, ); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: sessionId, thread_id: "thread-doc-config", turn_id: "turn-doc-config", prompt: "$deep-interview prove documented config context", }, { cwd }, ); const serializedOutput = JSON.stringify(result.outputJson); const modeState = JSON.parse( await readFile( join( cwd, ".omx", "state", "sessions", sessionId, "deep-interview-state.json", ), "utf-8", ), ) as { deep_interview_config?: { profile?: string; threshold?: number; maxRounds?: number; }; profile?: string; threshold?: number; max_rounds?: number; }; assert.equal( result.skillState?.deep_interview_config?.profile, "standard", ); assert.equal(result.skillState?.deep_interview_config?.threshold, 0.2); assert.equal(result.skillState?.deep_interview_config?.maxRounds, 12); assert.match(serializedOutput, /Deep-interview config override active/); assert.match(serializedOutput, /profile=standard/); assert.match(serializedOutput, /threshold=0\.2/); assert.match(serializedOutput, /max_rounds=12/); assert.equal(modeState.deep_interview_config?.profile, "standard"); assert.equal(modeState.profile, "standard"); assert.equal(modeState.threshold, 0.2); assert.equal(modeState.max_rounds, 12); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("injects deep-interview config overrides when state is boxed under OMX_ROOT", async () => { const root = await mkdtemp( join(tmpdir(), "omx-native-hook-deep-interview-config-boxed-"), ); const cwd = join(root, "source"); const omxRoot = join(root, "box"); const sessionId = "sess-boxed-deep-interview-config"; const previousOmxRoot = process.env.OMX_ROOT; const previousOmxStateRoot = process.env.OMX_STATE_ROOT; const previousTeamStateRoot = process.env.OMX_TEAM_STATE_ROOT; try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); await writeFile( join(cwd, ".omx", "config.toml"), `[omx.deepInterview] defaultProfile = "standard" standardThreshold = 0.05 standardMaxRounds = 15 `, ); process.env.OMX_ROOT = omxRoot; delete process.env.OMX_STATE_ROOT; delete process.env.OMX_TEAM_STATE_ROOT; const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: sessionId, thread_id: "thread-boxed", turn_id: "turn-boxed", prompt: "$deep-interview prove boxed config reflection", }, { cwd }, ); assert.equal(result.omxEventName, "keyword-detector"); assert.equal( result.skillState?.initialized_state_path, `.omx/state/sessions/${sessionId}/deep-interview-state.json`, ); const boxedStatePath = join( omxRoot, ".omx", "state", "sessions", sessionId, "deep-interview-state.json", ); assert.equal(existsSync(boxedStatePath), true); assert.equal( existsSync( join( cwd, ".omx", "state", "sessions", sessionId, "deep-interview-state.json", ), ), false, ); const serializedOutput = JSON.stringify(result.outputJson); assert.match(serializedOutput, /Deep-interview config override active/); assert.match(serializedOutput, /threshold=0\.05/); assert.match(serializedOutput, /max_rounds=15/); } finally { if (typeof previousOmxRoot === "string") process.env.OMX_ROOT = previousOmxRoot; else delete process.env.OMX_ROOT; if (typeof previousOmxStateRoot === "string") process.env.OMX_STATE_ROOT = previousOmxStateRoot; else delete process.env.OMX_STATE_ROOT; if (typeof previousTeamStateRoot === "string") process.env.OMX_TEAM_STATE_ROOT = previousTeamStateRoot; else delete process.env.OMX_TEAM_STATE_ROOT; await rm(root, { recursive: true, force: true }); } }); it("records boxed keyword activation mode detail and skill state under OMX_ROOT", async () => { const root = await mkdtemp(join(tmpdir(), "omx-native-hook-boxed-")); const cwd = join(root, "source"); const omxRoot = join(root, "box"); const sessionId = "sess-boxed-ralplan"; const previousOmxRoot = process.env.OMX_ROOT; const previousOmxStateRoot = process.env.OMX_STATE_ROOT; const previousTeamStateRoot = process.env.OMX_TEAM_STATE_ROOT; const previousOmxSessionId = process.env.OMX_SESSION_ID; try { await mkdir(cwd, { recursive: true }); process.env.OMX_ROOT = omxRoot; delete process.env.OMX_STATE_ROOT; delete process.env.OMX_TEAM_STATE_ROOT; process.env.OMX_SESSION_ID = sessionId; const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: sessionId, thread_id: "thread-boxed", turn_id: "turn-boxed", prompt: "$ralplan implement issue #1307", }, { cwd }, ); assert.equal(result.omxEventName, "keyword-detector"); assert.equal(result.skillState?.skill, "ralplan"); const boxedSessionDir = join( omxRoot, ".omx", "state", "sessions", sessionId, ); assert.equal( existsSync(join(boxedSessionDir, "skill-active-state.json")), true, ); assert.equal( existsSync(join(boxedSessionDir, "ralplan-state.json")), true, ); assert.equal( existsSync( join( cwd, ".omx", "state", "sessions", sessionId, "skill-active-state.json", ), ), false, ); assert.equal( existsSync( join( cwd, ".omx", "state", "sessions", sessionId, "ralplan-state.json", ), ), false, ); const hudState = await readAllState(cwd); assert.equal(hudState.ralplan?.active, true); assert.equal(hudState.ralplan?.current_phase, "planning"); } finally { if (typeof previousOmxRoot === "string") process.env.OMX_ROOT = previousOmxRoot; else delete process.env.OMX_ROOT; if (typeof previousOmxStateRoot === "string") process.env.OMX_STATE_ROOT = previousOmxStateRoot; else delete process.env.OMX_STATE_ROOT; if (typeof previousTeamStateRoot === "string") process.env.OMX_TEAM_STATE_ROOT = previousTeamStateRoot; else delete process.env.OMX_TEAM_STATE_ROOT; if (typeof previousOmxSessionId === "string") process.env.OMX_SESSION_ID = previousOmxSessionId; else delete process.env.OMX_SESSION_ID; await rm(root, { recursive: true, force: true }); } }); it("records native keyword activation mode detail and skill state under OMX_TEAM_STATE_ROOT", async () => { const root = await mkdtemp(join(tmpdir(), "omx-native-hook-team-root-")); const cwd = join(root, "source"); const teamStateRoot = join(root, "team-state"); const sessionId = "sess-team-root-ralplan"; const previousOmxRoot = process.env.OMX_ROOT; const previousOmxStateRoot = process.env.OMX_STATE_ROOT; const previousTeamStateRoot = process.env.OMX_TEAM_STATE_ROOT; const previousOmxSessionId = process.env.OMX_SESSION_ID; try { await mkdir(cwd, { recursive: true }); delete process.env.OMX_ROOT; delete process.env.OMX_STATE_ROOT; process.env.OMX_TEAM_STATE_ROOT = teamStateRoot; process.env.OMX_SESSION_ID = sessionId; const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: sessionId, thread_id: "thread-team-root", turn_id: "turn-team-root", prompt: "$ralplan implement issue #1307", }, { cwd }, ); assert.equal(result.omxEventName, "keyword-detector"); assert.equal(result.skillState?.skill, "ralplan"); const teamSessionDir = join(teamStateRoot, "sessions", sessionId); assert.equal( existsSync(join(teamSessionDir, "skill-active-state.json")), true, ); assert.equal( existsSync(join(teamSessionDir, "ralplan-state.json")), true, ); assert.equal( existsSync( join( cwd, ".omx", "state", "sessions", sessionId, "skill-active-state.json", ), ), false, ); assert.equal( existsSync( join( cwd, ".omx", "state", "sessions", sessionId, "ralplan-state.json", ), ), false, ); const hudState = await readAllState(cwd); assert.equal(hudState.ralplan?.active, true); assert.equal(hudState.ralplan?.current_phase, "planning"); } finally { if (typeof previousOmxRoot === "string") process.env.OMX_ROOT = previousOmxRoot; else delete process.env.OMX_ROOT; if (typeof previousOmxStateRoot === "string") process.env.OMX_STATE_ROOT = previousOmxStateRoot; else delete process.env.OMX_STATE_ROOT; if (typeof previousTeamStateRoot === "string") process.env.OMX_TEAM_STATE_ROOT = previousTeamStateRoot; else delete process.env.OMX_TEAM_STATE_ROOT; if (typeof previousOmxSessionId === "string") process.env.OMX_SESSION_ID = previousOmxSessionId; else delete process.env.OMX_SESSION_ID; await rm(root, { recursive: true, force: true }); } }); it("classifies only actionable goal completion wording", () => { const actionable = [ "complete this goal now", 'Performance goal complete; next call update_goal({status: "complete"}).', "get_goal returned a completed legacy goal, so ultragoal complete failed; marking complete now.", "omx ultragoal checkpoint --goal-id G001-demo --status complete --codex-goal-json goal.json", 'Call update_goal({status: "complete"}) after verification.', "Goal complete.", "The goal is complete.", "Goal complete: verified with tests.", "Goal complete — verified with tests.", "The goal is complete: verified.", "The goal is complete — verified.", ]; const ordinary = [ "my goal is to complete the migration without regressions", "Our goal is to finish this carefully after tests pass.", "The goal of this patch is to close a review gap.", "A goal can be complete only after a human review.", ]; for (const text of actionable) { assert.equal(looksLikeGoalCompletionPrompt(text), true, text); } for (const text of ordinary) { assert.equal(looksLikeGoalCompletionPrompt(text), false, text); } }); it("warns completion-like prompts when active goal workflows need Codex snapshot reconciliation", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-goal-warning-")); try { await writeJson(join(cwd, ".omx", "ultragoal", "goals.json"), { version: 1, activeGoalId: "G001-demo", goals: [ { id: "G001-demo", status: "in_progress", objective: "Demo goal" }, ], }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "sess-goal-warning", thread_id: "thread-goal-warning", prompt: "complete this goal now", }, { cwd }, ); assert.match( JSON.stringify(result.outputJson), /requires Codex goal snapshot reconciliation/, ); assert.match(JSON.stringify(result.outputJson), /get_goal/); assert.match(JSON.stringify(result.outputJson), /--codex-goal-json/); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks Stop when a completion-like final answer skips active goal snapshot reconciliation", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-goal-stop-")); try { await writeJson( join(cwd, ".omx", "goals", "performance", "latency", "state.json"), { version: 1, workflow: "performance-goal", slug: "latency", objective: "Reduce latency", status: "validation_passed", }, ); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-goal-stop", thread_id: "thread-goal-stop", last_assistant_message: 'Performance goal complete; next call update_goal({status: "complete"}).', }, { cwd }, ); assert.equal(result.outputJson?.decision, "block"); assert.match( JSON.stringify(result.outputJson), /get_goal snapshot reconciliation/, ); assert.match( JSON.stringify(result.outputJson), /omx performance-goal complete --slug latency/, ); assert.match( JSON.stringify(result.outputJson), /Hooks must not mutate Codex goal state/, ); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not repeat performance-goal reconciliation after a recorded objective mismatch blocker", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-performance-mismatch-blocked-stop-"), ); try { await writeJson( join(cwd, ".omx", "goals", "performance", "latency", "state.json"), { version: 1, workflow: "performance-goal", slug: "latency", objective: "Reduce latency", status: "blocked", lastValidation: { status: "blocked", evidence: 'omx performance-goal complete rejected the fresh get_goal snapshot: Codex goal objective mismatch: expected "reduce latency", got "legacy objective".', recordedAt: "2026-05-20T00:00:00.000Z", }, }, ); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-performance-mismatch-blocked-stop", thread_id: "thread-performance-mismatch-blocked-stop", last_assistant_message: 'Performance goal complete; next call update_goal({status: "complete"}).', }, { cwd }, ); assert.notEqual(result.outputJson?.decision, "block"); assert.doesNotMatch( JSON.stringify(result.outputJson), /omx performance-goal complete --slug latency/, ); assert.doesNotMatch( JSON.stringify(result.outputJson), /get_goal snapshot reconciliation/, ); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not block Stop for an already complete performance-goal state", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-performance-complete-stop-"), ); try { await writeJson( join(cwd, ".omx", "goals", "performance", "latency", "state.json"), { version: 1, workflow: "performance-goal", slug: "latency", objective: "Reduce latency", status: "complete", completedAt: "2026-05-20T00:00:00.000Z", }, ); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-performance-complete-stop", thread_id: "thread-performance-complete-stop", last_assistant_message: 'Performance goal complete; next call update_goal({status: "complete"}).', }, { cwd }, ); assert.notEqual(result.outputJson?.decision, "block"); assert.doesNotMatch( JSON.stringify(result.outputJson), /omx performance-goal complete --slug latency/, ); assert.doesNotMatch( JSON.stringify(result.outputJson), /get_goal snapshot reconciliation/, ); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("nudges next-goal prompts when a completed Codex goal cleanup step remains", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-completed-goal-prompt-"), ); try { await writeJson( join(cwd, ".omx", "goals", "performance", "latency", "state.json"), { version: 1, workflow: "performance-goal", slug: "latency", objective: "Reduce latency", status: "complete", completedAt: "2026-05-20T00:00:00.000Z", }, ); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "sess-completed-goal-prompt", thread_id: "thread-completed-goal-prompt", prompt: "Start the next performance goal now", }, { cwd }, ); const output = JSON.stringify(result.outputJson); assert.match(output, /run \/goal clear/); assert.match(output, /before calling create_goal/); assert.match( output, /hooks only nudge and must not mutate Codex goal state/, ); assert.doesNotMatch(output, /cleared Codex goal state/i); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not block ordinary Stop after completed Ultragoal cleanup remains", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-completed-ultragoal-ordinary-stop-"), ); try { await writeJson(join(cwd, ".omx", "ultragoal", "goals.json"), { version: 1, aggregateCompletion: { status: "complete", completedAt: "2026-05-20T00:00:00.000Z", }, goals: [{ id: "G001-done", status: "complete", objective: "Done" }], }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-completed-ultragoal-ordinary-stop", thread_id: "thread-completed-ultragoal-ordinary-stop", last_assistant_message: "Implemented the requested fix and verified the focused tests.", }, { cwd }, ); const output = JSON.stringify(result.outputJson); assert.notEqual(result.outputJson?.decision, "block"); assert.notEqual( result.outputJson?.stopReason, "completed_codex_goal_cleanup_required", ); assert.doesNotMatch(output, /run \/goal clear/); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not treat explanatory create_goal warnings as completed-goal cleanup attempts", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-completed-goal-explainer-stop-"), ); try { await writeJson(join(cwd, ".omx", "ultragoal", "goals.json"), { version: 1, aggregateCompletion: { status: "complete", completedAt: "2026-05-20T00:00:00.000Z", }, goals: [{ id: "G001-done", status: "complete", objective: "Done" }], }); for (const [index, last_assistant_message] of [ "Do not call create_goal until the user has explicitly cleared the old goal state.", "No create_goal attempt was made; this is only the final summary.", "I am not calling create_goal; the completed work is summarized above.", "I am not starting another goal; cleanup is already documented.", "Do not start another goal until cleanup is explicit.", "Do not create a new ultragoal; this is only a final summary.", ].entries()) { const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: `sess-completed-goal-explainer-stop-${index}`, thread_id: `thread-completed-goal-explainer-stop-${index}`, last_assistant_message, }, { cwd }, ); const output = JSON.stringify(result.outputJson); assert.notEqual(result.outputJson?.decision, "block"); assert.notEqual( result.outputJson?.stopReason, "completed_codex_goal_cleanup_required", ); assert.doesNotMatch(output, /run \/goal clear/); } } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks explicit create_goal attempts when fresh native-goal cleanup evidence remains", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-completed-goal-mixed-stop-"), ); try { await writeJson(join(cwd, ".omx", "ultragoal", "goals.json"), { version: 1, aggregateCompletion: { status: "complete", completedAt: "2026-05-20T00:00:00.000Z", }, goals: [{ id: "G001-done", status: "complete", objective: "Done" }], }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-completed-goal-mixed-stop", thread_id: "thread-completed-goal-mixed-stop", last_user_message: "get_goal reports a completed Codex goal still attached to this thread; do not call create_goal until cleanup is explicit.", last_assistant_message: "I am starting another run now; create_goal payload follows.", }, { cwd }, ); const output = JSON.stringify(result.outputJson); assert.equal(result.outputJson?.decision, "block"); assert.equal( result.outputJson?.stopReason, "completed_codex_goal_cleanup_required", ); assert.match(output, /run \/goal clear/); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not block Stop when completed durable history alone precedes another goal", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-completed-goal-stop-"), ); try { await writeJson(join(cwd, ".omx", "ultragoal", "goals.json"), { version: 1, aggregateCompletion: { status: "complete", completedAt: "2026-05-20T00:00:00.000Z", }, goals: [{ id: "G001-done", status: "complete", objective: "Done" }], }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-completed-goal-stop", thread_id: "thread-completed-goal-stop", last_assistant_message: "Starting another ultragoal now; create_goal payload follows after /goal clear already cleared native state.", }, { cwd }, ); const output = JSON.stringify(result.outputJson); assert.notEqual(result.outputJson?.decision, "block"); assert.notEqual( result.outputJson?.stopReason, "completed_codex_goal_cleanup_required", ); assert.doesNotMatch(output, /run \/goal clear/); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks ultragoal Stop for concise generic goal completion claims", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-ultragoal-generic-complete-stop-"), ); try { await writeJson(join(cwd, ".omx", "ultragoal", "goals.json"), { version: 1, activeGoalId: "G001-demo", goals: [ { id: "G001-demo", status: "in_progress", objective: "Demo goal" }, ], }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-ultragoal-generic-complete-stop", thread_id: "thread-ultragoal-generic-complete-stop", last_assistant_message: "Goal complete.", }, { cwd }, ); assert.equal(result.outputJson?.decision, "block"); assert.match( JSON.stringify(result.outputJson), /omx ultragoal checkpoint --goal-id G001-demo --status complete/, ); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("gives finite Stop guidance for paused ultragoals", async () => { for (const status of ["review_blocked", "needs_user_decision"]) { const cwd = await mkdtemp(join(tmpdir(), `omx-native-hook-ultragoal-${status}-stop-`)); try { await writeJson(join(cwd, ".omx", "ultragoal", "goals.json"), { version: 1, activeGoalId: "G001-paused", goals: [{ id: "G001-paused", status, objective: "Paused goal" }], }); const payload = { hook_event_name: "Stop", cwd, session_id: `sess-ultragoal-${status}-stop`, thread_id: `thread-ultragoal-${status}-stop`, turn_id: `turn-ultragoal-${status}-stop`, last_assistant_message: "Goal complete.", }; const first = await dispatchCodexNativeHook(payload, { cwd }); assert.equal(first.outputJson?.decision, "block"); assert.equal(first.outputJson?.stopReason, "ultragoal_paused"); assert.match(String(first.outputJson?.systemMessage), new RegExp(`status ${status}`)); assert.doesNotMatch(JSON.stringify(first.outputJson), /ultragoal checkpoint/); const replayed = await dispatchCodexNativeHook( { ...payload, stop_hook_active: true }, { cwd }, ); assert.equal(replayed.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } } }); it("does not block ultragoal Stop for ordinary prose about a goal to complete work", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-ultragoal-ordinary-stop-"), ); try { await writeJson(join(cwd, ".omx", "ultragoal", "goals.json"), { version: 1, activeGoalId: "G001-demo", goals: [ { id: "G001-demo", status: "in_progress", objective: "Demo goal" }, ], }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-ultragoal-ordinary-stop", thread_id: "thread-ultragoal-ordinary-stop", last_assistant_message: "My goal is to complete the migration without regressions, so I will keep testing.", }, { cwd }, ); assert.notEqual( result.outputJson?.stopReason, "ultragoal_codex_goal_snapshot_required", ); assert.doesNotMatch( JSON.stringify(result.outputJson), /omx ultragoal checkpoint --goal-id G001-demo --status complete/, ); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks ultragoal Stop with blocked checkpoint and available-goal-context remediation for completed legacy snapshots", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-ultragoal-legacy-stop-"), ); try { await writeJson(join(cwd, ".omx", "ultragoal", "goals.json"), { version: 1, activeGoalId: "G001-demo", goals: [ { id: "G001-demo", status: "in_progress", objective: "Demo goal" }, ], }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-ultragoal-legacy-stop", thread_id: "thread-ultragoal-legacy-stop", last_assistant_message: "get_goal returned a completed legacy goal, so ultragoal complete failed; marking complete now.", }, { cwd }, ); const output = JSON.stringify(result.outputJson); assert.equal(result.outputJson?.decision, "block"); assert.match( output, /omx ultragoal checkpoint --goal-id G001-demo --status complete/, ); assert.match(output, /--status blocked/); assert.match(output, /Codex goal context/); assert.match(output, /no such table: thread_goals/); assert.match(output, /unavailable get_goal error JSON or path/); assert.match(output, /safe-recovery blocker/); assert.doesNotMatch(output, /fresh (?:Codex )?(?:thread|session)s?/i); assert.match(output, /Hooks must not mutate Codex goal state/); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not repeat ultragoal Stop recovery after a safe completed-aggregate microgoal blocker is recorded", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-ultragoal-aggregate-blocked-stop-"), ); try { await writeJson(join(cwd, ".omx", "ultragoal", "goals.json"), { version: 1, codexGoalMode: "aggregate", activeGoalId: "G001-demo", goals: [ { id: "G001-demo", status: "in_progress", objective: "Demo goal", failureReason: "aggregate Codex goal already complete and unreconcilable while repo-native .omx/ultragoal/goals.json still has an in-progress microgoal; stop the recovery loop", }, ], }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-ultragoal-aggregate-blocked-stop", thread_id: "thread-ultragoal-aggregate-blocked-stop", stop_hook_active: true, last_assistant_message: "Goal complete.", }, { cwd }, ); assert.notEqual(result.outputJson?.decision, "block"); assert.notEqual( result.outputJson?.stopReason, "ultragoal_codex_goal_snapshot_required", ); assert.doesNotMatch( JSON.stringify(result.outputJson), /omx ultragoal checkpoint --goal-id G001-demo --status complete/, ); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not block ultragoal Stop after task-scoped reconciliation finishes exploded bookkeeping", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-ultragoal-reconciled-stop-"), ); try { await writeJson(join(cwd, ".omx", "ultragoal", "goals.json"), { version: 1, codexGoalMode: "aggregate", codexObjective: "Complete the durable ultragoal plan in .omx/ultragoal/goals.json, including later accepted/appended stories, under the original brief constraints; use .omx/ultragoal/ledger.jsonl as the audit trail.", activeGoalId: "G001-micro", aggregateCompletion: { status: "complete", completedAt: "2026-05-04T10:04:00.000Z", evidence: "planned work done; validation complete; reviews clean", }, goals: Array.from({ length: 136 }, (_, index) => ({ id: `G${String(index + 1).padStart(3, "0")}-micro`, status: index === 0 ? "in_progress" : "pending", objective: `Synthetic slice ${index + 1}.`, })), }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-ultragoal-reconciled-stop", thread_id: "thread-ultragoal-reconciled-stop", last_assistant_message: "Yes — planned implementation work is done; ultragoal bookkeeping reconciled complete.", }, { cwd }, ); assert.notEqual( result.outputJson?.stopReason, "ultragoal_codex_goal_snapshot_required", ); assert.doesNotMatch( JSON.stringify(result.outputJson), /omx ultragoal checkpoint --goal-id/, ); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not block Stop for non-passing autoresearch-goal professor-critic verdicts", async () => { for (const verdict of ["blocked", "fail", "failed"]) { const cwd = await mkdtemp( join(tmpdir(), `omx-native-hook-autoresearch-${verdict}-stop-`), ); const slug = `${verdict}-mission`; try { await writeJson( join(cwd, ".omx", "goals", "autoresearch", slug, "mission.json"), { version: 1, workflow: "autoresearch-goal", slug, topic: "Blocked research", status: verdict === "blocked" ? "blocked" : "failed", }, ); await writeJson( join(cwd, ".omx", "goals", "autoresearch", slug, "completion.json"), { verdict, passed: false, }, ); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: `sess-autoresearch-${verdict}-stop`, thread_id: `thread-autoresearch-${verdict}-stop`, last_assistant_message: 'Autoresearch goal complete; next call update_goal({status: "complete"}).', }, { cwd }, ); assert.notEqual(result.outputJson?.decision, "block"); assert.doesNotMatch( JSON.stringify(result.outputJson), new RegExp(`autoresearch-goal complete --slug ${slug}`), ); assert.doesNotMatch( JSON.stringify(result.outputJson), /get_goal snapshot reconciliation/, ); } finally { await rm(cwd, { recursive: true, force: true }); } } }); it("blocks Stop for passing autoresearch-goal professor-critic verdicts that need reconciliation", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-autoresearch-pass-stop-"), ); try { await writeJson( join( cwd, ".omx", "goals", "autoresearch", "passing-mission", "mission.json", ), { version: 1, workflow: "autoresearch-goal", slug: "passing-mission", topic: "Passing research", status: "validation_passed", }, ); await writeJson( join( cwd, ".omx", "goals", "autoresearch", "passing-mission", "completion.json", ), { verdict: "fail", passed: true, }, ); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-autoresearch-pass-stop", thread_id: "thread-autoresearch-pass-stop", last_assistant_message: 'Autoresearch goal complete; next call update_goal({status: "complete"}).', }, { cwd }, ); assert.equal(result.outputJson?.decision, "block"); assert.match( JSON.stringify(result.outputJson), /get_goal snapshot reconciliation/, ); assert.match( JSON.stringify(result.outputJson), /omx autoresearch-goal complete --slug passing-mission/, ); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks Stop for autoresearch-goal verdict=pass even when passed is omitted", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-autoresearch-verdict-pass-stop-"), ); try { await writeJson( join( cwd, ".omx", "goals", "autoresearch", "verdict-pass-mission", "mission.json", ), { version: 1, workflow: "autoresearch-goal", slug: "verdict-pass-mission", topic: "Passing research", status: "validation_passed", }, ); await writeJson( join( cwd, ".omx", "goals", "autoresearch", "verdict-pass-mission", "completion.json", ), { verdict: "pass", }, ); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-autoresearch-verdict-pass-stop", thread_id: "thread-autoresearch-verdict-pass-stop", last_assistant_message: 'Autoresearch goal complete; next call update_goal({status: "complete"}).', }, { cwd }, ); assert.equal(result.outputJson?.decision, "block"); assert.match( JSON.stringify(result.outputJson), /omx autoresearch-goal complete --slug verdict-pass-mission/, ); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not repeat Stop block when the last autoresearch-goal completion attempt reported objective mismatch", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-autoresearch-mismatch-reported-stop-"), ); try { await writeJson( join( cwd, ".omx", "goals", "autoresearch", "mismatched-mission", "mission.json", ), { version: 1, workflow: "autoresearch-goal", slug: "mismatched-mission", topic: "Passing research bound to another Codex goal", status: "passed", }, ); await writeJson( join( cwd, ".omx", "goals", "autoresearch", "mismatched-mission", "completion.json", ), { verdict: "pass", passed: true, }, ); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-autoresearch-mismatch-reported-stop", thread_id: "thread-autoresearch-mismatch-reported-stop", last_assistant_message: [ "I called get_goal and ran omx autoresearch-goal complete --slug mismatched-mission --codex-goal-json /tmp/snapshot.json.", "The autoresearch-goal completion failed with Codex goal objective mismatch, so I will not repeat the same complete command blindly in this thread.", ].join("\n"), }, { cwd }, ); assert.notEqual(result.outputJson?.decision, "block"); assert.doesNotMatch( JSON.stringify(result.outputJson), /autoresearch-goal complete --slug mismatched-mission/, ); assert.doesNotMatch( JSON.stringify(result.outputJson), /get_goal snapshot reconciliation/, ); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("still blocks later autoresearch-goal completion claims after an objective mismatch if no mismatch is reported in the final answer", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-autoresearch-mismatch-later-retry-stop-"), ); try { await writeJson( join( cwd, ".omx", "goals", "autoresearch", "retryable-mission", "mission.json", ), { version: 1, workflow: "autoresearch-goal", slug: "retryable-mission", topic: "Passing research that can still retry with the correct snapshot", status: "passed", }, ); await writeJson( join( cwd, ".omx", "goals", "autoresearch", "retryable-mission", "completion.json", ), { verdict: "pass", passed: true, }, ); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-autoresearch-mismatch-later-retry-stop", thread_id: "thread-autoresearch-mismatch-later-retry-stop", last_assistant_message: 'Autoresearch goal complete; next call update_goal({status: "complete"}).', }, { cwd }, ); assert.equal(result.outputJson?.decision, "block"); assert.match( JSON.stringify(result.outputJson), /get_goal snapshot reconciliation/, ); assert.match( JSON.stringify(result.outputJson), /omx autoresearch-goal complete --slug retryable-mission/, ); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("treats workflow keywords in native subagent prompt text as literal delegation text", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-subagent-keyword-literal-"), ); try { const stateDir = join(cwd, ".omx", "state"); const canonicalSessionId = "sess-parent"; const leaderNativeSessionId = "native-parent-thread"; const childNativeSessionId = "native-child-thread"; const nowIso = new Date().toISOString(); await writeJson(join(stateDir, "session.json"), { session_id: canonicalSessionId, native_session_id: leaderNativeSessionId, }); await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { [canonicalSessionId]: { session_id: canonicalSessionId, leader_thread_id: leaderNativeSessionId, updated_at: nowIso, threads: { [leaderNativeSessionId]: { thread_id: leaderNativeSessionId, kind: "leader", first_seen_at: nowIso, last_seen_at: nowIso, turn_count: 1, }, [childNativeSessionId]: { thread_id: childNativeSessionId, kind: "subagent", first_seen_at: nowIso, last_seen_at: nowIso, turn_count: 1, mode: "architect", }, }, }, }, }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: childNativeSessionId, thread_id: childNativeSessionId, turn_id: "turn-child-1", prompt: "$ralplan Architect review step. Review the draft plan and return APPROVE or ITERATE.", }, { cwd }, ); assert.equal(result.omxEventName, "keyword-detector"); assert.equal(result.skillState, null); assert.equal(result.outputJson, null); assert.equal( existsSync(join(stateDir, "skill-active-state.json")), false, ); assert.equal( existsSync( join( stateDir, "sessions", canonicalSessionId, "skill-active-state.json", ), ), false, ); assert.equal( existsSync( join(stateDir, "sessions", canonicalSessionId, "ralplan-state.json"), ), false, ); assert.equal( existsSync( join( stateDir, "sessions", childNativeSessionId, "ralplan-state.json", ), ), false, ); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not activate Conductor guidance for typed agent-role prompts without native subagent tracking", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-typed-agent-role-autopilot-"), ); try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "sess-typed-executor", thread_id: "thread-typed-executor", agent_role: "executor", turn_id: "turn-typed-executor", prompt: "$autopilot continue the current implementation lane", }, { cwd }, ); assert.equal(result.omxEventName, "keyword-detector"); assert.equal(result.skillState, null); assert.equal(result.outputJson, null); assert.equal( existsSync( join( cwd, ".omx", "state", "sessions", "sess-typed-executor", "autopilot-state.json", ), ), false, ); assert.equal( existsSync( join( cwd, ".omx", "state", "sessions", "sess-typed-executor", "skill-active-state.json", ), ), false, ); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("treats installed custom native agent roles as typed lanes for prompt submit", async () => { await withIsolatedHome("custom-native-agent-role", async (homeDir) => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-custom-agent-role-autopilot-"), ); try { const agentsDir = join(homeDir, ".codex", "agents"); await mkdir(agentsDir, { recursive: true }); await writeFile( join(agentsDir, "custom-executor.toml"), [ "# oh-my-codex agent: custom-executor", 'name = "custom-executor"', 'description = "Custom executor lane"', ].join("\n"), "utf-8", ); await mkdir(join(cwd, ".omx", "state"), { recursive: true }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "sess-custom-executor", thread_id: "thread-custom-executor", agent_type: "custom-executor", turn_id: "turn-custom-executor", prompt: "$autopilot continue the current implementation lane", }, { cwd }, ); assert.equal(result.omxEventName, "keyword-detector"); assert.equal(result.skillState, null); assert.equal(result.outputJson, null); assert.equal( existsSync( join( cwd, ".omx", "state", "sessions", "sess-custom-executor", "autopilot-state.json", ), ), false, ); assert.equal( existsSync( join( cwd, ".omx", "state", "sessions", "sess-custom-executor", "skill-active-state.json", ), ), false, ); } finally { await rm(cwd, { recursive: true, force: true }); } }); }); it("does not inject Conductor guidance into typed agent-role autopilot continuations", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-typed-agent-role-active-autopilot-"), ); try { const sessionId = "sess-typed-executor-active"; const sessionDir = join(cwd, ".omx", "state", "sessions", sessionId); await mkdir(sessionDir, { recursive: true }); await writeJson(join(sessionDir, "skill-active-state.json"), { version: 1, active: true, skill: "autopilot", keyword: "$autopilot", phase: "planning", initialized_mode: "autopilot", initialized_state_path: `.omx/state/sessions/${sessionId}/autopilot-state.json`, session_id: sessionId, active_skills: [ { skill: "autopilot", phase: "planning", active: true, session_id: sessionId, }, ], }); await writeJson(join(sessionDir, "autopilot-state.json"), { active: true, mode: "autopilot", current_phase: "execution", started_at: "2026-04-19T00:00:00.000Z", updated_at: "2026-04-19T00:10:00.000Z", session_id: sessionId, }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: sessionId, thread_id: "thread-typed-executor-active", turn_id: "turn-typed-executor-active", agent_role: "executor", prompt: "keep going now", }, { cwd }, ); const message = String( ( result.outputJson as { hookSpecificOutput?: { additionalContext?: string }; } )?.hookSpecificOutput?.additionalContext || "", ); assert.equal(result.omxEventName, "keyword-detector"); assert.doesNotMatch(message, /Conductor mode contract:/); assert.doesNotMatch( message, /Golden Rule: When the Main agent is acting in Conductor mode/, ); assert.doesNotMatch(message, /Conductor reuse and ledger guidance:/); assert.doesNotMatch(message, /typed subagents never receive this block/); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not treat a corrupt leader kind=subagent tracker entry as native subagent prompt scope", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-corrupt-leader-subagent-"), ); try { const stateDir = join(cwd, ".omx", "state"); const canonicalSessionId = "sess-corrupt-leader"; const leaderNativeSessionId = "native-corrupt-leader"; const nowIso = new Date().toISOString(); await writeJson(join(stateDir, "session.json"), { session_id: canonicalSessionId, native_session_id: leaderNativeSessionId, }); await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { [canonicalSessionId]: { session_id: canonicalSessionId, leader_thread_id: leaderNativeSessionId, updated_at: nowIso, threads: { [leaderNativeSessionId]: { thread_id: leaderNativeSessionId, kind: "subagent", first_seen_at: nowIso, last_seen_at: nowIso, turn_count: 2, }, }, }, }, }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: leaderNativeSessionId, thread_id: leaderNativeSessionId, turn_id: "turn-corrupt-leader", prompt: "$autopilot continue this review blocker fix", }, { cwd }, ); assert.equal(result.omxEventName, "keyword-detector"); assert.equal(result.skillState?.skill, "autopilot"); assert.equal( existsSync( join( stateDir, "sessions", canonicalSessionId, "autopilot-state.json", ), ), true, ); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("lets the current canonical leader boundary beat stale global subagent tracking with a distinct prompt thread id", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-current-leader-stale-global-"), ); try { const stateDir = join(cwd, ".omx", "state"); const canonicalSessionId = "sess-current-leader"; const leaderNativeSessionId = "native-current-leader"; const staleSessionId = "sess-stale-subagent"; const staleLeaderNativeSessionId = "native-stale-leader"; const nowIso = new Date().toISOString(); await writeJson(join(stateDir, "session.json"), { session_id: canonicalSessionId, native_session_id: leaderNativeSessionId, }); await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { [canonicalSessionId]: { session_id: canonicalSessionId, leader_thread_id: leaderNativeSessionId, updated_at: nowIso, threads: { [leaderNativeSessionId]: { thread_id: leaderNativeSessionId, kind: "leader", first_seen_at: nowIso, last_seen_at: nowIso, turn_count: 1, }, }, }, [staleSessionId]: { session_id: staleSessionId, leader_thread_id: staleLeaderNativeSessionId, updated_at: nowIso, threads: { [staleLeaderNativeSessionId]: { thread_id: staleLeaderNativeSessionId, kind: "leader", first_seen_at: nowIso, last_seen_at: nowIso, turn_count: 1, }, [leaderNativeSessionId]: { thread_id: leaderNativeSessionId, kind: "subagent", first_seen_at: nowIso, last_seen_at: nowIso, turn_count: 1, mode: "architect", }, }, }, }, }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: leaderNativeSessionId, thread_id: "thread-current-turn-not-native-session", turn_id: "turn-current-leader", prompt: "$autopilot continue", }, { cwd }, ); assert.equal(result.omxEventName, "keyword-detector"); assert.equal(result.skillState?.skill, "autopilot"); assert.equal( existsSync( join( stateDir, "sessions", canonicalSessionId, "autopilot-state.json", ), ), true, ); assert.equal( existsSync( join(stateDir, "sessions", staleSessionId, "autopilot-state.json"), ), false, ); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("lets the current session native leader beat stale global subagent tracking without a canonical summary and with a distinct prompt thread id", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-current-native-leader-stale-global-"), ); try { const stateDir = join(cwd, ".omx", "state"); const canonicalSessionId = "sess-current-native-leader"; const leaderNativeSessionId = "native-current-leader-no-summary"; const staleSessionId = "sess-stale-native-subagent"; const staleLeaderNativeSessionId = "native-stale-parent"; const nowIso = new Date().toISOString(); await writeJson(join(stateDir, "session.json"), { session_id: canonicalSessionId, native_session_id: leaderNativeSessionId, }); await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { [staleSessionId]: { session_id: staleSessionId, leader_thread_id: staleLeaderNativeSessionId, updated_at: nowIso, threads: { [staleLeaderNativeSessionId]: { thread_id: staleLeaderNativeSessionId, kind: "leader", first_seen_at: nowIso, last_seen_at: nowIso, turn_count: 1, }, [leaderNativeSessionId]: { thread_id: leaderNativeSessionId, kind: "subagent", first_seen_at: nowIso, last_seen_at: nowIso, turn_count: 1, mode: "critic", }, }, }, }, }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: leaderNativeSessionId, thread_id: "thread-current-turn-not-native-session", turn_id: "turn-current-native-leader", prompt: "$autopilot continue", }, { cwd }, ); assert.equal(result.omxEventName, "keyword-detector"); assert.equal(result.skillState?.skill, "autopilot"); assert.equal( existsSync( join( stateDir, "sessions", canonicalSessionId, "autopilot-state.json", ), ), true, ); assert.equal( existsSync( join(stateDir, "sessions", staleSessionId, "autopilot-state.json"), ), false, ); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("lets the current session native leader beat a malformed canonical subagent entry", async () => { const cwd = await mkdtemp( join( tmpdir(), "omx-native-hook-current-native-leader-malformed-canonical-", ), ); try { const stateDir = join(cwd, ".omx", "state"); const canonicalSessionId = "sess-current-native-leader-malformed"; const leaderNativeSessionId = "native-current-leader-malformed"; const nowIso = new Date().toISOString(); await writeJson(join(stateDir, "session.json"), { session_id: canonicalSessionId, native_session_id: leaderNativeSessionId, }); await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { [canonicalSessionId]: { session_id: canonicalSessionId, updated_at: nowIso, threads: { [leaderNativeSessionId]: { thread_id: leaderNativeSessionId, kind: "subagent", first_seen_at: nowIso, last_seen_at: nowIso, turn_count: 1, mode: "architect", }, }, }, }, }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: leaderNativeSessionId, thread_id: leaderNativeSessionId, turn_id: "turn-current-native-leader-malformed", prompt: "$autopilot continue", }, { cwd }, ); assert.equal(result.omxEventName, "keyword-detector"); assert.equal(result.skillState?.skill, "autopilot"); assert.equal( existsSync( join( stateDir, "sessions", canonicalSessionId, "autopilot-state.json", ), ), true, ); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("still treats mixed child and leader payload identities as native subagent scope", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-mixed-child-leader-identity-"), ); try { const stateDir = join(cwd, ".omx", "state"); const canonicalSessionId = "sess-mixed-child-leader"; const leaderNativeSessionId = "native-mixed-leader"; const childNativeSessionId = "native-mixed-child"; const nowIso = new Date().toISOString(); await writeJson(join(stateDir, "session.json"), { session_id: canonicalSessionId, native_session_id: leaderNativeSessionId, }); await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { [canonicalSessionId]: { session_id: canonicalSessionId, leader_thread_id: leaderNativeSessionId, updated_at: nowIso, threads: { [leaderNativeSessionId]: { thread_id: leaderNativeSessionId, kind: "leader", first_seen_at: nowIso, last_seen_at: nowIso, turn_count: 1, }, [childNativeSessionId]: { thread_id: childNativeSessionId, kind: "subagent", first_seen_at: nowIso, last_seen_at: nowIso, turn_count: 1, mode: "critic", }, }, }, }, }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: childNativeSessionId, thread_id: leaderNativeSessionId, turn_id: "turn-mixed-child-leader", prompt: "$ralplan review this as delegated text", }, { cwd }, ); assert.equal(result.omxEventName, "keyword-detector"); assert.equal(result.skillState, null); assert.equal(result.outputJson, null); assert.equal( existsSync( join(stateDir, "sessions", canonicalSessionId, "ralplan-state.json"), ), false, ); assert.equal( existsSync( join( stateDir, "sessions", childNativeSessionId, "ralplan-state.json", ), ), false, ); const reversedResult = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: leaderNativeSessionId, thread_id: childNativeSessionId, turn_id: "turn-mixed-leader-child", prompt: "$autopilot review this as delegated text", }, { cwd }, ); assert.equal(reversedResult.omxEventName, "keyword-detector"); assert.equal(reversedResult.skillState, null); assert.equal(reversedResult.outputJson, null); assert.equal( existsSync( join( stateDir, "sessions", canonicalSessionId, "autopilot-state.json", ), ), false, ); assert.equal( existsSync( join( stateDir, "sessions", childNativeSessionId, "autopilot-state.json", ), ), false, ); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("records plugin-prefixed keyword activation from UserPromptSubmit payloads", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-plugin-prefixed-"), ); try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "sess-plugin-1", thread_id: "thread-plugin-1", turn_id: "turn-plugin-1", prompt: "$oh-my-codex:ralplan implement issue #1307", }, { cwd }, ); assert.equal(result.omxEventName, "keyword-detector"); assert.equal(result.skillState?.skill, "ralplan"); const message = String( ( result.outputJson as { hookSpecificOutput?: { additionalContext?: string }; } )?.hookSpecificOutput?.additionalContext || "", ); assert.match(message, /\$oh-my-codex:ralplan" -> ralplan/); assert.match( message, /use CLI-first state updates via `omx state write\/read\/clear --input '' --json`/, ); assert.equal( existsSync( join( cwd, ".omx", "state", "sessions", "sess-plugin-1", "ralplan-state.json", ), ), true, ); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("denies fresh autopilot activation at host-receipt preflight before protocol guidance", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-autopilot-ralplan-gate-"), ); try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "sess-autopilot-ralplan-gate", thread_id: "thread-autopilot-ralplan-gate", turn_id: "turn-autopilot-ralplan-gate", prompt: "$autopilot implement issue #2430", }, { cwd }, ); assert.equal(result.omxEventName, "keyword-detector"); assert.equal(result.skillState?.skill, "autopilot"); // #3463: autopilot starts normally assert.notEqual(result.skillState?.error, "documented_host_consensus_receipt_unavailable"); const message = String( ( result.outputJson as { hookSpecificOutput?: { additionalContext?: string }; } )?.hookSpecificOutput?.additionalContext || "", ); // #3463: autopilot starts normally; the preflight no longer blocks. assert.doesNotMatch(message, /documented_host_consensus_receipt_unavailable/); const sessionDir = join(cwd, ".omx", "state", "sessions", "sess-autopilot-ralplan-gate"); const autopilotState = JSON.parse( await readFile(join(sessionDir, "autopilot-state.json"), "utf-8"), ) as { active?: boolean; current_phase?: string; error?: string }; // #3463: autopilot starts normally assert.equal(autopilotState.active, true); // #3463: phase is no longer "failed" assert.notEqual(autopilotState.current_phase, "failed"); // #3463: no host-receipt error assert.notEqual(autopilotState.error, "documented_host_consensus_receipt_unavailable"); // #3463: deep-interview state may now exist // #3463: ultragoal state not created yet } finally { await rm(cwd, { recursive: true, force: true }); } }); it("denies fresh autopilot activation after capacity-only native subagent evidence", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-autopilot-capacity-native-"), ); try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); await dispatchCodexNativeHook( { hook_event_name: "PostToolUse", cwd, session_id: "sess-autopilot-capacity-native", thread_id: "thread-autopilot-capacity-native", turn_id: "turn-autopilot-capacity-native-spawn", tool_name: "multi_agent_v1.spawn_agent", tool_response: { error: "collab spawn failed: agent thread limit reached", }, }, { cwd }, ); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "sess-autopilot-capacity-native", thread_id: "thread-autopilot-capacity-native", turn_id: "turn-autopilot-capacity-native-prompt", prompt: "$autopilot implement issue #3078", }, { cwd }, ); assert.equal(result.omxEventName, "keyword-detector"); assert.equal(result.skillState?.skill, "autopilot"); // #3463: autopilot starts normally assert.notEqual(result.skillState?.error, "documented_host_consensus_receipt_unavailable"); const message = String( ( result.outputJson as { hookSpecificOutput?: { additionalContext?: string }; } )?.hookSpecificOutput?.additionalContext || "", ); // #3463: autopilot starts normally; the preflight no longer blocks. assert.doesNotMatch(message, /documented_host_consensus_receipt_unavailable/); const sessionDir = join(cwd, ".omx", "state", "sessions", "sess-autopilot-capacity-native"); // #3463: deep-interview state may now exist // #3463: ultragoal state not created yet } finally { await rm(cwd, { recursive: true, force: true }); } }); it("denies unsupported fresh autopilot activation at host-receipt preflight", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-autopilot-unsupported-native-"), ); try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "sess-autopilot-unsupported-native", thread_id: "thread-autopilot-unsupported-native", turn_id: "turn-autopilot-unsupported-native", prompt: "$autopilot implement issue #3078", capabilities: { native_subagents: false, multi_agent_v1: false }, }, { cwd }, ); assert.equal(result.skillState?.skill, "autopilot"); // #3463: autopilot starts normally assert.notEqual(result.skillState?.error, "documented_host_consensus_receipt_unavailable"); const message = String( ( result.outputJson as { hookSpecificOutput?: { additionalContext?: string }; } )?.hookSpecificOutput?.additionalContext || "", ); // #3463: autopilot starts normally; the preflight no longer blocks. assert.doesNotMatch(message, /documented_host_consensus_receipt_unavailable/); const sessionDir = join(cwd, ".omx", "state", "sessions", "sess-autopilot-unsupported-native"); // #3463: deep-interview state may now exist // #3463: ultragoal state not created yet } finally { await rm(cwd, { recursive: true, force: true }); } }); it("records ultragoal prompt skill activation with goal-tool handoff guidance", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ultragoal-")); try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "sess-ultragoal-1", thread_id: "thread-ultragoal-1", turn_id: "turn-ultragoal-1", prompt: "$ultragoal split this launch into durable goals", }, { cwd }, ); assert.equal(result.omxEventName, "keyword-detector"); assert.equal(result.skillState?.skill, "ultragoal"); assert.equal(result.skillState?.initialized_mode, "ultragoal"); assert.equal( result.skillState?.initialized_state_path, ".omx/state/sessions/sess-ultragoal-1/ultragoal-state.json", ); const message = String( ( result.outputJson as { hookSpecificOutput?: { additionalContext?: string }; } )?.hookSpecificOutput?.additionalContext || "", ); assert.match(message, /"\$ultragoal" -> ultragoal/); assert.match(message, /Ultragoal protocol:/); assert.match(message, /get_goal/); assert.match(message, /create_goal/); assert.match(message, /update_goal/); assert.match(message, /does not call `\/goal clear`/); assert.match(message, /multiple sequential ultragoal runs/); assert.equal( existsSync( join( cwd, ".omx", "state", "sessions", "sess-ultragoal-1", "ultragoal-state.json", ), ), true, ); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("keeps deep-interview active when ultragoal bypasses required planning handoff (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("applies only explicit structured UserPromptSubmit ultragoal steering directives", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-ultragoal-steer-"), ); try { await createUltragoalPlan(cwd, { brief: "G002-cli-and-prompt-submit-bridge .omx/ultragoal hook steering fixture", goals: [ { title: "First", objective: "Complete first milestone with tests." }, ], }); const prose = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "sess-ultragoal-steer-1", prompt: "Please add a subgoal for docs later; this is normal prose, not a directive.", }, { cwd }, ); assert.equal(prose.outputJson, null); assert.equal((await readUltragoalPlan(cwd)).goals.length, 1); const jsonExample = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "sess-ultragoal-steer-1", prompt: `Here is an inert example:\n\`\`\`json\n${JSON.stringify({ kind: "add_subgoal", source: "user_prompt_submit", evidence: "Example JSON should not mutate .omx/ultragoal.", rationale: "Only explicit steering fences or labels are executable.", title: "Inert JSON example", objective: "This example must not be added.", })}\n\`\`\``, }, { cwd }, ); assert.equal(jsonExample.outputJson, null); assert.equal((await readUltragoalPlan(cwd)).goals.length, 1); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "sess-ultragoal-steer-1", prompt: `OMX_ULTRAGOAL_STEER: ${JSON.stringify({ kind: "add_subgoal", source: "user_prompt_submit", evidence: "Prompt-submit supplied a structured .omx/ultragoal directive for G002-cli-and-prompt-submit-bridge.", rationale: "Add bounded hook regression work while preserving all completion gates.", title: "Prompt bridge regression", objective: "Verify UserPromptSubmit bounded steering bridge with tests.", })}`, }, { cwd }, ); const message = String( ( result.outputJson as { hookSpecificOutput?: { additionalContext?: string }; } )?.hookSpecificOutput?.additionalContext || "", ); assert.match(message, /bounded \.omx\/ultragoal steering/); assert.match(message, /G002-cli-and-prompt-submit-bridge/); assert.match(message, /accepted/); const plan = await readUltragoalPlan(cwd); assert.equal(plan.goals.length, 2); assert.equal(plan.goals[1]?.title, "Prompt bridge regression"); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not apply UserPromptSubmit ultragoal steering from native subagent prompts", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-ultragoal-steer-subagent-"), ); try { await createUltragoalPlan(cwd, { brief: "G002-cli-and-prompt-submit-bridge .omx/ultragoal subagent steering fixture", goals: [ { title: "First", objective: "Complete first milestone with tests." }, ], }); const stateDir = join(cwd, ".omx", "state"); const canonicalSessionId = "sess-ultragoal-parent"; const leaderNativeSessionId = "native-ultragoal-parent"; const childNativeSessionId = "native-ultragoal-child"; const nowIso = new Date().toISOString(); await writeJson(join(stateDir, "session.json"), { session_id: canonicalSessionId, native_session_id: leaderNativeSessionId, }); await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { [canonicalSessionId]: { session_id: canonicalSessionId, leader_thread_id: leaderNativeSessionId, updated_at: nowIso, threads: { [leaderNativeSessionId]: { thread_id: leaderNativeSessionId, kind: "leader", first_seen_at: nowIso, last_seen_at: nowIso, turn_count: 1, }, [childNativeSessionId]: { thread_id: childNativeSessionId, kind: "subagent", first_seen_at: nowIso, last_seen_at: nowIso, turn_count: 1, mode: "architect", }, }, }, }, }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: childNativeSessionId, thread_id: childNativeSessionId, turn_id: "turn-ultragoal-child-1", prompt: `OMX_ULTRAGOAL_STEER: ${JSON.stringify({ kind: "add_subgoal", source: "user_prompt_submit", evidence: "Subagent prompt text must be literal delegated context.", rationale: "Subagent prompts should not mutate the parent .omx/ultragoal ledger.", title: "Subagent should not add this", objective: "This must remain literal prompt text.", })}`, }, { cwd }, ); assert.equal(result.outputJson, null); const plan = await readUltragoalPlan(cwd); assert.equal(plan.goals.length, 1); const ledger = await readFile( join(cwd, ".omx/ultragoal/ledger.jsonl"), "utf-8", ); assert.equal( (ledger.match(/"event":"steering_accepted"/g) ?? []).length, 0, ); assert.equal( (ledger.match(/"event":"steering_rejected"/g) ?? []).length, 0, ); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("dedupes repeated UserPromptSubmit ultragoal steering directives by prompt signature", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-ultragoal-steer-dedupe-"), ); try { await createUltragoalPlan(cwd, { brief: "G002-cli-and-prompt-submit-bridge .omx/ultragoal dedupe fixture", goals: [ { title: "First", objective: "Complete first milestone with tests." }, ], }); const prompt = `\`\`\`omx-ultragoal-steer ${JSON.stringify({ kind: "add_subgoal", source: "user_prompt_submit", evidence: "Structured prompt-submit directive adds exactly one deduped goal.", rationale: "Use idempotent bridge semantics for repeated hook delivery.", title: "Deduped bridge regression", objective: "Verify repeated UserPromptSubmit steering does not duplicate goals.", })} \`\`\``; await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "sess-dedupe", prompt, }, { cwd }, ); const second = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "sess-dedupe", prompt, }, { cwd }, ); const message = String( ( second.outputJson as { hookSpecificOutput?: { additionalContext?: string }; } )?.hookSpecificOutput?.additionalContext || "", ); assert.match(message, /deduped/); const plan = await readUltragoalPlan(cwd); assert.equal( plan.goals.filter((goal) => goal.title === "Deduped bridge regression") .length, 1, ); const ledger = await readFile( join(cwd, ".omx/ultragoal/ledger.jsonl"), "utf-8", ); assert.equal( (ledger.match(/"event":"steering_accepted"/g) ?? []).length, 1, ); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not activate ultrawork from its retired token or Korean typo shorthand", async () => { // ultrawork is a sunset skill with no trigger, and the Korean IME alias existed only to reach its // `ulw` shorthand, so neither form may seed a projection through UserPromptSubmit. for (const [label, prompt] of [["explicit", "$ultrawork fan out the regression checks"], ["korean-typo", "\u3155\u3163\u3148\ub85c \ubcc4\ub82c \ucc98\ub9ac\ud574\uc918"]]) { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ultrawork-retired-")); try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "sess-uw-retired", thread_id: "thread-uw-retired", turn_id: "turn-uw-retired", prompt }, { cwd }, ); assert.notEqual(result.skillState?.active, true, `${label} must not activate ultrawork`); assert.equal(existsSync(join(cwd, ".omx", "state", "sessions", "sess-uw-retired", "ultrawork-state.json")), false, `${label} must not seed an ultrawork projection`); } finally { await rm(cwd, { recursive: true, force: true }); } } }); it("does not activate Ralph workflow state from a plain conversational mention", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-ralph-plain-text-"), ); try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "sess-ralph-plain-text", thread_id: "thread-ralph-plain-text", turn_id: "turn-ralph-plain-text", prompt: "why does ralph keep blocking stop?", }, { cwd }, ); assert.equal(result.omxEventName, "keyword-detector"); assert.equal(result.skillState, null); // Triage may inject advisory LIGHT/explore context for the question-shaped // prompt, but the invariant this test guards is that no Ralph workflow state // is seeded and no Ralph-activation message is emitted. const advisoryContext = String( ( result.outputJson as { hookSpecificOutput?: { additionalContext?: string }; } )?.hookSpecificOutput?.additionalContext || "", ); assert.doesNotMatch(advisoryContext, /skill:\s*ralph/i); assert.doesNotMatch(advisoryContext, /ralph-state\.json/i); assert.equal( existsSync(join(cwd, ".omx", "state", "skill-active-state.json")), false, ); assert.equal( existsSync( join( cwd, ".omx", "state", "sessions", "sess-ralph-plain-text", "skill-active-state.json", ), ), false, ); assert.equal( existsSync( join( cwd, ".omx", "state", "sessions", "sess-ralph-plain-text", "ralph-state.json", ), ), false, ); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not inject generic execution handoff coaching for non-keyword prompts", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-execution-handoff-"), ); try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); const prompts = [ "按照这个plan开始执行优化", "开始执行", "继续优化", "直接修复", ]; for (const [index, prompt] of prompts.entries()) { const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: `sess-exec-handoff-${index}`, thread_id: `thread-exec-handoff-${index}`, turn_id: `turn-exec-handoff-${index}`, prompt, }, { cwd }, ); const message = String( ( result.outputJson as { hookSpecificOutput?: { additionalContext?: string }; } )?.hookSpecificOutput?.additionalContext || "", ); assert.doesNotMatch(message, /Newest user input is an execution handoff/i, prompt); assert.doesNotMatch(message, /Do not restate the prior plan/i, prompt); } } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not inject redundant priority coaching for short follow-up prompts", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-followup-priority-"), ); try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "sess-followup-priority", thread_id: "thread-followup-priority", turn_id: "turn-followup-priority", prompt: "这些优化都做了么", }, { cwd }, ); const message = String( ( result.outputJson as { hookSpecificOutput?: { additionalContext?: string }; } )?.hookSpecificOutput?.additionalContext || "", ); assert.doesNotMatch(message, /Newest user input is a same-thread follow-up/i); assert.doesNotMatch(message, /prefer it over older unresolved prompts/i); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not activate Ralph from a prompt-side $ralph token, leaving the CLI path untouched", async () => { // These two cases pinned that prompt-side `$ralph` seeded Ralph workflow state while only // `omx ralph --prd` used the PRD-gated CLI startup. `$ralph` is a sunset skill token now, so the // prompt side seeds nothing; the durable half of their subject - the CLI is a separate, live // surface - is what this asserts. for (const prompt of ["$ralph continue verification", "$oh-my-codex:ralph continue verification"]) { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ralph-routing-")); try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "sess-ralph-msg", thread_id: "thread-ralph-msg", turn_id: "turn-ralph-msg", prompt }, { cwd }, ); // `skill` still names the REJECTED token as diagnostic metadata, so assert non-activation. assert.notEqual(result.skillState?.active, true, `${prompt} must not activate Ralph`); assert.equal(existsSync(join(cwd, ".omx", "state", "sessions", "sess-ralph-msg", "ralph-state.json")), false, `${prompt} must not seed a Ralph projection`); } finally { await rm(cwd, { recursive: true, force: true }); } } }); it("keeps bare keep-going continuation on the active autopilot skill instead of denying with generic ralph overlap", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-autopilot-bare-continuation-"), ); try { const sessionId = "sess-autopilot-cont"; const sessionDir = join(cwd, ".omx", "state", "sessions", sessionId); await mkdir(sessionDir, { recursive: true }); await writeJson(join(sessionDir, "skill-active-state.json"), { version: 1, active: true, skill: "autopilot", keyword: "$autopilot", phase: "planning", session_id: sessionId, active_skills: [ { skill: "autopilot", phase: "planning", active: true, session_id: sessionId, }, ], }); await writeJson(join(sessionDir, "autopilot-state.json"), { active: true, mode: "autopilot", current_phase: "execution", started_at: "2026-04-19T00:00:00.000Z", updated_at: "2026-04-19T00:10:00.000Z", session_id: sessionId, }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: sessionId, thread_id: "thread-autopilot-cont", turn_id: "turn-autopilot-cont", prompt: "\ keep going now", }, { cwd }, ); assert.equal(result.omxEventName, "keyword-detector"); assert.equal(result.skillState?.skill, "autopilot"); const message = String( ( result.outputJson as { hookSpecificOutput?: { additionalContext?: string }; } )?.hookSpecificOutput?.additionalContext || "", ); // "keep going" is a retired trigger, so the guidance must not claim the mapping while the // continuation still stays on the active skill. assert.doesNotMatch(message, /"keep going" -> ralph/); assert.match(message, /Autopilot protocol:/); assert.match( message, /structured question chain, not a one-question gate/, ); assert.match(message, /re-score ambiguity against the active threshold/); assert.match(message, /max_rounds as a cap/); assert.match( message, /Do not advance from deep-interview to ralplan merely because the first question was answered/, ); assert.doesNotMatch(message, /denied workflow keyword/i); assert.doesNotMatch( message, /Unsupported workflow overlap: autopilot \+ ralph\./, ); assert.doesNotMatch(message, /Prompt-side `\$ralph` activation/); assert.equal(existsSync(join(sessionDir, "ralph-state.json")), false); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("keeps omx question answers on the active autopilot skill so the interview chain guidance is injected", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-autopilot-question-answer-continuation-"), ); try { const sessionId = "sess-autopilot-question-answer"; const sessionDir = join(cwd, ".omx", "state", "sessions", sessionId); await mkdir(sessionDir, { recursive: true }); await writeJson(join(sessionDir, "skill-active-state.json"), { version: 1, active: true, skill: "autopilot", keyword: "$autopilot", phase: "deep-interview", initialized_mode: "autopilot", initialized_state_path: `.omx/state/sessions/${sessionId}/autopilot-state.json`, session_id: sessionId, active_skills: [ { skill: "autopilot", phase: "deep-interview", active: true, session_id: sessionId, }, ], }); await writeJson(join(sessionDir, "autopilot-state.json"), { active: true, mode: "autopilot", current_phase: "deep-interview", started_at: "2026-04-19T00:00:00.000Z", updated_at: "2026-04-19T00:10:00.000Z", session_id: sessionId, }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: sessionId, thread_id: "thread-autopilot-question-answer", turn_id: "turn-autopilot-question-answer", prompt: "[omx question answered] semantic_marker_expansion $ralplan", }, { cwd }, ); assert.equal(result.omxEventName, "keyword-detector"); assert.equal(result.skillState?.skill, "autopilot"); const message = String( ( result.outputJson as { hookSpecificOutput?: { additionalContext?: string }; } )?.hookSpecificOutput?.additionalContext || "", ); assert.match(message, /continued active workflow skill "autopilot"/); assert.match(message, /Autopilot protocol:/); assert.match( message, /structured question chain, not a one-question gate/, ); assert.match(message, /This turn is a marked omx question answer/); assert.match(message, /then re-score/); assert.match(message, /write interview_complete evidence and hand off/); assert.match( message, /readiness gate remains unresolved and the answer would materially change execution/, ); assert.match( message, /Do not advance from deep-interview to ralplan merely because the first question was answered/, ); assert.doesNotMatch(message, /denied workflow keyword/i); assert.equal(existsSync(join(sessionDir, "ralplan-state.json")), false); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("keeps deep-interview bridge guidance on marked question answers with workflow-like tokens", async () => { const cwd = await mkdtemp( join( tmpdir(), "omx-native-hook-deep-interview-question-answer-continuation-", ), ); try { const sessionId = "sess-deep-interview-question-answer"; const sessionDir = join(cwd, ".omx", "state", "sessions", sessionId); await mkdir(sessionDir, { recursive: true }); await writeJson(join(sessionDir, "skill-active-state.json"), { version: 1, active: true, skill: "deep-interview", keyword: "$deep-interview", phase: "planning", initialized_mode: "deep-interview", initialized_state_path: `.omx/state/sessions/${sessionId}/deep-interview-state.json`, session_id: sessionId, active_skills: [ { skill: "deep-interview", phase: "planning", active: true, session_id: sessionId, }, ], }); await writeJson(join(sessionDir, "deep-interview-state.json"), { active: true, mode: "deep-interview", current_phase: "intent-first", started_at: "2026-04-21T10:00:00.000Z", updated_at: "2026-04-21T10:00:00.000Z", }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: sessionId, thread_id: "thread-deep-interview-question-answer", turn_id: "turn-deep-interview-question-answer", prompt: "[omx question answered] answer text $ralplan", }, { cwd }, ); assert.equal(result.omxEventName, "keyword-detector"); assert.equal(result.skillState?.skill, "deep-interview"); const message = String( ( result.outputJson as { hookSpecificOutput?: { additionalContext?: string }; } )?.hookSpecificOutput?.additionalContext || "", ); assert.match(message, /continued active workflow skill "deep-interview"/); assert.match( message, /workflow-like tokens inside the marked omx question answer are treated as answer text/, ); assert.match( message, /Deep-interview is active, but this session is not attached to tmux/, ); assert.match(message, /native structured question tool when available/); assert.doesNotMatch( message, /detected workflow keyword "\$ralplan" -> ralplan/, ); assert.equal(existsSync(join(sessionDir, "ralplan-state.json")), false); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("clarifies outside-tmux prompt-side deep-interview activation without pretending omx question is directly available", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-deep-interview-routing-"), ); try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "sess-deep-interview-msg", thread_id: "thread-deep-interview-msg", turn_id: "turn-deep-interview-msg", prompt: "$deep-interview gather requirements", }, { cwd }, ); assert.equal(result.omxEventName, "keyword-detector"); assert.equal(result.skillState?.skill, "deep-interview"); const message = String( ( result.outputJson as { hookSpecificOutput?: { additionalContext?: string }; } )?.hookSpecificOutput?.additionalContext || "", ); assert.match(message, /\$deep-interview" -> deep-interview/); assert.match( message, /use CLI-first state updates via `omx state write\/read\/clear --input '' --json`/, ); assert.match( message, /Deep-interview is active, but this session is not attached to tmux/, ); assert.match( message, /Do not invoke `omx question`, `omx hud`, or `omx team`/, ); assert.match(message, /native structured question tool when available/); assert.match(message, /ask exactly one concise plain-text question/); assert.match( message, /no tmux question obligation should be created outside tmux/, ); assert.doesNotMatch(message, /OMX_QUESTION_RETURN_PANE=/); assert.doesNotMatch(message, /preserve the leader pane/i); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("uses native fallback deep-interview guidance on Windows outside tmux", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-deep-interview-routing-win32-"), ); const originalPlatform = Object.getOwnPropertyDescriptor( process, "platform", ); try { Object.defineProperty(process, "platform", { value: "win32", configurable: true, }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "sess-deep-interview-msg-win32", thread_id: "thread-deep-interview-msg-win32", turn_id: "turn-deep-interview-msg-win32", prompt: "$deep-interview gather requirements", }, { cwd }, ); assert.equal(result.omxEventName, "keyword-detector"); assert.equal(result.skillState?.skill, "deep-interview"); const message = String( ( result.outputJson as { hookSpecificOutput?: { additionalContext?: string }; } )?.hookSpecificOutput?.additionalContext || "", ); assert.match( message, /Deep-interview is active, but this session is not attached to tmux/, ); assert.match(message, /native structured question tool when available/); assert.doesNotMatch(message, /OMX_QUESTION_RETURN_PANE=/); assert.doesNotMatch(message, /current-session CLI bridge command/); } finally { if (originalPlatform) Object.defineProperty(process, "platform", originalPlatform); await rm(cwd, { recursive: true, force: true }); } }); it("includes leader-pane preservation guidance when a pane hint is available", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-deep-interview-pane-hint-"), ); try { const sessionId = "sess-deep-interview-pane-hint"; const sessionDir = join(cwd, ".omx", "state", "sessions", sessionId); await mkdir(sessionDir, { recursive: true }); await writeJson(join(sessionDir, "deep-interview-state.json"), { active: true, mode: "deep-interview", current_phase: "intent-first", started_at: "2026-04-21T10:00:00.000Z", updated_at: "2026-04-21T10:00:00.000Z", tmux_pane_id: "%77", }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: sessionId, thread_id: "thread-deep-interview-pane-hint", turn_id: "turn-deep-interview-pane-hint", prompt: "$deep-interview gather requirements", }, { cwd }, ); assert.equal(result.omxEventName, "keyword-detector"); assert.equal(result.skillState?.skill, "deep-interview"); const message = String( ( result.outputJson as { hookSpecificOutput?: { additionalContext?: string }; } )?.hookSpecificOutput?.additionalContext || "", ); assert.match(message, /not attached to tmux/); assert.match(message, /native structured question tool when available/); assert.match(message, /tmux return bridge \(%77\) is recorded/); assert.doesNotMatch(message, /current-session CLI bridge command/); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("uses native fallback guidance on Windows when a pane hint is available", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-deep-interview-pane-hint-win32-"), ); const originalPlatform = Object.getOwnPropertyDescriptor( process, "platform", ); try { Object.defineProperty(process, "platform", { value: "win32", configurable: true, }); const sessionId = "sess-deep-interview-pane-hint-win32"; const sessionDir = join(cwd, ".omx", "state", "sessions", sessionId); await mkdir(sessionDir, { recursive: true }); await writeJson(join(sessionDir, "deep-interview-state.json"), { active: true, mode: "deep-interview", current_phase: "intent-first", started_at: "2026-04-21T10:00:00.000Z", updated_at: "2026-04-21T10:00:00.000Z", tmux_pane_id: "%77", }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: sessionId, thread_id: "thread-deep-interview-pane-hint-win32", turn_id: "turn-deep-interview-pane-hint-win32", prompt: "$deep-interview gather requirements", }, { cwd }, ); assert.equal(result.omxEventName, "keyword-detector"); assert.equal(result.skillState?.skill, "deep-interview"); const message = String( ( result.outputJson as { hookSpecificOutput?: { additionalContext?: string }; } )?.hookSpecificOutput?.additionalContext || "", ); assert.match(message, /not attached to tmux/); assert.match(message, /native structured question tool when available/); assert.match(message, /tmux return bridge \(%77\) is recorded/); assert.doesNotMatch(message, /OMX_QUESTION_RETURN_PANE=/); assert.doesNotMatch(message, /PowerShell\/background-terminal/); } finally { if (originalPlatform) Object.defineProperty(process, "platform", originalPlatform); await rm(cwd, { recursive: true, force: true }); } }); it("keeps bare keep-going continuation on the active ralph skill without resetting through generic keep-going routing", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-ralph-bare-continuation-"), ); try { const sessionId = "sess-ralph-cont"; const sessionDir = join(cwd, ".omx", "state", "sessions", sessionId); await mkdir(sessionDir, { recursive: true }); await writeJson(join(sessionDir, "skill-active-state.json"), { version: 1, active: true, skill: "ultragoal", keyword: "$ultragoal", phase: "executing", session_id: sessionId, active_skills: [ { skill: "ultragoal", phase: "executing", active: true, session_id: sessionId, }, ], }); await writeJson(join(sessionDir, "ultragoal-state.json"), { active: true, mode: "ultragoal", current_phase: "verifying", started_at: "2026-04-19T00:00:00.000Z", updated_at: "2026-04-19T00:10:00.000Z", iteration: 4, max_iterations: 50, session_id: sessionId, }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: sessionId, thread_id: "thread-ralph-cont", turn_id: "turn-ralph-cont", prompt: "keep going now", }, { cwd }, ); assert.equal(result.omxEventName, "keyword-detector"); assert.equal(result.skillState?.skill, "ultragoal"); const message = String( ( result.outputJson as { hookSpecificOutput?: { additionalContext?: string }; } )?.hookSpecificOutput?.additionalContext || "", ); // "keep going" is a retired trigger, so the guidance must not claim the mapping while the // continuation still stays on the active skill. assert.doesNotMatch(message, /"keep going" -> ralph/); assert.doesNotMatch(message, /denied workflow keyword/i); assert.doesNotMatch(message, /mode transiting:/); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("ignores generic wrapper fields so metadata cannot trigger workflow routing or Stop blocking", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-wrapper-metadata-"), ); try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); const promptResult = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "sess-wrapper-meta-1", thread_id: "thread-wrapper-meta-1", turn_id: "turn-wrapper-meta-1", input: "$ralplan hidden wrapper text should stay non-routing", text: JSON.stringify({ hook_run_id: "native-stop-wrapper-1", note: "cancel stop wrapper metadata must not be treated like user intent", }), }, { cwd }, ); assert.equal(promptResult.omxEventName, "keyword-detector"); assert.equal(promptResult.skillState, null); assert.equal(promptResult.outputJson, null); assert.equal( existsSync(join(cwd, ".omx", "state", "skill-active-state.json")), false, ); const stopResult = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-wrapper-meta-1", thread_id: "thread-wrapper-meta-1", turn_id: "turn-wrapper-meta-2", }, { cwd }, ); assert.equal(stopResult.omxEventName, "stop"); assert.equal(stopResult.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not expose submitted prompt text to keyword-detector hook plugins", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-prompt-sanitized-"), ); try { await mkdir(join(cwd, ".omx", "hooks"), { recursive: true }); await writeFile( join(cwd, ".omx", "hooks", "capture-keyword-context.mjs"), `import { mkdir, writeFile } from "node:fs/promises"; import { dirname, join } from "node:path"; export async function onHookEvent(event) { if (event.event !== "keyword-detector") return; const outPath = join(process.cwd(), ".omx", "captured-keyword-context.json"); await mkdir(dirname(outPath), { recursive: true }); await writeFile(outPath, JSON.stringify(event.context, null, 2)); } `, "utf-8", ); await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "sess-sanitized-1", thread_id: "thread-sanitized-1", turn_id: "turn-sanitized-1", prompt: "$ralplan approve this blocker-sensitive request", }, { cwd }, ); const captured = JSON.parse( await readFile( join(cwd, ".omx", "captured-keyword-context.json"), "utf-8", ), ) as { prompt?: string; payload?: Record }; assert.equal(captured.prompt, undefined); assert.equal(captured.payload?.prompt, undefined); assert.equal(captured.payload?.input, undefined); assert.equal(captured.payload?.user_prompt, undefined); assert.equal(captured.payload?.userPrompt, undefined); assert.equal(captured.payload?.text, undefined); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not emit UserPromptSubmit routing context for unknown $tokens", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-unknown-token-")); try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "sess-unknown-1", thread_id: "thread-unknown-1", turn_id: "turn-unknown-1", prompt: "$maer-thinking 다시 설명해봐", }, { cwd }, ); assert.equal(result.omxEventName, "keyword-detector"); assert.equal(result.skillState, null); assert.equal(result.outputJson, null); assert.equal( existsSync(join(cwd, ".omx", "state", "skill-active-state.json")), false, ); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not emit UserPromptSubmit routing context for unknown plugin-prefixed $tokens", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-unknown-plugin-token-"), ); try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "sess-unknown-plugin-1", thread_id: "thread-unknown-plugin-1", turn_id: "turn-unknown-plugin-1", prompt: "$oh-my-codex:maer-thinking 다시 설명해봐", }, { cwd }, ); assert.equal(result.omxEventName, "keyword-detector"); assert.equal(result.skillState, null); assert.equal(result.outputJson, null); assert.equal( existsSync(join(cwd, ".omx", "state", "skill-active-state.json")), false, ); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("rejects inert and reserved direct-looking submits across native and CLI surfaces without Stop blockers", async () => { const cases = [ { source: "codex-app", sessionId: "sess-inert-native", prompt: "Do not run $autopilot" }, { source: "cli", sessionId: "sess-reserved-cli", prompt: "/prompts:architect $autopilot" }, { source: "codex-app", sessionId: "sess-marked-native", prompt: "[omx question answered] $autopilot" }, ] as const; for (const testCase of cases) { const cwd = await mkdtemp(join(tmpdir(), `omx-native-hook-${testCase.sessionId}-`)); try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); const submit = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, source: testCase.source, session_id: testCase.sessionId, thread_id: `thread-${testCase.sessionId}`, prompt: testCase.prompt, }, { cwd }, ); assert.equal(submit.skillState, null); assert.equal( existsSync(join(cwd, ".omx", "state", "sessions", testCase.sessionId, "skill-active-state.json")), false, ); assert.equal(existsSync(join(cwd, ".omx", "state", "skill-active-state.json")), false); assert.equal( existsSync(join(cwd, ".omx", "state", "sessions", testCase.sessionId, "autopilot-state.json")), false, ); assert.doesNotMatch( String((submit.outputJson as { hookSpecificOutput?: { additionalContext?: string } } | null)?.hookSpecificOutput?.additionalContext ?? ""), /detected workflow keyword|Autopilot protocol|denied workflow keyword/i, ); const stop = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, source: testCase.source, session_id: testCase.sessionId }, { cwd }, ); assert.equal(stop.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } } }); it("keeps issue #3133 negated, multilingual, and quoted ralplan mentions inert on the native entrypoint", async () => { const rejectedInputs = [ { source: "codex-app", sessionId: "sess-3133-negated", prompt: "Do not run $ralplan and do not repeat the review." }, { source: "cli", sessionId: "sess-3133-russian", prompt: "Не запускай $ralplan" }, { source: "codex-app", sessionId: "sess-3133-quoted", prompt: "Logged review text: \"$ralplan plan this change\"." }, ] as const; for (const testCase of rejectedInputs) { const cwd = await mkdtemp(join(tmpdir(), `omx-native-hook-${testCase.sessionId}-`)); try { const stateDir = join(cwd, ".omx", "state"); const sessionDir = join(stateDir, "sessions", testCase.sessionId); await mkdir(stateDir, { recursive: true }); const submit = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, source: testCase.source, session_id: testCase.sessionId, thread_id: `thread-${testCase.sessionId}`, turn_id: `turn-${testCase.sessionId}`, prompt: testCase.prompt, }, { cwd }, ); assert.equal(submit.skillState, null); assert.equal(existsSync(join(sessionDir, "skill-active-state.json")), false); assert.equal(existsSync(join(sessionDir, "ralplan-state.json")), false); assert.equal(existsSync(join(stateDir, "skill-active-state.json")), false); assert.doesNotMatch( String((submit.outputJson as { hookSpecificOutput?: { additionalContext?: string } } | null)?.hookSpecificOutput?.additionalContext ?? ""), /ralplan/i, ); const stop = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, source: testCase.source, session_id: testCase.sessionId, thread_id: `thread-${testCase.sessionId}`, turn_id: `stop-${testCase.sessionId}`, }, { cwd }, ); assert.equal(stop.outputJson, null); assert.doesNotMatch(JSON.stringify(stop.outputJson ?? {}), /ralplan/i); } finally { await rm(cwd, { recursive: true, force: true }); } } const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-3133-positive-")); const sessionId = "sess-3133-positive"; try { const stateDir = join(cwd, ".omx", "state"); const sessionDir = join(stateDir, "sessions", sessionId); await mkdir(stateDir, { recursive: true }); const submit = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, source: "codex-app", session_id: sessionId, thread_id: "thread-3133-positive", turn_id: "turn-3133-positive", prompt: "$ralplan plan this change", }, { cwd }, ); assert.equal(submit.skillState?.skill, "ralplan"); assert.equal(submit.skillState?.active, true); assert.equal(existsSync(join(sessionDir, "skill-active-state.json")), true); assert.equal(existsSync(join(sessionDir, "ralplan-state.json")), true); const stop = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, source: "codex-app", session_id: sessionId, thread_id: "thread-3133-positive", turn_id: "stop-3133-positive", }, { cwd }, ); assert.equal(stop.outputJson?.decision, "block"); assert.match(String(stop.outputJson?.reason ?? ""), /ralplan is still active/i); assert.match(String(stop.outputJson?.reason ?? ""), /continue from the current ralplan artifact/i); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("denies fresh leading Autopilot invocations on native and CLI prompt submits", async () => { for (const source of ["codex-app", "cli"] as const) { const cwd = await mkdtemp(join(tmpdir(), `omx-native-hook-direct-${source}-`)); const sessionId = `sess-direct-${source}`; try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, source, session_id: sessionId, thread_id: `thread-${source}`, prompt: "$autopilot resume this task", }, { cwd }, ); assert.equal(result.skillState?.skill, "autopilot"); // #3463: autopilot starts normally instead of being preflight-denied assert.notEqual(result.skillState?.error, "documented_host_consensus_receipt_unavailable"); assert.equal( existsSync(join(cwd, ".omx", "state", "sessions", sessionId, "autopilot-state.json")), true, ); assert.equal( existsSync(join(cwd, ".omx", "state", "sessions", sessionId, "deep-interview-state.json")), false, ); } finally { await rm(cwd, { recursive: true, force: true }); } } }); it("uses the first effective Team match for native outside-tmux blocking (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("denies direct $team prompt activation from Codex App/native outside tmux (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("still denies direct $team prompt activation from Codex App/native outside tmux when a tmux return bridge exists (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("keeps direct CLI outside-tmux $team prompt guidance compatible with manual shell launch", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-team-cli-guidance-"), ); try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, source: "cli", session_id: "sess-team-cli-guidance", thread_id: "thread-team-cli-guidance", turn_id: "turn-team-cli-guidance", prompt: "$team ship this fix with verification", }, { cwd }, ); const message = String( ( result.outputJson as { hookSpecificOutput?: { additionalContext?: string }; } | null )?.hookSpecificOutput?.additionalContext ?? "", ); assert.match(message, /run `omx team \.\.\.` yourself from shell/); assert.doesNotMatch(message, /not directly available here/); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("keeps $team prompt-submit routing directly tmux-capable when already inside tmux", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-team-tmux-")); process.env.TMUX = "/tmp/tmux-live"; process.env.TMUX_PANE = "%5"; try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "sess-team-tmux-1", thread_id: "thread-team-tmux-1", turn_id: "turn-team-tmux-1", prompt: "$team ship this fix with verification", }, { cwd }, ); const message = String( ( result.outputJson as { hookSpecificOutput?: { additionalContext?: string }; } )?.hookSpecificOutput?.additionalContext ?? "", ); assert.match( message, /Use the durable OMX team runtime via `omx team \.\.\.`/, ); assert.match(message, /run `omx team --help` yourself/); assert.doesNotMatch(message, /not directly available here/); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("returns actionable denial guidance for unsupported workflow overlaps on prompt submit (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("surfaces transition success output for allowlisted prompt-submit handoffs", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-transition-success-"), ); try { const sessionDir = join( cwd, ".omx", "state", "sessions", "sess-handoff-1", ); await mkdir(sessionDir, { recursive: true }); await writeJson(join(sessionDir, "deep-interview-state.json"), { active: true, mode: "deep-interview", current_phase: "intent-first", deep_interview_gate: { status: "complete", rationale: "Requirements are clarified and ready for ralplan consensus.", }, }); await writeJson(join(sessionDir, "skill-active-state.json"), { active: true, skill: "deep-interview", phase: "planning", session_id: "sess-handoff-1", active_skills: [ { skill: "deep-interview", phase: "planning", active: true, session_id: "sess-handoff-1", }, ], }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "sess-handoff-1", thread_id: "thread-handoff-1", turn_id: "turn-handoff-1", prompt: "$ralplan implement the approved contract", }, { cwd }, ); assert.match( JSON.stringify(result.outputJson), /mode transiting: deep-interview -> ralplan/, ); const completed = JSON.parse( await readFile(join(sessionDir, "deep-interview-state.json"), "utf-8"), ) as { active?: boolean; current_phase?: string; }; assert.equal(completed.active, false); assert.equal(completed.current_phase, "completed"); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("keeps the planning skill active when planning and execution workflows are invoked together", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-planning-precedence-"), ); try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "sess-multi-1", thread_id: "thread-multi-1", turn_id: "turn-multi-1", prompt: "$ralplan $team $ultragoal ship this fix", }, { cwd }, ); const message = String( ( result.outputJson as { hookSpecificOutput?: { additionalContext?: string }; } )?.hookSpecificOutput?.additionalContext || "", ); assert.match(message, /\$ralplan" -> ralplan/); assert.match(message, /\$team" -> team/); assert.match(message, /\$ultragoal" -> ultragoal/); assert.doesNotMatch(message, /mode transiting:/); assert.match( message, /planning preserved over simultaneous execution follow-up; deferred skills: team, ultragoal\./, ); assert.match( message, /use CLI-first state updates via `omx state write\/read\/clear --input '' --json`/, ); assert.doesNotMatch( message, /Use the durable OMX team runtime via `omx team \.\.\.`/, ); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("keeps the planning skill active for mixed plugin-prefixed and bare workflow invocations together", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-plugin-planning-precedence-"), ); try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "sess-plugin-multi-1", thread_id: "thread-plugin-multi-1", turn_id: "turn-plugin-multi-1", prompt: "$oh-my-codex:ralplan $team $oh-my-codex:ultragoal ship this fix", }, { cwd }, ); const message = String( ( result.outputJson as { hookSpecificOutput?: { additionalContext?: string }; } )?.hookSpecificOutput?.additionalContext || "", ); assert.match(message, /\$oh-my-codex:ralplan" -> ralplan/); assert.match(message, /\$team" -> team/); assert.match(message, /\$oh-my-codex:ultragoal" -> ultragoal/); assert.doesNotMatch(message, /mode transiting:/); assert.match( message, /planning preserved over simultaneous execution follow-up; deferred skills: team, ultragoal\./, ); assert.match( message, /use CLI-first state updates via `omx state write\/read\/clear --input '' --json`/, ); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("skips prompt-submit HUD reconciliation for confirmed team worker panes", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-hud-team-worker-skip-"), ); try { const teamName = "hud-worker-skip"; await configureAuthoritativeTeamWorker(cwd, teamName); process.env.TMUX = "1"; process.env.TMUX_PANE = "%10"; process.env.OMX_TEAM_INTERNAL_WORKER = `${teamName}/worker-1`; process.env.OMX_TEAM_WORKER = `${teamName}/worker-1`; process.env[OMX_TMUX_HUD_OWNER_ENV] = "1"; let reconcileCalls = 0; const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "sess-hud-team-worker", prompt: "$ralplan prepare plan", }, { cwd, reconcileHudForPromptSubmitFn: async () => { reconcileCalls += 1; return { status: "recreated", paneId: "%9", desiredHeight: 3, duplicateCount: 0, }; }, }, ); assert.equal(result.omxEventName, "keyword-detector"); assert.equal(reconcileCalls, 0); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("preserves prompt-submit HUD reconciliation for team leader panes", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-hud-team-leader-preserve-"), ); try { const teamName = "hud-leader-keep"; await initTeamState( teamName, "preserve leader HUD reconcile", "executor", 1, cwd, ); await setTeamPaneIds(cwd, teamName, { leaderPaneId: "%42", workerPaneIds: { "worker-1": "%10" }, }); process.env.TMUX = "1"; process.env.TMUX_PANE = "%42"; process.env[OMX_TMUX_HUD_OWNER_ENV] = "1"; let reconcileCall: { cwd: string; sessionId?: string } | null = null; const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "sess-hud-team-leader", prompt: "$ralplan prepare plan", }, { cwd, reconcileHudForPromptSubmitFn: async (hookCwd, deps = {}) => { reconcileCall = { cwd: hookCwd, sessionId: deps.sessionId }; return { status: "recreated", paneId: "%9", desiredHeight: 3, duplicateCount: 0, }; }, }, ); assert.equal(result.omxEventName, "keyword-detector"); assert.deepEqual(reconcileCall, { cwd, sessionId: "sess-hud-team-leader", }); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("preserves prompt-submit HUD reconciliation when worker pane detection is ambiguous", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-hud-team-worker-ambiguous-"), ); try { const teamName = "hud-worker-ambiguous"; await configureAuthoritativeTeamWorker(cwd, teamName); process.env.TMUX = "1"; process.env.TMUX_PANE = "%99"; process.env.OMX_TEAM_INTERNAL_WORKER = `${teamName}/worker-1`; process.env.OMX_TEAM_WORKER = `${teamName}/worker-1`; process.env[OMX_TMUX_HUD_OWNER_ENV] = "1"; let reconcileCalls = 0; const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "sess-hud-team-worker-ambiguous", prompt: "$ralplan prepare plan", }, { cwd, reconcileHudForPromptSubmitFn: async () => { reconcileCalls += 1; return { status: "recreated", paneId: "%9", desiredHeight: 3, duplicateCount: 0, }; }, }, ); assert.equal(result.omxEventName, "keyword-detector"); assert.equal(reconcileCalls, 1); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("preserves prompt-submit HUD reconciliation for native subagents even with worker pane env", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-hud-subagent-worker-preserve-"), ); try { const teamName = "hud-subagent-keep"; await initTeamState( teamName, "preserve subagent HUD reconcile", "executor", 1, cwd, ); await setTeamPaneIds(cwd, teamName, { leaderPaneId: "%42", workerPaneIds: { "worker-1": "%10" }, }); const stateDir = join(cwd, ".omx", "state"); const canonicalSessionId = "sess-subagent-hud-parent"; const leaderNativeSessionId = "native-subagent-hud-parent"; const childNativeSessionId = "native-subagent-hud-child"; const nowIso = new Date().toISOString(); await writeJson(join(stateDir, "session.json"), { session_id: canonicalSessionId, native_session_id: leaderNativeSessionId, }); await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { [canonicalSessionId]: { session_id: canonicalSessionId, leader_thread_id: leaderNativeSessionId, updated_at: nowIso, threads: { [leaderNativeSessionId]: { thread_id: leaderNativeSessionId, kind: "leader", first_seen_at: nowIso, last_seen_at: nowIso, turn_count: 1, }, [childNativeSessionId]: { thread_id: childNativeSessionId, kind: "subagent", first_seen_at: nowIso, last_seen_at: nowIso, turn_count: 1, mode: "verifier", }, }, }, }, }); process.env.TMUX = "1"; process.env.TMUX_PANE = "%10"; process.env.OMX_TEAM_INTERNAL_WORKER = `${teamName}/worker-1`; process.env.OMX_TEAM_WORKER = `${teamName}/worker-1`; process.env[OMX_TMUX_HUD_OWNER_ENV] = "1"; let reconcileCall: { cwd: string; sessionId?: string } | null = null; const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: childNativeSessionId, thread_id: childNativeSessionId, turn_id: "turn-subagent-hud-child", prompt: "Review the worker patch literally; do not activate $ralplan.", }, { cwd, reconcileHudForPromptSubmitFn: async (hookCwd, deps = {}) => { reconcileCall = { cwd: hookCwd, sessionId: deps.sessionId }; return { status: "recreated", paneId: "%9", desiredHeight: 3, duplicateCount: 0, }; }, }, ); assert.equal(result.outputJson, null); assert.equal(reconcileCall, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("runs prompt-submit HUD reconciliation as a best-effort tmux-only side effect", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-hud-reconcile-")); const originalTmux = process.env.TMUX; const originalTmuxPane = process.env.TMUX_PANE; const originalPath = process.env.PATH; const originalHudOwner = process.env[OMX_TMUX_HUD_OWNER_ENV]; const originalArgv = process.argv; try { process.env.TMUX = "1"; process.env.TMUX_PANE = "%1"; process.env[OMX_TMUX_HUD_OWNER_ENV] = "1"; await mkdir(join(cwd, ".omx", "state"), { recursive: true }); await writeFile( join(cwd, ".omx", "hud-config.json"), JSON.stringify( { preset: "focused", git: { display: "branch" } }, null, 2, ), ); const binDir = await mkdtemp( join(tmpdir(), "omx-native-hook-hud-reconcile-bin-"), ); const tmuxLog = join(cwd, "tmux.log"); await writeFile( join(binDir, "tmux"), `#!/usr/bin/env bash set -euo pipefail printf '%s\n' "$*" >> ${JSON.stringify(tmuxLog)} options_file=${JSON.stringify(join(cwd, "tmux-options"))} state_file=${JSON.stringify(join(cwd, "tmux-state"))} if [[ -f "$state_file" ]]; then IFS=$'\t' read -r panes marker < "$state_file" else panes='%1' marker='' fi case "$1" in list-panes) if [[ "$*" == *'#{pane_id} #{pane_dead} #{pane_pid}'* ]]; then printf '%%1 0 200\n' [[ "$panes" == *'%9'* ]] && printf '%%9 0 201\n' elif [[ "$*" == *'pane_current_command'* ]]; then printf '%%1\x1f/bin/codex\x1f0\x1f0\x1f80\x1f58\x1f57\x1f80\x1f61\x1f$1\x1f@1\x1f0\x1f200\x1f/bin/codex\x1f/tmp\n' [[ "$panes" == *'%9'* ]] && printf '%%9\x1fnode\x1f0\x1f59\x1f80\x1f2\x1f60\x1f80\x1f61\x1f$1\x1f@1\x1f0\x1f201\x1fenv OMX_SESSION_ID=sess-hud-1 OMX_TMUX_HUD_LEADER_PANE=%%1 node omx hud --watch\x1f/tmp\n' elif [[ "$*" == *'#{pane_id}|#{pane_start_command}'* ]]; then printf '%%1|/bin/codex\n' [[ "$panes" == *'%9'* ]] && printf '%%9|OMX_TMUX_SPLIT_OPERATION_MARKER=%s; export OMX_TMUX_SPLIT_OPERATION_MARKER; node dist/cli/omx.js hud --watch\n' "'$marker'" elif [[ "$*" == *'pane_start_command'* ]]; then printf '%%1\t/bin/codex\n' [[ "$panes" == *'%9'* ]] && printf '%%9\tOMX_TMUX_SPLIT_OPERATION_MARKER='"'"'"$marker'"'"'; export OMX_TMUX_SPLIT_OPERATION_MARKER; node dist/cli/omx.js hud --watch\n' elif [[ "$panes" == *'%9'* ]]; then printf '%%1\n%%9\n' else printf '%%1\n' fi ;; display-message) if [[ "$*" == *'#{pane_id}'*'#{pane_dead}'*'#{pane_pid}'*'#{session_id}'*'#{window_id}'* ]]; then printf '%%1|0|200|$1|@1\n' elif [[ "$*" == *'#{session_id}'*'#{window_id}'* ]]; then printf '$1|@1\n' else printf '200|60\n' fi ;; set-option) printf '%s\t%s\n' "$3" "$4" >> "$options_file" ;; show-options) value='' if [[ -f "$options_file" ]]; then while IFS=$'\t' read -r key stored; do [[ "$key" == "$4" ]] && value="$stored" done < "$options_file" fi printf '%s\n' "$value" ;; if-shell) success="$6" if [[ "$success" == *'split-window'* ]]; then if [[ "$success" =~ OMX_TMUX_SPLIT_OPERATION_MARKER=\\'([^\\']+)\\' ]]; then marker="\${BASH_REMATCH[1]}" fi panes='%1 %9' printf '%s\t%s\n' "$panes" "$marker" > "$state_file" fi receipt="\${success#*display-message -p }" if [[ "$receipt" != "$success" ]]; then receipt="\${receipt%%[[:space:]]*}" printf '%s\n' "$receipt" fi ;; resize-pane) ;; esac exit 0 ` ); await chmod(join(binDir, "tmux"), 0o755); const tmuxSyntax = spawnSync("bash", ["-n", join(binDir, "tmux")], { encoding: "utf-8", }); assert.equal(tmuxSyntax.status, 0, tmuxSyntax.stderr); process.env.PATH = `${binDir}:${originalPath}`; process.argv = [originalArgv[0] || "node", "/tmp/codex-host-binary"]; const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "sess-hud-1", prompt: "$ralplan prepare plan", }, { cwd }, ); assert.equal(result.omxEventName, "keyword-detector"); const tmuxCalls = await readFile(tmuxLog, "utf-8"); assert.match(tmuxCalls, /list-panes -t %1 -F/); assert.match( tmuxCalls, new RegExp(`if-shell -F -t %1 [^\\n]*#\\{pane_pid\\},200[^\\n]*#\\{session_id\\},\\$1[^\\n]*#\\{window_id\\},@1[^\\n]*split-window -v -l ${HUD_TMUX_HEIGHT_LINES} -d -t %1 [^\\n]*display-message -p __omx_hud_split_`), ); assert.match( tmuxCalls, /dist\/cli\/omx\.js'\\'' hud --watch '\\''--preset=focused'\\'''/, ); assert.doesNotMatch(tmuxCalls, /\/tmp\/codex-host-binary' hud --watch/); } finally { if (originalTmux === undefined) { delete process.env.TMUX; } else { process.env.TMUX = originalTmux; } if (originalTmuxPane === undefined) { delete process.env.TMUX_PANE; } else { process.env.TMUX_PANE = originalTmuxPane; } if (originalHudOwner === undefined) { delete process.env[OMX_TMUX_HUD_OWNER_ENV]; } else { process.env[OMX_TMUX_HUD_OWNER_ENV] = originalHudOwner; } process.env.PATH = originalPath; process.argv = originalArgv; await rm(cwd, { recursive: true, force: true }); } }); it("skips prompt-submit HUD reconciliation during doctor smoke validation", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-doctor-smoke-hud-"), ); const originalTmux = process.env.TMUX; const originalTmuxPane = process.env.TMUX_PANE; const originalHudOwner = process.env[OMX_TMUX_HUD_OWNER_ENV]; const originalDoctorSmoke = process.env.OMX_NATIVE_HOOK_DOCTOR_SMOKE; try { process.env.TMUX = "1"; process.env.TMUX_PANE = "%1"; process.env[OMX_TMUX_HUD_OWNER_ENV] = "1"; process.env.OMX_NATIVE_HOOK_DOCTOR_SMOKE = "1"; let reconcileCalled = false; const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "omx-doctor-plugin-hook-smoke", prompt: "$ralplan doctor plugin hook smoke test", }, { cwd, reconcileHudForPromptSubmitFn: async () => { reconcileCalled = true; return { status: "recreated", paneId: "%9", desiredHeight: 3, duplicateCount: 0, }; }, }, ); assert.equal(result.omxEventName, "keyword-detector"); assert.equal(reconcileCalled, false); } finally { if (originalTmux === undefined) delete process.env.TMUX; else process.env.TMUX = originalTmux; if (originalTmuxPane === undefined) delete process.env.TMUX_PANE; else process.env.TMUX_PANE = originalTmuxPane; if (originalHudOwner === undefined) delete process.env[OMX_TMUX_HUD_OWNER_ENV]; else process.env[OMX_TMUX_HUD_OWNER_ENV] = originalHudOwner; if (originalDoctorSmoke === undefined) delete process.env.OMX_NATIVE_HOOK_DOCTOR_SMOKE; else process.env.OMX_NATIVE_HOOK_DOCTOR_SMOKE = originalDoctorSmoke; await rm(cwd, { recursive: true, force: true }); } }); it("recreates a leader-only HUD pane when UserPromptSubmit revives with the canonical session id", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-hud-reuse-")); const originalTmux = process.env.TMUX; const originalTmuxPane = process.env.TMUX_PANE; const originalPath = process.env.PATH; const originalHudOwner = process.env[OMX_TMUX_HUD_OWNER_ENV]; try { process.env.TMUX = "1"; process.env.TMUX_PANE = "%1"; process.env[OMX_TMUX_HUD_OWNER_ENV] = "1"; const canonicalSessionId = "omx-canonical-hud-reuse"; const nativeSessionId = "codex-native-hud-reuse"; await mkdir(join(cwd, ".omx", "state", "sessions", canonicalSessionId), { recursive: true }); await writeSessionStart(cwd, canonicalSessionId, { nativeSessionId }); const binDir = await mkdtemp( join(tmpdir(), "omx-native-hook-hud-reuse-bin-"), ); const tmuxLog = join(cwd, "tmux.log"); await writeFile( join(binDir, "tmux"), `#!/usr/bin/env bash set -euo pipefail printf '%s\n' "$*" >> ${JSON.stringify(tmuxLog)} options_file=${JSON.stringify(join(cwd, "tmux-options"))} state_file=${JSON.stringify(join(cwd, "tmux-state"))} if [[ -f "$state_file" ]]; then IFS=$'\t' read -r panes marker < "$state_file" else panes='%1 %2' marker='' fi case "$1" in list-panes) if [[ "$*" == *'#{pane_id} #{pane_dead} #{pane_pid}'* ]]; then printf '%%1 0 200\n%%2 0 201\n' [[ "$panes" == *'%9'* ]] && printf '%%9 0 202\n' elif [[ "$*" == *'pane_current_command'* ]]; then printf '%%1\x1f/bin/codex\x1f0\x1f0\x1f80\x1f58\x1f57\x1f80\x1f61\x1f$1\x1f@1\x1f0\x1f200\x1f/bin/codex\x1f/tmp\n' if [[ "$*" != *'-t %1'* ]]; then printf '%%2\x1fnode\x1f0\x1f59\x1f80\x1f2\x1f60\x1f80\x1f61\x1f$1\x1f@2\x1f0\x1f201\x1fenv OMX_SESSION_ID=omx-canonical-hud-reuse OMX_TMUX_HUD_LEADER_PANE=%%1 node omx hud --watch\x1f/tmp\n' fi [[ "$panes" == *'%9'* ]] && printf '%%9\x1fnode\x1f0\x1f59\x1f80\x1f2\x1f60\x1f80\x1f61\x1f$1\x1f@1\x1f0\x1f202\x1fenv OMX_SESSION_ID=omx-canonical-hud-reuse OMX_TMUX_HUD_LEADER_PANE=%%1 node omx hud --watch\x1f/tmp\n' elif [[ "$*" == *'#{pane_id}|#{pane_start_command}'* ]]; then printf '%%1|/bin/codex\n' printf '%%2|exec env OMX_TMUX_HUD_OWNER=1 ${OMX_TMUX_HUD_LEADER_PANE_ENV}=%%1 /node /omx.js hud --watch\n' [[ "$panes" == *'%9'* ]] && printf '%%9|OMX_TMUX_SPLIT_OPERATION_MARKER=%s; export OMX_TMUX_SPLIT_OPERATION_MARKER; node dist/cli/omx.js hud --watch\n' "'$marker'" elif [[ "$*" == *'pane_start_command'* ]]; then printf '%%1\t/bin/codex\n' printf '%%2\texec env OMX_TMUX_HUD_OWNER='"'"'"1'"'"' ${OMX_TMUX_HUD_LEADER_PANE_ENV}='"'"'"%%1'"'"' /node /omx.js hud --watch\n' [[ "$panes" == *'%9'* ]] && printf '%%9\tOMX_TMUX_SPLIT_OPERATION_MARKER='"'"'"$marker'"'"'; export OMX_TMUX_SPLIT_OPERATION_MARKER; node dist/cli/omx.js hud --watch\n' elif [[ "$panes" == *'%9'* ]]; then if [[ "$*" == *'-t %1'* ]]; then printf '%%1\n%%9\n' else printf '%%1\n%%2\n%%9\n' fi else if [[ "$*" == *'-t %1'* ]]; then printf '%%1\n' else printf '%%1\n%%2\n' fi fi ;; display-message) if [[ "$*" == *'#{pane_id}'*'#{pane_dead}'*'#{pane_pid}'*'#{session_id}'*'#{window_id}'* ]]; then printf '%%1|0|200|$1|@1\n' elif [[ "$*" == *'#{session_id}'*'#{window_id}'* ]]; then printf '$1|@1\n' else printf '200|60\n' fi ;; set-option) printf '%s\t%s\n' "$3" "$4" >> "$options_file" ;; show-options) value='' if [[ -f "$options_file" ]]; then while IFS=$'\t' read -r key stored; do [[ "$key" == "$4" ]] && value="$stored" done < "$options_file" fi printf '%s\n' "$value" ;; if-shell) success="$6" if [[ "$success" == *'split-window'* ]]; then if [[ "$success" =~ OMX_TMUX_SPLIT_OPERATION_MARKER=\\'([^\\']+)\\' ]]; then marker="\${BASH_REMATCH[1]}" fi panes='%1 %2 %9' printf '%s\t%s\n' "$panes" "$marker" > "$state_file" fi receipt="\${success#*display-message -p }" if [[ "$receipt" != "$success" ]]; then receipt="\${receipt%%[[:space:]]*}" printf '%s\n' "$receipt" fi ;; resize-pane) ;; esac exit 0 ` ); await chmod(join(binDir, "tmux"), 0o755); const tmuxSyntax = spawnSync("bash", ["-n", join(binDir, "tmux")], { encoding: "utf-8", }); assert.equal(tmuxSyntax.status, 0, tmuxSyntax.stderr); process.env.PATH = `${binDir}:${originalPath}`; const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: nativeSessionId, thread_id: "thread-hud-reuse", turn_id: "turn-hud-reuse", prompt: "$ralplan prepare plan", }, { cwd }, ); assert.equal(result.omxEventName, "keyword-detector"); const tmuxCalls = await readFile(tmuxLog, "utf-8"); assert.match(tmuxCalls, /list-panes -t %1 -F/); assert.match( tmuxCalls, /if-shell -F -t %1 [^\n]*#\{pane_pid\},200[^\n]*#\{session_id\},\$1[^\n]*#\{window_id\},@1[^\n]*split-window [^\n]*display-message -p __omx_hud_split_/, ); assert.equal( existsSync( join( cwd, ".omx", "state", "sessions", canonicalSessionId, "ralplan-state.json", ), ), true, ); assert.equal( existsSync( join( cwd, ".omx", "state", "sessions", nativeSessionId, "ralplan-state.json", ), ), false, ); } finally { if (originalTmux === undefined) delete process.env.TMUX; else process.env.TMUX = originalTmux; if (originalTmuxPane === undefined) delete process.env.TMUX_PANE; else process.env.TMUX_PANE = originalTmuxPane; if (originalHudOwner === undefined) delete process.env[OMX_TMUX_HUD_OWNER_ENV]; else process.env[OMX_TMUX_HUD_OWNER_ENV] = originalHudOwner; process.env.PATH = originalPath; await rm(cwd, { recursive: true, force: true }); } }); it("skips prompt-submit HUD reconciliation inside unowned tmux panes", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-hud-unowned-")); const originalTmux = process.env.TMUX; const originalTmuxPane = process.env.TMUX_PANE; const originalPath = process.env.PATH; const originalHudOwner = process.env[OMX_TMUX_HUD_OWNER_ENV]; try { process.env.TMUX = "1"; process.env.TMUX_PANE = "%claude"; delete process.env[OMX_TMUX_HUD_OWNER_ENV]; const binDir = await mkdtemp( join(tmpdir(), "omx-native-hook-hud-unowned-bin-"), ); const tmuxLog = join(cwd, "tmux.log"); await writeFile( join(binDir, "tmux"), `#!/usr/bin/env bash printf '%s\n' "$*" >> ${JSON.stringify(tmuxLog)} exit 0 `, ); await chmod(join(binDir, "tmux"), 0o755); process.env.PATH = `${binDir}:${originalPath}`; const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "sess-hud-unowned", prompt: "$ralplan prepare plan", }, { cwd }, ); assert.equal(result.omxEventName, "keyword-detector"); assert.equal(existsSync(tmuxLog), false); } finally { if (originalTmux === undefined) delete process.env.TMUX; else process.env.TMUX = originalTmux; if (originalTmuxPane === undefined) delete process.env.TMUX_PANE; else process.env.TMUX_PANE = originalTmuxPane; if (originalHudOwner === undefined) delete process.env[OMX_TMUX_HUD_OWNER_ENV]; else process.env[OMX_TMUX_HUD_OWNER_ENV] = originalHudOwner; process.env.PATH = originalPath; await rm(cwd, { recursive: true, force: true }); } }); it("blocks Bash omx question when no leader-pane return hint is preserved (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("does not block Bash commands that only mention omx question in quoted arguments", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-pretool-question-quoted-mention-"), ); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-question-quoted-mention", tool_input: { command: `omx ultragoal create-goals --brief "Deep interview says omx question failed in tmux"`, }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not block Bash heredocs that only document omx question text", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-pretool-question-heredoc-mention-"), ); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-question-heredoc-mention", tool_input: { command: `cat > issue-notes.md <<'EOF'\nomx question failed in the attached tmux pane\nEOF`, }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("allows Bash omx question when the command preserves the leader-pane return hint", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-pretool-question-allow-"), ); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-question-allow", tool_input: { command: `OMX_QUESTION_RETURN_PANE=$TMUX_PANE omx question --json --input '{"question":"Q?","options":["A"],"allow_other":true}'`, }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("allows the quoted pane env assignment emitted by the deep-interview bridge command", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-pretool-question-quoted-allow-"), ); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-question-quoted-allow", tool_input: { command: `OMX_QUESTION_RETURN_PANE='%42' node ./dist/cli/omx.js question --json --input '{"question":"Q?","options":["A"],"allow_other":true}'`, }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("allows PowerShell env bridge forms for omx question return panes", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-pretool-question-powershell-allow-"), ); try { const commands = [ `$env:OMX_QUESTION_RETURN_PANE=$env:TMUX_PANE; omx question --json --input '{"question":"Q?","options":["A"],"allow_other":true}'`, `$env:OMX_QUESTION_RETURN_PANE='%42'; node ./dist/cli/omx.js question --json --input '{"question":"Q?","options":["A"],"allow_other":true}'`, `$env:OMX_LEADER_PANE_ID="%43"; omx question --json --input '{"question":"Q?","options":["A"],"allow_other":true}'`, ]; for (const [index, command] of commands.entries()) { const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "Bash", tool_use_id: `tool-question-powershell-allow-${index}`, tool_input: { command }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal(result.outputJson, null); } } finally { await rm(cwd, { recursive: true, force: true }); } }); it("allows Bash omx question when a valid inherited OMX_QUESTION_RETURN_PANE bridge is already exported", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-pretool-question-env-allow-"), ); const originalReturnPane = process.env.OMX_QUESTION_RETURN_PANE; try { process.env.OMX_QUESTION_RETURN_PANE = "%42"; const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-question-env-allow", tool_input: { command: `omx question --json --input '{"question":"Q?","options":["A"],"allow_other":true}'`, }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal(result.outputJson, null); } finally { if (originalReturnPane === undefined) delete process.env.OMX_QUESTION_RETURN_PANE; else process.env.OMX_QUESTION_RETURN_PANE = originalReturnPane; await rm(cwd, { recursive: true, force: true }); } }); it("allows Bash omx question when a valid inherited OMX_LEADER_PANE_ID bridge is already exported", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-pretool-question-leader-env-allow-"), ); const originalLeaderPane = process.env.OMX_LEADER_PANE_ID; try { process.env.OMX_LEADER_PANE_ID = "%43"; const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-question-leader-env-allow", tool_input: { command: `omx question --json --input '{"question":"Q?","options":["A"],"allow_other":true}'`, }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal(result.outputJson, null); } finally { if (originalLeaderPane === undefined) delete process.env.OMX_LEADER_PANE_ID; else process.env.OMX_LEADER_PANE_ID = originalLeaderPane; await rm(cwd, { recursive: true, force: true }); } }); it("still blocks Bash omx question when an inherited OMX_QUESTION_RETURN_PANE value is malformed (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("blocks Bash node omx.js question when the command does not preserve the leader-pane return hint (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("blocks native/App Bash omx question with bridge-specific outside-tmux guidance (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("blocks native/App Bash omx question even when the command preserves a tmux return bridge (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("blocks native/App Bash omx question when a valid inherited OMX_QUESTION_RETURN_PANE bridge is already exported (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("blocks Bash omx hud from Codex App/native outside tmux without PreToolUse additionalContext (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("blocks Bash omx team from Codex App/native outside tmux (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("blocks Bash node omx.js team from Codex App/native outside tmux (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("preserves direct CLI outside-tmux omx team Bash behavior", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-pretool-team-cli-outside-"), ); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, source: "cli", session_id: "sess-team-cli-outside", tool_name: "Bash", tool_use_id: "tool-team-cli-outside", tool_input: { command: "omx team status my-team" }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("preserves source-less outside-tmux omx team Bash behavior when no native session evidence exists", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-pretool-team-cli-nosource-"), ); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: "sess-team-cli-nosource", tool_name: "Bash", tool_use_id: "tool-team-cli-nosource", tool_input: { command: "omx team status my-team" }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks implementation file edits while deep-interview remains active after a clarified answer (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("does not self-lock PreToolUse after completed deep-interview handoff leaves Autopilot in deep-interview phase", async () => { const cwd = await mkdtemp( join( tmpdir(), "omx-native-hook-pretool-deep-interview-completed-autopilot-", ), ); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-di-completed-autopilot"; const threadId = "thread-di-completed-autopilot"; const sessionDir = join(stateDir, "sessions", sessionId); await mkdir(sessionDir, { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId, cwd, }); await writeJson(join(sessionDir, "skill-active-state.json"), { version: 1, active: true, skill: "autopilot", keyword: "$autopilot", phase: "deep-interview", initialized_mode: "autopilot", initialized_state_path: `.omx/state/sessions/${sessionId}/autopilot-state.json`, session_id: sessionId, thread_id: threadId, active_skills: [ { skill: "autopilot", phase: "deep-interview", active: true, session_id: sessionId, thread_id: threadId, }, ], }); await writeJson(join(sessionDir, "deep-interview-state.json"), { active: false, mode: "deep-interview", current_phase: "completed", session_id: sessionId, thread_id: threadId, deep_interview_gate: { status: "complete", handoff_summary: "Requirements were clarified before the Autopilot handoff.", }, }); await writeJson(join(sessionDir, "autopilot-state.json"), { active: true, mode: "autopilot", current_phase: "deep-interview", session_id: sessionId, thread_id: threadId, }); const implementationEdit = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: threadId, tool_name: "Edit", tool_use_id: "tool-di-completed-autopilot-edit", tool_input: { file_path: "src/runtime.ts", old_string: "a", new_string: "b", }, }, { cwd }, ); assert.equal(implementationEdit.omxEventName, "pre-tool-use"); assert.equal(implementationEdit.outputJson, null); const stateRepair = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: threadId, tool_name: "mcp__omx_state__state_write", tool_use_id: "tool-di-completed-autopilot-state-repair", tool_input: { mode: "autopilot", current_phase: "ralplan", active: true, }, }, { cwd }, ); assert.equal(stateRepair.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("issue #3239 permits command-specific safe operations during Autopilot deep-interview without relaxing mutation guards (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("allows only the canonical leader's authenticated standalone deep-interview complete terminal state write (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("issue #3313/#3314 permits standalone deep-interview lifecycle reachability and read-only discovery without relaxing write guards (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("issue #3314 permits ralplan planning read-only discovery without relaxing write guards (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("does not protect matching state basenames outside the canonical state root", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-product-session-json-")); try { await mkdir(join(cwd, "src"), { recursive: true }); const result = await dispatchCodexNativeHook({ hook_event_name: "PreToolUse", cwd, session_id: "sess-product-session-json", tool_name: "Write", tool_use_id: "tool-product-session-json", tool_input: { file_path: "src/session.json", content: "{}\n" }, }, { cwd }); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks filesystem MCP writes to protected workflow state without an active mode (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("allows canonical leader deep-interview artifact and state writes while blocking implementation Bash writes (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("allows canonical leader ralplan complete terminal state writes while blocking partial deactivation writes (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("emits hook-specific deny ralplan PreToolUse JSON for wrapped implementation writes on the live CLI path (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("blocks MCP state_clear for standalone protected planning modes (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("allows read-only diagnostics mentioning apply_patch while deep-interview blocks real apply_patch invocations (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("blocks operator-adjacent transport bypass forms while still allowing a safe direct-wrapper state write (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("allows direct literal planning redirects while blocking all variable redirect targets (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("allows deep-interview apply_patch artifact writes from freeform patch text while blocking outside paths (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("allows Ralplan read-only inspection and planning artifact writes while blocking implementation targets (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("allows Autopilot ralplan planning artifacts while blocking implementation writes (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("allows Ralplan Beads tracker metadata writes during planning", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-ralplan-beads-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionDir = join(stateDir, "sessions", "sess-ralplan-beads"); await mkdir(sessionDir, { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: "sess-ralplan-beads", cwd }); await writeJson(join(sessionDir, "skill-active-state.json"), { version: 1, active: true, skill: "ralplan", phase: "planning", session_id: "sess-ralplan-beads", thread_id: "thread-ralplan-beads", active_skills: [ { skill: "ralplan", phase: "planning", active: true, session_id: "sess-ralplan-beads", thread_id: "thread-ralplan-beads", }, ], }); await writeJson(join(sessionDir, "ralplan-state.json"), { active: true, mode: "ralplan", current_phase: "planning", session_id: "sess-ralplan-beads", thread_id: "thread-ralplan-beads", }); await writeCanonicalLeaderFixture( stateDir, "sess-ralplan-beads", "thread-ralplan-beads", cwd, ); const allowedWrite = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: "sess-ralplan-beads", thread_id: "thread-ralplan-beads", agent_id: "thread-ralplan-beads", tool_name: "Write", tool_use_id: "tool-ralplan-beads-write", tool_input: { file_path: ".beads/issues.db", content: "metadata" }, }, { cwd }, ); assert.equal(allowedWrite.outputJson, null); const allowedEdit = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: "sess-ralplan-beads", thread_id: "thread-ralplan-beads", agent_id: "thread-ralplan-beads", tool_name: "Edit", tool_use_id: "tool-ralplan-beads-edit", tool_input: { file_path: ".beads/tasks/issue.json", old_string: "old", new_string: "new" }, }, { cwd }, ); assert.equal(allowedEdit.outputJson, null); const allowedPatch = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: "sess-ralplan-beads", thread_id: "thread-ralplan-beads", agent_id: "thread-ralplan-beads", tool_name: "apply_patch", tool_use_id: "tool-ralplan-beads-apply-patch", tool_input: { input: "*** Begin Patch\n*** Add File: .beads/tasks/issue.json\n+{}\n*** End Patch\n", }, }, { cwd }, ); assert.equal(allowedPatch.outputJson, null); const allowedCommands = [ "bd --db .beads/issues.db create 'Issue title'", "bd --db .beads/issues.db update OMX-1 --title 'Issue title'", "bd --db .beads/issues.db edit OMX-1 --title 'Issue title'", "bd --db .beads/issues.db comments add OMX-1 'evidence'", "bd --db .beads/issues.db close OMX-1", "bd --db .beads/issues.db reopen OMX-1", "bd --db .beads/issues.db status OMX-1", "bd --db .beads/issues.db dep add OMX-1 OMX-2", ]; for (const [index, command] of allowedCommands.entries()) { const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: "sess-ralplan-beads", thread_id: "thread-ralplan-beads", agent_id: "thread-ralplan-beads", tool_name: "Bash", tool_use_id: `tool-ralplan-beads-bd-${index}`, tool_input: { command }, }, { cwd }, ); assert.equal(result.outputJson, null, `expected Beads metadata command to be allowed: ${command}`); } } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks unsafe Beads tracker targets and mixed implementation writes during Autopilot ralplan planning (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("allows Autopilot rework implementation writes while ralplan remains guarded", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-autopilot-rework-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionDir = join(stateDir, "sessions", "sess-autopilot-rework"); await mkdir(sessionDir, { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: "sess-autopilot-rework", cwd }); await writeJson(join(sessionDir, "skill-active-state.json"), { version: 1, active: true, skill: "autopilot", phase: "rework", session_id: "sess-autopilot-rework", thread_id: "thread-autopilot-rework", active_skills: [ { skill: "autopilot", phase: "rework", active: true, session_id: "sess-autopilot-rework", thread_id: "thread-autopilot-rework", }, ], }); await writeJson(join(sessionDir, "autopilot-state.json"), { active: true, mode: "autopilot", current_phase: "rework", review_cycle: 2, review_verdict: { clean: false, recommendation: "REQUEST_CHANGES", findings: ["src/implementation.ts"] }, session_id: "sess-autopilot-rework", thread_id: "thread-autopilot-rework", }); const allowedImplementationEdit = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: "sess-autopilot-rework", thread_id: "thread-autopilot-rework", tool_name: "Edit", tool_use_id: "tool-autopilot-rework-src-edit", tool_input: { file_path: "src/implementation.ts", old_string: "a", new_string: "b" }, }, { cwd }, ); assert.equal(allowedImplementationEdit.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks typed agent-role-only implementation writes under active ralplan (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("blocks trusted typed agent-role implementation writes under active ralplan while preserving planning artifacts (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("blocks typed thread-spawn provenance when the parent belongs to another leader session (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("keeps typed and untyped collaboration child implementation writes blocked under active ralplan planning (#3116) (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("keeps typed and untyped collaboration child implementation writes blocked under active deep-interview planning (#3116) (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("allows null-device fd redirects while deep-interview blocks real Bash writes (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("allows implementation tools after an explicit deep-interview handoff deactivates the mode", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-deep-interview-handoff-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionDir = join(stateDir, "sessions", "sess-di-handoff"); await mkdir(sessionDir, { recursive: true }); await writeJson(join(sessionDir, "skill-active-state.json"), { version: 1, active: true, skill: "deep-interview", phase: "planning", session_id: "sess-di-handoff", active_skills: [{ skill: "deep-interview", phase: "planning", active: true, session_id: "sess-di-handoff" }], }); await writeJson(join(sessionDir, "deep-interview-state.json"), { active: true, mode: "deep-interview", current_phase: "intent-first", session_id: "sess-di-handoff", }); await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "sess-di-handoff", prompt: "$ultragoal implement the clarified spec in src/implementation.ts", }, { cwd }, ); const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: "sess-di-handoff", tool_name: "Edit", tool_use_id: "tool-di-post-handoff-edit", tool_input: { file_path: "src/implementation.ts", old_string: "a", new_string: "b" }, }, { cwd }, ); assert.equal(result.outputJson, null); const completed = JSON.parse(await readFile(join(sessionDir, "deep-interview-state.json"), "utf-8")) as { active?: boolean }; assert.equal(completed.active, false); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("returns a destructive-command caution on PreToolUse for rm -rf dist", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-danger-")); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-danger", tool_input: { command: "rm -rf dist" }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.deepEqual(result.outputJson, { hookSpecificOutput: { hookEventName: "PreToolUse", }, systemMessage: "Destructive Bash command detected (`rm -rf dist`). Confirm the target and expected side effects before running it.", }); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("stays silent on PreToolUse for neutral pwd", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-neutral-")); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-neutral", tool_input: { command: "pwd" }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("records native subagent capacity exhaustion from spawn_agent PostToolUse output", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-subagent-capacity-record-")); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PostToolUse", cwd, session_id: "sess-subagent-capacity-record", thread_id: "thread-subagent-capacity-record", turn_id: "turn-subagent-capacity-record", tool_name: "multi_agent_v1.spawn_agent", tool_response: { error: "collab spawn failed: agent thread limit reached", }, }, { cwd }, ); assert.equal(result.omxEventName, "post-tool-use"); assert.equal(result.outputJson, null); const blocker = JSON.parse( await readFile(join(cwd, ".omx", "state", "native-subagent-capacity-blocker.json"), "utf-8"), ) as Record; assert.equal(blocker.reason, "agent_thread_limit_reached"); assert.equal(blocker.session_id, "sess-subagent-capacity-record"); assert.equal(blocker.thread_id, "thread-subagent-capacity-record"); assert.equal(blocker.tool_name, "multi_agent_v1.spawn_agent"); assert.match(String(blocker.error_summary), /agent thread limit reached/); assert.ok(Date.parse(String(blocker.expires_at)) > Date.parse(String(blocker.observed_at))); assert.equal(existsSync(join(cwd, ".omx", "state", "native-subagent-support.json")), false); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("records unsupported native subagent support blocker from spawn_agent PostToolUse output", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-subagent-support-record-")); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PostToolUse", cwd, session_id: "sess-subagent-support-record", thread_id: "thread-subagent-support-record", turn_id: "turn-subagent-support-record", tool_name: "multi_agent_v1.spawn_agent", tool_response: { error: "unknown tool: multi_agent_v1.spawn_agent is unavailable" }, }, { cwd }, ); assert.equal(result.omxEventName, "post-tool-use"); const blocker = JSON.parse( await readFile(join(cwd, ".omx", "state", "native-subagent-support.json"), "utf-8"), ) as Record; assert.equal(blocker.status, "unsupported"); assert.equal(blocker.reason, "multi_agent_v1_unavailable"); assert.equal(blocker.session_id, "sess-subagent-support-record"); assert.equal(blocker.thread_id, "thread-subagent-support-record"); assert.equal(blocker.tool_name, "multi_agent_v1.spawn_agent"); assert.match(String(blocker.evidence), /unknown tool/); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not persist support or capacity poison from successful collaboration child output", async () => { for (const toolName of [ "collaboration.spawn_agent", "collaboration.list_agents", "collaboration.followup_task", "collaboration.wait_agent", ]) { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-collaboration-success-")); try { await dispatchCodexNativeHook( { hook_event_name: "PostToolUse", cwd, session_id: "sess-collaboration-success", thread_id: "thread-collaboration-success", tool_name: toolName, tool_response: { success: true, status: "completed", output: "Child completed. Optional packed plugin was unavailable, unsupported, and not found.", }, }, { cwd }, ); const stateDir = join(cwd, ".omx", "state"); assert.equal(existsSync(join(stateDir, "native-subagent-support.json")), false, toolName); assert.equal(existsSync(join(stateDir, "native-subagent-capacity-blocker.json")), false, toolName); await dispatchCodexNativeHook({ hook_event_name: "SessionStart", cwd, session_id: "sess-collaboration-success", thread_id: "thread-collaboration-success", }, { cwd }); assert.equal(existsSync(join(stateDir, "native-subagent-support.json")), false, `${toolName} restart`); } finally { await rm(cwd, { recursive: true, force: true }); } } }); it("does not persist legacy prose poison from non-spawn collaboration results", async () => { for (const toolName of [ "collaboration.list_agents", "collaboration.followup_task", "collaboration.wait_agent", ]) { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-collaboration-legacy-success-")); try { await dispatchCodexNativeHook({ hook_event_name: "PostToolUse", cwd, tool_name: toolName, tool_response: "Successful result: optional dependency unavailable, unsupported, or not found.", }, { cwd }); assert.equal(existsSync(join(cwd, ".omx", "state", "native-subagent-support.json")), false, toolName); } finally { await rm(cwd, { recursive: true, force: true }); } } }); it("#3316: denies collaboration.send_message during active deep-interview without host-authenticated caller-parent-target proof (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("#3316: denies collaboration.send_message during active ralplan without host-authenticated caller-parent-target proof (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); // Direct-cancel positives must prove behavior in a clean execution context, // but the test runner itself may inherit Node startup/output instrumentation // (e.g. NODE_V8_COVERAGE from the coverage lane). Clear all listed baseline // values, including the tolerated NODE_EXTRA_CA_CERTS, solely to establish a // deterministic baseline; denial cases inject unsafe values explicitly. // The list is a test-environment reset, not the hook's unsafe-env denylist. const RUNNER_BASELINE_NODE_ENV_NAMES = [ "NODE_OPTIONS", "NODE_EXTRA_CA_CERTS", "OPENSSL_CONF", "NODE_V8_COVERAGE", "NODE_COMPILE_CACHE", "NODE_REDIRECT_WARNINGS", "NODE_REPORT_DIRECTORY", "NODE_REPORT_FILENAME", ]; const withCleanRunnerNodeEnvironment = async (run: () => Promise): Promise => { const previous = new Map(); for (const name of RUNNER_BASELINE_NODE_ENV_NAMES) { previous.set(name, process.env[name]); delete process.env[name]; } try { return await run(); } finally { for (const [name, value] of previous) { if (value === undefined) delete process.env[name]; else process.env[name] = value; } } }; const writeFlattenedCollaborationRalplanFixture = async (cwd: string) => { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-flattened-collab-ralplan"; const leaderThreadId = "thread-flattened-collab-ralplan-leader"; const sessionDir = join(stateDir, "sessions", sessionId); await mkdir(sessionDir, { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId, native_session_id: leaderThreadId, cwd, }); await writeJson(join(sessionDir, "skill-active-state.json"), { version: 1, active: true, skill: "autopilot", phase: "ralplan", session_id: sessionId, thread_id: leaderThreadId, active_skills: [{ skill: "autopilot", phase: "ralplan", active: true, session_id: sessionId, thread_id: leaderThreadId, }], }); await writeJson(join(sessionDir, "autopilot-state.json"), { active: true, mode: "autopilot", current_phase: "ralplan", started_at: "2026-07-22T00:00:00.000Z", updated_at: "2026-07-22T00:10:00.000Z", session_id: sessionId, thread_id: leaderThreadId, }); await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { [sessionId]: { session_id: sessionId, leader_thread_id: leaderThreadId, updated_at: "2026-07-22T00:10:00.000Z", threads: { [leaderThreadId]: { thread_id: leaderThreadId, kind: "leader", first_seen_at: "2026-07-22T00:00:00.000Z", last_seen_at: "2026-07-22T00:10:00.000Z", turn_count: 1, }, }, }, }, }); return { sessionId, leaderThreadId, stateDir }; }; it("denies flattened collaboration tools under active ralplan without documented host authority (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("allows installed-role spawn_agent consensus delegation during active ralplan planning (#3451-B) (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("denies unknown typed roles dispatched through flattened spawn names (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("records native blockers from flattened collaboration spawn failures with the original tool name", async () => { const capacityCwd = await mkdtemp(join(tmpdir(), "omx-native-hook-flattened-collab-capacity-")); try { const result = await dispatchCodexNativeHook({ hook_event_name: "PostToolUse", cwd: capacityCwd, session_id: "sess-flattened-capacity", thread_id: "thread-flattened-capacity", turn_id: "turn-flattened-capacity", tool_name: "collaborationspawn_agent", tool_response: "collab spawn failed: agent thread limit reached", }, { cwd: capacityCwd }); assert.equal(result.outputJson, null); const blocker = JSON.parse( await readFile(join(capacityCwd, ".omx", "state", "native-subagent-capacity-blocker.json"), "utf-8"), ) as Record; assert.equal(blocker.tool_name, "collaborationspawn_agent"); assert.match(String(blocker.error_summary), /agent thread limit reached/); } finally { await rm(capacityCwd, { recursive: true, force: true }); } const supportCwd = await mkdtemp(join(tmpdir(), "omx-native-hook-flattened-collab-support-")); try { await dispatchCodexNativeHook({ hook_event_name: "PostToolUse", cwd: supportCwd, session_id: "sess-flattened-support", thread_id: "thread-flattened-support", turn_id: "turn-flattened-support", tool_name: "collaborationspawn_agent", tool_response: { error: "unknown tool: collaborationspawn_agent is unavailable" }, }, { cwd: supportCwd }); const blocker = JSON.parse( await readFile(join(supportCwd, ".omx", "state", "native-subagent-support.json"), "utf-8"), ) as Record; assert.equal(blocker.tool_name, "collaborationspawn_agent"); } finally { await rm(supportCwd, { recursive: true, force: true }); } }); it("does not persist support or capacity poison from successful flattened collaboration child output", async () => { for (const toolName of [ "collaborationspawn_agent", "collaborationlist_agents", "collaborationfollowup_task", "collaborationwait_agent", ]) { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-flattened-collab-success-")); try { await dispatchCodexNativeHook({ hook_event_name: "PostToolUse", cwd, session_id: "sess-flattened-success", thread_id: "thread-flattened-success", tool_name: toolName, tool_response: { success: true, status: "completed", output: "Child completed. Optional packed plugin was unavailable, unsupported, and not found.", }, }, { cwd }); const stateDir = join(cwd, ".omx", "state"); assert.equal(existsSync(join(stateDir, "native-subagent-support.json")), false, toolName); assert.equal(existsSync(join(stateDir, "native-subagent-capacity-blocker.json")), false, toolName); } finally { await rm(cwd, { recursive: true, force: true }); } } }); it("does not persist blockers from nested-only flattened collaboration lifecycle evidence", async () => { for (const toolName of ["collaboration.list_agents", "collaborationlist_agents"]) { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-collab-nested-lifecycle-")); try { await dispatchCodexNativeHook({ hook_event_name: "PostToolUse", cwd, session_id: "sess-nested-lifecycle", thread_id: "thread-nested-lifecycle", tool_name: toolName, tool_response: { agents: [ { agent_name: "/root", agent_status: "running" }, { agent_name: "/root/reviewer", agent_status: { completed: "Child report discusses an unavailable and unsupported optional adapter.", }, }, ], }, }, { cwd }); const stateDir = join(cwd, ".omx", "state"); assert.equal(existsSync(join(stateDir, "native-subagent-support.json")), false, toolName); assert.equal(existsSync(join(stateDir, "native-subagent-capacity-blocker.json")), false, toolName); } finally { await rm(cwd, { recursive: true, force: true }); } } }); it("blocks flattened close_agent cleanup after recent native subagent capacity exhaustion (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("allows session-scoped omx cancel under active ralplan without weakening state guards (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("does not persist native blockers from structured failures on unrelated tools", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-unrelated-structured-failure-")); try { await dispatchCodexNativeHook({ hook_event_name: "PostToolUse", cwd, tool_name: "Bash", tool_response: { success: false, status: "failed", error: "optional plugin is unsupported and unavailable", }, }, { cwd }); const stateDir = join(cwd, ".omx", "state"); assert.equal(existsSync(join(stateDir, "native-subagent-support.json")), false); assert.equal(existsSync(join(stateDir, "native-subagent-capacity-blocker.json")), false); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks close_agent cleanup after recent native subagent capacity exhaustion (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("emits hook-specific deny PreToolUse CLI stdout for close_agent capacity blocks (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("blocks parallel close_agent cleanup after recent native subagent capacity exhaustion (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("blocks parallel close_agent cleanup with flattened nested recipients after native capacity exhaustion (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("does not block close_agent without a recent native capacity blocker", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-subagent-capacity-close-no-blocker-")); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: "sess-subagent-capacity-close-no-blocker", thread_id: "thread-subagent-capacity-close-no-blocker", tool_name: "multi_agent_v1.close_agent", tool_input: { target: "completed-agent" }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not block close_agent when the native capacity blocker is expired", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-subagent-capacity-close-expired-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(stateDir, { recursive: true }); await writeJson(join(stateDir, "native-subagent-capacity-blocker.json"), { schema_version: 1, reason: "agent_thread_limit_reached", session_id: "sess-subagent-capacity-close-expired", error_summary: "agent thread limit reached", observed_at: "2026-06-20T00:00:00.000Z", expires_at: "2026-06-20T00:30:00.000Z", cwd, }); const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: "sess-subagent-capacity-close-expired", thread_id: "thread-subagent-capacity-close-expired", tool_name: "multi_agent_v1.close_agent", tool_input: { target: "completed-agent" }, }, { cwd }, ); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("warns on PreToolUse for vague sloppy fallback implementation framing", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-slop-warn-")); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-slop-warn", tool_input: { command: [ "cat > src/runtime.ts <<'EOF'", "export function loadRuntime() {", " // implement a quick hack fallback if it fails", " return process.env.RUNTIME || 'local';", "}", "EOF", ].join("\n"), }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal((result.outputJson as { decision?: string } | null)?.decision, undefined); assert.equal((result.outputJson as { hookSpecificOutput?: { hookEventName?: string } } | null)?.hookSpecificOutput?.hookEventName, "PreToolUse"); assert.match(JSON.stringify(result.outputJson), /don't make potential slop/); assert.match(JSON.stringify(result.outputJson), /architect/); assert.match(JSON.stringify(result.outputJson), /environment issue/); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not warn on PreToolUse for read-only fallback text inspection", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-slop-readonly-")); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-slop-readonly", tool_input: { command: "rg \"quick hack fallback if it fails\" src docs" }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("warns when a read-only command is chained before sloppy fallback writes", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-slop-chained-write-")); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-slop-chained-write", tool_input: { command: [ "rg foo src && cat > src/runtime.ts < { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-slop-grounded-")); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-slop-grounded", tool_input: { command: [ "cat > src/compat.ts <<'EOF'", "export function resolveCompatMode() {", " // temporary fallback because legacy compatibility needs fail-safe startup behavior", " return 'legacy';", "}", "// Tested: npm test", "EOF", ].join("\n"), }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks Stop for untracked non-Bash-style sloppy fallback source edits", async () => { const cwd = await initTempGitRepo("omx-native-hook-stop-slop-untracked-"); try { await mkdir(join(cwd, "src"), { recursive: true }); await writeFile( join(cwd, "src", "runtime.ts"), [ "export function loadRuntime() {", " // implement a quick hack fallback if it fails", " return process.env.RUNTIME || 'local';", "}", ].join("\n"), ); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-slop-untracked" }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal((result.outputJson as { decision?: string } | null)?.decision, "block"); assert.equal((result.outputJson as { stopReason?: string } | null)?.stopReason, "sloppy_fallback_diff_audit"); assert.match(JSON.stringify(result.outputJson), /src\/runtime\.ts/); assert.match(JSON.stringify(result.outputJson), /grounded design/); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("keeps blocking repeated Stop while sloppy fallback diff remains", async () => { const cwd = await initTempGitRepo("omx-native-hook-stop-slop-repeat-"); try { await mkdir(join(cwd, "src"), { recursive: true }); await writeFile( join(cwd, "src", "runtime.ts"), [ "export function loadRuntime() {", " // implement a quick hack fallback if it fails", " return process.env.RUNTIME || 'local';", "}", ].join("\n"), ); const payload = { hook_event_name: "Stop", cwd, session_id: "sess-stop-slop-repeat", turn_id: "turn-repeat" }; const first = await dispatchCodexNativeHook(payload, { cwd }); const repeated = await dispatchCodexNativeHook({ ...payload, stop_hook_active: true }, { cwd }); assert.equal((first.outputJson as { decision?: string } | null)?.decision, "block"); assert.equal((repeated.outputJson as { decision?: string } | null)?.decision, "block"); assert.equal((repeated.outputJson as { stopReason?: string } | null)?.stopReason, "sloppy_fallback_diff_audit"); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("fails open after repeated identical sloppy fallback findings exceed the repeat cap", async () => { const cwd = await initTempGitRepo("omx-native-hook-stop-slop-cap-"); try { await mkdir(join(cwd, "src"), { recursive: true }); await writeFile( join(cwd, "src", "runtime.ts"), [ "export function loadRuntime() {", " // implement a quick hack fallback if it fails", " return process.env.RUNTIME || 'local';", "}", ].join("\n"), ); const payload = { hook_event_name: "Stop", cwd, session_id: "sess-stop-slop-cap", turn_id: "turn-cap" }; const decisions: Array = []; for (let attempt = 0; attempt < 5; attempt += 1) { const result = await dispatchCodexNativeHook( attempt === 0 ? payload : { ...payload, stop_hook_active: true }, { cwd }, ); decisions.push((result.outputJson as { decision?: string } | null)?.decision ?? null); } assert.deepEqual(decisions, ["block", "block", "block", null, null]); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("resets the sloppy fallback repeat cap when findings change", async () => { const cwd = await initTempGitRepo("omx-native-hook-stop-slop-cap-reset-"); try { await mkdir(join(cwd, "src"), { recursive: true }); const sloppySource = (exportName: string) => [ `export function ${exportName}() {`, " // implement a quick hack fallback if it fails", " return process.env.RUNTIME || 'local';", "}", ].join("\n"); await writeFile(join(cwd, "src", "runtime.ts"), sloppySource("loadRuntime")); const payload = { hook_event_name: "Stop", cwd, session_id: "sess-stop-slop-cap-reset", turn_id: "turn-cap-reset" }; for (let attempt = 0; attempt < 5; attempt += 1) { await dispatchCodexNativeHook( attempt === 0 ? payload : { ...payload, stop_hook_active: true }, { cwd }, ); } const allowed = await dispatchCodexNativeHook({ ...payload, stop_hook_active: true }, { cwd }); assert.equal(allowed.outputJson, null); await writeFile(join(cwd, "src", "other.ts"), sloppySource("loadOther")); const blockedAgain = await dispatchCodexNativeHook({ ...payload, stop_hook_active: true }, { cwd }); assert.equal((blockedAgain.outputJson as { decision?: string } | null)?.decision, "block"); assert.equal((blockedAgain.outputJson as { stopReason?: string } | null)?.stopReason, "sloppy_fallback_diff_audit"); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("keeps the repeat count when identical findings move across staging states", async () => { const cwd = await initTempGitRepo("omx-native-hook-stop-slop-staging-"); try { await mkdir(join(cwd, "src"), { recursive: true }); await writeFile( join(cwd, "src", "runtime.ts"), [ "export function loadRuntime() {", " // implement a quick hack fallback if it fails", " return process.env.RUNTIME || 'local';", "}", ].join("\n"), ); const payload = { hook_event_name: "Stop", cwd, session_id: "sess-stop-slop-staging", turn_id: "turn-staging" }; const stop = (attempt: number) => dispatchCodexNativeHook(attempt === 0 ? payload : { ...payload, stop_hook_active: true }, { cwd }); const first = await stop(0); execFileSync("git", ["add", "src/runtime.ts"], { cwd, stdio: "ignore" }); const staged = await stop(1); execFileSync("git", ["reset", "-q", "--", "src/runtime.ts"], { cwd, stdio: "ignore" }); const unstagedAgain = await stop(2); const afterCap = await stop(3); assert.equal((first.outputJson as { decision?: string } | null)?.decision, "block"); assert.equal((staged.outputJson as { decision?: string } | null)?.decision, "block"); assert.equal((unstagedAgain.outputJson as { decision?: string } | null)?.decision, "block"); assert.equal(afterCap.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("keeps the repeat count when staging a subset reorders identical findings", async () => { const cwd = await initTempGitRepo("omx-native-hook-stop-slop-subset-"); try { await mkdir(join(cwd, "src"), { recursive: true }); const sloppySource = (exportName: string) => [ `export function ${exportName}() {`, " // implement a quick hack fallback if it fails", " return process.env.RUNTIME || 'local';", "}", ].join("\n"); await writeFile(join(cwd, "src", "alpha.ts"), sloppySource("loadAlpha")); await writeFile(join(cwd, "src", "beta.ts"), sloppySource("loadBeta")); const payload = { hook_event_name: "Stop", cwd, session_id: "sess-stop-slop-subset", turn_id: "turn-subset" }; const stop = (attempt: number) => dispatchCodexNativeHook(attempt === 0 ? payload : { ...payload, stop_hook_active: true }, { cwd }); const first = await stop(0); // Staging only beta reorders the raw finding list (staged findings come // first), but the fingerprint must stay identical for the same set. execFileSync("git", ["add", "src/beta.ts"], { cwd, stdio: "ignore" }); const subsetStaged = await stop(1); execFileSync("git", ["add", "src/alpha.ts"], { cwd, stdio: "ignore" }); const allStaged = await stop(2); const afterCap = await stop(3); assert.equal((first.outputJson as { decision?: string } | null)?.decision, "block"); assert.equal((subsetStaged.outputJson as { decision?: string } | null)?.decision, "block"); assert.equal((allStaged.outputJson as { decision?: string } | null)?.decision, "block"); assert.equal(afterCap.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("resets the repeat cap when a finding moves to a new line in the same file", async () => { const cwd = await initTempGitRepo("omx-native-hook-stop-slop-moveline-"); try { await mkdir(join(cwd, "src"), { recursive: true }); const sloppySource = (headerLines: string[]) => [ ...headerLines, "export function loadRuntime() {", " // implement a quick hack fallback if it fails", " return process.env.RUNTIME || 'local';", "}", ].join("\n"); await writeFile(join(cwd, "src", "runtime.ts"), sloppySource([])); const payload = { hook_event_name: "Stop", cwd, session_id: "sess-stop-slop-moveline", turn_id: "turn-moveline" }; const stop = (attempt: number) => dispatchCodexNativeHook(attempt === 0 ? payload : { ...payload, stop_hook_active: true }, { cwd }); for (let attempt = 0; attempt < 4; attempt += 1) { await stop(attempt); } const allowed = await dispatchCodexNativeHook({ ...payload, stop_hook_active: true }, { cwd }); assert.equal(allowed.outputJson, null); // The identical text relocated to another line is a new finding set and // must block again rather than inherit the exhausted repeat cap. await writeFile(join(cwd, "src", "runtime.ts"), sloppySource(["// header comment"])); const blockedAgain = await dispatchCodexNativeHook({ ...payload, stop_hook_active: true }, { cwd }); assert.equal((blockedAgain.outputJson as { decision?: string } | null)?.decision, "block"); assert.equal((blockedAgain.outputJson as { stopReason?: string } | null)?.stopReason, "sloppy_fallback_diff_audit"); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("skips the sloppy fallback diff audit when OMX_NATIVE_STOP_SLOPPY_FALLBACK_AUDIT is off", async () => { const cwd = await initTempGitRepo("omx-native-hook-stop-slop-disabled-"); const previousValue = process.env.OMX_NATIVE_STOP_SLOPPY_FALLBACK_AUDIT; process.env.OMX_NATIVE_STOP_SLOPPY_FALLBACK_AUDIT = "off"; try { await mkdir(join(cwd, "src"), { recursive: true }); await writeFile( join(cwd, "src", "runtime.ts"), [ "export function loadRuntime() {", " // implement a quick hack fallback if it fails", " return process.env.RUNTIME || 'local';", "}", ].join("\n"), ); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-slop-disabled" }, { cwd }, ); assert.equal(result.outputJson, null); } finally { if (previousValue === undefined) delete process.env.OMX_NATIVE_STOP_SLOPPY_FALLBACK_AUDIT; else process.env.OMX_NATIVE_STOP_SLOPPY_FALLBACK_AUDIT = previousValue; await rm(cwd, { recursive: true, force: true }); } }); it("ignores untracked sloppy fallback files last written before the session transcript", async (t) => { const cwd = await initTempGitRepo("omx-native-hook-stop-slop-preexisting-"); try { await mkdir(join(cwd, "src"), { recursive: true }); await writeFile( join(cwd, "src", "runtime.ts"), [ "export function loadRuntime() {", " // implement a quick hack fallback if it fails", " return process.env.RUNTIME || 'local';", "}", ].join("\n"), ); // The file must genuinely predate the session: ctime/birth time cannot // be backdated with utimes, so create the transcript afterwards. await new Promise((resolve) => setTimeout(resolve, 25)); const transcriptPath = join(cwd, "transcript.jsonl"); await writeFile(transcriptPath, "{}\n"); // A real session appends to its transcript; the append makes the // transcript's ctime newer than its immutable birth time, which is what // lets the audit trust that birth time as the session start. await new Promise((resolve) => setTimeout(resolve, 25)); await appendFile(transcriptPath, "{}\n"); const { birthtimeMs, ctimeMs } = statSync(transcriptPath); if (!(birthtimeMs > 0) || birthtimeMs >= ctimeMs) { t.skip("immutable file birth time is not available on this platform"); return; } const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-slop-preexisting", transcript_path: transcriptPath }, { cwd }, ); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("trusts only immutable transcript birth times as the sloppy fallback session start", () => { assert.equal(isSloppyFallbackTranscriptStartUsable(50, 100), true); assert.equal(isSloppyFallbackTranscriptStartUsable(100, 100), false); assert.equal(isSloppyFallbackTranscriptStartUsable(0, 100), false); assert.equal(isSloppyFallbackTranscriptStartUsable(100, 50), false); assert.equal(isSloppyFallbackTranscriptStartUsable(Number.NaN, 100), false); assert.equal(isSloppyFallbackTranscriptStartUsable(50, Number.NaN), false); }); it("audits untracked files when the transcript birth time is indistinguishable from ctime", async (t) => { const cwd = await initTempGitRepo("omx-native-hook-stop-slop-ctimebacked-"); try { await mkdir(join(cwd, "src"), { recursive: true }); await writeFile( join(cwd, "src", "runtime.ts"), [ "export function loadRuntime() {", " // implement a quick hack fallback if it fails", " return process.env.RUNTIME || 'local';", "}", ].join("\n"), ); await new Promise((resolve) => setTimeout(resolve, 25)); const transcriptPath = join(cwd, "transcript.jsonl"); await writeFile(transcriptPath, "{}\n"); // A freshly created transcript has birth time == ctime, mimicking // filesystems where Node reports a mutable ctime fallback as birthtime; // the audit must treat that as unavailable rather than trust it. const { birthtimeMs, ctimeMs } = statSync(transcriptPath); if (!(birthtimeMs > 0) || birthtimeMs < ctimeMs) { t.skip("this platform reports an immutable birth time distinct from ctime"); return; } const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-slop-ctimebacked", transcript_path: transcriptPath }, { cwd }, ); assert.equal((result.outputJson as { decision?: string } | null)?.decision, "block"); assert.equal((result.outputJson as { stopReason?: string } | null)?.stopReason, "sloppy_fallback_diff_audit"); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("still blocks untracked sloppy fallback files materialized in-session with preserved mtime", async (t) => { const cwd = await initTempGitRepo("omx-native-hook-stop-slop-cpreserve-"); try { const seedPath = join(cwd, "seed.ts"); await writeFile( seedPath, [ "export function loadRuntime() {", " // implement a quick hack fallback if it fails", " return process.env.RUNTIME || 'local';", "}", ].join("\n"), ); const oldTime = new Date(Date.now() - 10 * 60_000); await utimes(seedPath, oldTime, oldTime); await new Promise((resolve) => setTimeout(resolve, 25)); const transcriptPath = join(cwd, "transcript.jsonl"); await writeFile(transcriptPath, "{}\n"); const { birthtimeMs } = statSync(transcriptPath); if (!(birthtimeMs > 0)) { t.skip("file birth time is not available on this platform"); return; } await mkdir(join(cwd, "src"), { recursive: true }); execFileSync("cp", ["-p", seedPath, join(cwd, "src", "runtime.ts")], { stdio: "ignore" }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-slop-cpreserve", transcript_path: transcriptPath }, { cwd }, ); assert.equal((result.outputJson as { decision?: string } | null)?.decision, "block"); assert.equal((result.outputJson as { stopReason?: string } | null)?.stopReason, "sloppy_fallback_diff_audit"); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("still blocks untracked sloppy fallback files reachable through an in-session symlink to an older target", async (t) => { const cwd = await initTempGitRepo("omx-native-hook-stop-slop-symlink-"); try { const seedPath = join(cwd, "seed.ts"); await writeFile( seedPath, [ "export function loadRuntime() {", " // implement a quick hack fallback if it fails", " return process.env.RUNTIME || 'local';", "}", ].join("\n"), ); const oldTime = new Date(Date.now() - 10 * 60_000); await utimes(seedPath, oldTime, oldTime); await new Promise((resolve) => setTimeout(resolve, 25)); const transcriptPath = join(cwd, "transcript.jsonl"); await writeFile(transcriptPath, "{}\n"); const { birthtimeMs } = statSync(transcriptPath); if (!(birthtimeMs > 0)) { t.skip("file birth time is not available on this platform"); return; } await mkdir(join(cwd, "src"), { recursive: true }); await symlink(seedPath, join(cwd, "src", "runtime.ts")); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-slop-symlink", transcript_path: transcriptPath }, { cwd }, ); assert.equal((result.outputJson as { decision?: string } | null)?.decision, "block"); assert.equal((result.outputJson as { stopReason?: string } | null)?.stopReason, "sloppy_fallback_diff_audit"); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("still blocks when a pre-session symlink's target is rewritten during the session", async (t) => { const cwd = await initTempGitRepo("omx-native-hook-stop-slop-targetrewrite-"); try { // A non-auditable target extension keeps the symlink path as the only // route to the flagged content. const targetPath = join(cwd, "seed-data.txt"); await writeFile(targetPath, "export const clean = true;\n"); await mkdir(join(cwd, "src"), { recursive: true }); await symlink(targetPath, join(cwd, "src", "runtime.ts")); await new Promise((resolve) => setTimeout(resolve, 25)); const transcriptPath = join(cwd, "transcript.jsonl"); await writeFile(transcriptPath, "{}\n"); await new Promise((resolve) => setTimeout(resolve, 25)); await appendFile(transcriptPath, "{}\n"); const { birthtimeMs, ctimeMs } = statSync(transcriptPath); if (!(birthtimeMs > 0) || birthtimeMs >= ctimeMs) { t.skip("immutable file birth time is not available on this platform"); return; } await writeFile( targetPath, [ "export function loadRuntime() {", " // implement a quick hack fallback if it fails", " return process.env.RUNTIME || 'local';", "}", ].join("\n"), ); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-slop-targetrewrite", transcript_path: transcriptPath }, { cwd }, ); assert.equal((result.outputJson as { decision?: string } | null)?.decision, "block"); assert.equal((result.outputJson as { stopReason?: string } | null)?.stopReason, "sloppy_fallback_diff_audit"); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("still blocks untracked sloppy fallback files written after the session transcript", async (t) => { const cwd = await initTempGitRepo("omx-native-hook-stop-slop-inturn-"); try { const transcriptPath = join(cwd, "transcript.jsonl"); await writeFile(transcriptPath, "{}\n"); const { birthtimeMs } = statSync(transcriptPath); if (!(birthtimeMs > 0)) { t.skip("file birth time is not available on this platform"); return; } await mkdir(join(cwd, "src"), { recursive: true }); await writeFile( join(cwd, "src", "runtime.ts"), [ "export function loadRuntime() {", " // implement a quick hack fallback if it fails", " return process.env.RUNTIME || 'local';", "}", ].join("\n"), ); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-slop-inturn", transcript_path: transcriptPath }, { cwd }, ); assert.equal((result.outputJson as { decision?: string } | null)?.decision, "block"); assert.equal((result.outputJson as { stopReason?: string } | null)?.stopReason, "sloppy_fallback_diff_audit"); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks Stop for unstaged tracked sloppy fallback source edits", async () => { const cwd = await initTempGitRepo("omx-native-hook-stop-slop-unstaged-"); try { await mkdir(join(cwd, "src"), { recursive: true }); await writeFile(join(cwd, "src", "runtime.ts"), "export const runtime = 'base';\n"); execFileSync("git", ["add", "src/runtime.ts"], { cwd, stdio: "ignore" }); execFileSync("git", ["commit", "-m", "initial"], { cwd, stdio: "ignore" }); await writeFile( join(cwd, "src", "runtime.ts"), [ "export function loadRuntime() {", " // just bypass fallback if it fails", " return process.env.RUNTIME || 'local';", "}", ].join("\n"), ); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-slop-unstaged" }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal((result.outputJson as { decision?: string } | null)?.decision, "block"); assert.match(JSON.stringify(result.outputJson), /unstaged/); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks Stop from a subdirectory cwd for untracked sloppy source elsewhere", async () => { const cwd = await initTempGitRepo("omx-native-hook-stop-slop-subdir-"); try { await mkdir(join(cwd, "src", "nested"), { recursive: true }); await writeFile(join(cwd, "src", "nested", "anchor.ts"), "export const anchor = true;\n"); await writeFile( join(cwd, "src", "runtime.ts"), [ "export function loadRuntime() {", " // implement a quick hack fallback if it fails", " return process.env.RUNTIME || 'local';", "}", ].join("\n"), ); const subdir = join(cwd, "src", "nested"); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd: subdir, session_id: "sess-stop-slop-subdir" }, { cwd: subdir }, ); assert.equal(result.omxEventName, "stop"); assert.equal((result.outputJson as { decision?: string } | null)?.decision, "block"); assert.match(JSON.stringify(result.outputJson), /src\/runtime\.ts/); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks Stop for staged sloppy fallback source edits", async () => { const cwd = await initTempGitRepo("omx-native-hook-stop-slop-staged-"); try { await mkdir(join(cwd, "src"), { recursive: true }); await writeFile(join(cwd, "src", "runtime.ts"), "export const runtime = 'base';\n"); execFileSync("git", ["add", "src/runtime.ts"], { cwd, stdio: "ignore" }); execFileSync("git", ["commit", "-m", "initial"], { cwd, stdio: "ignore" }); await writeFile( join(cwd, "src", "runtime.ts"), [ "export function loadRuntime() {", " // temporary workaround fallback if it fails", " return process.env.RUNTIME || 'local';", "}", ].join("\n"), ); execFileSync("git", ["add", "src/runtime.ts"], { cwd, stdio: "ignore" }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-slop-staged" }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal((result.outputJson as { decision?: string } | null)?.decision, "block"); assert.match(JSON.stringify(result.outputJson), /staged/); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not block Stop for grounded compatibility fallback source edits", async () => { const cwd = await initTempGitRepo("omx-native-hook-stop-slop-grounded-"); try { await mkdir(join(cwd, "src"), { recursive: true }); await writeFile( join(cwd, "src", "compat.ts"), [ "export function resolveCompatMode() {", " // temporary fallback for legacy startup", " // compatibility fail-safe tested by regression coverage", " return 'legacy';", "}", ].join("\n"), ); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-slop-grounded" }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not block Stop when existing nearby source context grounds a new fallback line", async () => { const cwd = await initTempGitRepo("omx-native-hook-stop-slop-existing-ground-"); try { await mkdir(join(cwd, "src"), { recursive: true }); await writeFile( join(cwd, "src", "compat.ts"), [ "export function resolveCompatMode() {", " // compatibility fail-safe tested by regression coverage", " return 'legacy';", "}", ].join("\n"), ); execFileSync("git", ["add", "src/compat.ts"], { cwd, stdio: "ignore" }); execFileSync("git", ["commit", "-m", "initial"], { cwd, stdio: "ignore" }); await writeFile( join(cwd, "src", "compat.ts"), [ "export function resolveCompatMode() {", " // compatibility fail-safe tested by regression coverage", " // temporary fallback if it fails", " return 'legacy';", "}", ].join("\n"), ); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-slop-existing-ground" }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not block Stop for source-adjacent test file fallback wording", async () => { const cwd = await initTempGitRepo("omx-native-hook-stop-slop-test-file-"); try { await mkdir(join(cwd, "src"), { recursive: true }); await writeFile( join(cwd, "src", "runtime.test.ts"), "it('documents no quick hack fallback if it fails', () => {});\n", ); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-slop-test-file" }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not block Stop for docs-only fallback wording", async () => { const cwd = await initTempGitRepo("omx-native-hook-stop-slop-docs-"); try { await mkdir(join(cwd, "docs"), { recursive: true }); await writeFile( join(cwd, "docs", "notes.md"), "Do not implement a quick hack fallback if it fails.\n", ); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-slop-docs" }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("keeps git commit Lore enforcement ahead of sloppy fallback advisory", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-slop-git-priority-")); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-slop-git-priority", tool_input: { command: 'OMX_LORE_COMMIT_GUARD=1 git commit -m "quick hack fallback if it fails"' }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal((result.outputJson as { decision?: string } | null)?.decision, "block"); assert.match(JSON.stringify(result.outputJson), /Lore protocol/); assert.doesNotMatch(JSON.stringify(result.outputJson), /don't make potential slop/); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks PreToolUse git commit with supported response shape when the inline message is not Lore-compliant", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-git-commit-invalid-")); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-git-commit-invalid", tool_input: { command: 'OMX_LORE_COMMIT_GUARD=1 git commit -m "fix tests"' }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.deepEqual(result.outputJson, { decision: "block", reason: "git commit is blocked until the inline commit message satisfies the Lore format and includes the required OmX co-author trailer.", hookSpecificOutput: { hookEventName: "PreToolUse", }, systemMessage: [ "git commit is blocked until the inline commit message follows the Lore protocol and includes `Co-authored-by: OmX `.", "- Add a blank line after the subject before the narrative body.", "- Add a narrative body paragraph explaining the decision context.", "- Add at least one Lore trailer such as `Constraint:`, `Confidence:`, or `Tested:`.", "- Add the required co-author trailer: `Co-authored-by: OmX `.", ].join("\n"), }); const hookSpecificOutput = (result.outputJson as { hookSpecificOutput?: Record }) .hookSpecificOutput ?? {}; assert.equal("additionalContext" in hookSpecificOutput, false); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks PreToolUse git commit when process env explicitly enables the Lore commit guard", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-git-commit-lore-env-enabled-")); const original = process.env.OMX_LORE_COMMIT_GUARD; try { process.env.OMX_LORE_COMMIT_GUARD = "1"; const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-git-commit-lore-env-enabled", tool_input: { command: 'git commit -m "fix tests"' }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal((result.outputJson as { decision?: string } | null)?.decision, "block"); assert.match(JSON.stringify(result.outputJson), /Lore protocol/); } finally { if (original === undefined) delete process.env.OMX_LORE_COMMIT_GUARD; else process.env.OMX_LORE_COMMIT_GUARD = original; await rm(cwd, { recursive: true, force: true }); } }); it("allows non-Lore git commit messages when the Lore commit guard is disabled by default", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-git-commit-lore-disabled-")); const original = process.env.OMX_LORE_COMMIT_GUARD; try { delete process.env.OMX_LORE_COMMIT_GUARD; const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-git-commit-lore-disabled", tool_input: { command: 'git commit -m "fix: use conventional commit"' }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal(result.outputJson, null); } finally { if (original === undefined) delete process.env.OMX_LORE_COMMIT_GUARD; else process.env.OMX_LORE_COMMIT_GUARD = original; await rm(cwd, { recursive: true, force: true }); } }); it("blocks non-Lore git commit messages when the Lore commit guard is enabled in CODEX_HOME config.toml", async () => { await withLoreGuardConfig("1", "config-enabled", async (cwd) => { const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-git-commit-lore-config-enabled", tool_input: { command: 'git commit -m "fix: conventional"' }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal((result.outputJson as { decision?: string } | null)?.decision, "block"); assert.match(JSON.stringify(result.outputJson), /Lore protocol/); }); }); it("allows non-Lore git commit messages when the Lore commit guard is disabled in CODEX_HOME config.toml", async () => { await withLoreGuardConfig("0", "config-disabled", async (cwd) => { const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-git-commit-lore-config-disabled", tool_input: { command: 'git commit -m "fix: use conventional commit"' }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal(result.outputJson, null); }); }); it("lets inline Lore commit guard values override a disabled CODEX_HOME config.toml", async () => { await withLoreGuardConfig("0", "config-inline-enabled", async (cwd) => { const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-git-commit-lore-config-inline-enabled", tool_input: { command: 'OMX_LORE_COMMIT_GUARD=1 git commit -m "fix: conventional"' }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal((result.outputJson as { decision?: string } | null)?.decision, "block"); assert.match(JSON.stringify(result.outputJson), /Lore protocol/); }); }); it("restores default-off Lore guard when env -u removes a disabled CODEX_HOME config source", async () => { await withLoreGuardConfig("0", "config-codex-home-unset", async (cwd) => { const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-git-commit-lore-config-codex-home-unset", tool_input: { command: 'env -u CODEX_HOME git commit -m "fix: conventional"' }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal(result.outputJson, null); }); }); it("allows non-Lore git commit messages when the Lore commit guard is disabled inline", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-git-commit-lore-inline-disabled-")); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-git-commit-lore-inline-disabled", tool_input: { command: 'OMX_LORE_COMMIT_GUARD=0 git commit -m "fix: conventional"' }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("allows inline disabled guard to override an enabled process env", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-git-commit-lore-inline-override-disabled-")); const original = process.env.OMX_LORE_COMMIT_GUARD; try { process.env.OMX_LORE_COMMIT_GUARD = "1"; const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-git-commit-lore-inline-override-disabled", tool_input: { command: 'OMX_LORE_COMMIT_GUARD=0 git commit -m "fix: conventional"' }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal(result.outputJson, null); } finally { if (original === undefined) delete process.env.OMX_LORE_COMMIT_GUARD; else process.env.OMX_LORE_COMMIT_GUARD = original; await rm(cwd, { recursive: true, force: true }); } }); it("does not treat newline-separated Lore guard assignment as inline git commit opt-in", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-git-commit-lore-newline-assignment-")); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-git-commit-lore-newline-assignment", tool_input: { command: 'OMX_LORE_COMMIT_GUARD=1\ngit commit -m "fix: conventional"' }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("restores default-off Lore guard when env -u unsets a config.toml fallback", async () => { await withLoreGuardConfig("1", "config-env-unset", async (cwd) => { const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-git-commit-lore-config-env-unset", tool_input: { command: 'env -u OMX_LORE_COMMIT_GUARD git commit -m "fix: conventional"' }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal(result.outputJson, null); }); }); it("restores default-off Lore guard when env -u unsets an enabled process env", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-git-commit-lore-env-unset-")); const original = process.env.OMX_LORE_COMMIT_GUARD; try { process.env.OMX_LORE_COMMIT_GUARD = "1"; const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-git-commit-lore-env-unset", tool_input: { command: 'env -u OMX_LORE_COMMIT_GUARD git commit -m "fix: conventional"' }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal(result.outputJson, null); } finally { if (original === undefined) delete process.env.OMX_LORE_COMMIT_GUARD; else process.env.OMX_LORE_COMMIT_GUARD = original; await rm(cwd, { recursive: true, force: true }); } }); it("restores default-off Lore guard when env -i clears an enabled process env", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-git-commit-lore-env-ignore-")); const original = process.env.OMX_LORE_COMMIT_GUARD; try { process.env.OMX_LORE_COMMIT_GUARD = "1"; const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-git-commit-lore-env-ignore", tool_input: { command: 'env -i PATH=/usr/bin git commit -m "fix: conventional"' }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal(result.outputJson, null); } finally { if (original === undefined) delete process.env.OMX_LORE_COMMIT_GUARD; else process.env.OMX_LORE_COMMIT_GUARD = original; await rm(cwd, { recursive: true, force: true }); } }); it("keeps Lore commit enforcement disabled for unknown inline guard values", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-git-commit-lore-inline-unknown-")); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-git-commit-lore-inline-unknown", tool_input: { command: 'OMX_LORE_COMMIT_GUARD=maybe git commit -m "fix: conventional"' }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("treats Lore commit guard disabled values as trim and case tolerant", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-git-commit-lore-off-")); const original = process.env.OMX_LORE_COMMIT_GUARD; try { process.env.OMX_LORE_COMMIT_GUARD = " OFF "; const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-git-commit-lore-off", tool_input: { command: 'git commit -m "chore: conventional commit"' }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal(result.outputJson, null); } finally { if (original === undefined) delete process.env.OMX_LORE_COMMIT_GUARD; else process.env.OMX_LORE_COMMIT_GUARD = original; await rm(cwd, { recursive: true, force: true }); } }); it("keeps Lore commit enforcement disabled for unknown guard values", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-git-commit-lore-unknown-")); const original = process.env.OMX_LORE_COMMIT_GUARD; try { process.env.OMX_LORE_COMMIT_GUARD = "maybe"; const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-git-commit-lore-unknown", tool_input: { command: 'git commit -m "fix tests"' }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal(result.outputJson, null); } finally { if (original === undefined) delete process.env.OMX_LORE_COMMIT_GUARD; else process.env.OMX_LORE_COMMIT_GUARD = original; await rm(cwd, { recursive: true, force: true }); } }); it("continues to later PreToolUse checks when Lore commit guard is disabled", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-lore-disabled-destructive-")); const original = process.env.OMX_LORE_COMMIT_GUARD; try { process.env.OMX_LORE_COMMIT_GUARD = "false"; const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-lore-disabled-destructive", tool_input: { command: "rm -rf dist" }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.doesNotMatch(JSON.stringify(result.outputJson), /Lore protocol/); assert.match(JSON.stringify(result.outputJson), /Destructive Bash command detected/); } finally { if (original === undefined) delete process.env.OMX_LORE_COMMIT_GUARD; else process.env.OMX_LORE_COMMIT_GUARD = original; await rm(cwd, { recursive: true, force: true }); } }); it("stays silent on PreToolUse for `git help commit`", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-git-help-commit-")); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-git-help-commit", tool_input: { command: "git help commit" }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("stays silent on PreToolUse for `git config alias.ci commit`", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-git-config-alias-commit-")); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-git-config-alias-commit", tool_input: { command: "git config alias.ci commit" }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("stays silent on PreToolUse for `git tag commit`", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-git-tag-commit-")); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-git-tag-commit", tool_input: { command: "git tag commit" }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks PreToolUse env-prefixed git commit when the inline message is not Lore-compliant", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-git-commit-env-invalid-")); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-git-commit-env-invalid", tool_input: { command: 'OMX_LORE_COMMIT_GUARD=1 HUSKY=0 git commit -m "fix tests"' }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.deepEqual(result.outputJson, { decision: "block", reason: "git commit is blocked until the inline commit message satisfies the Lore format and includes the required OmX co-author trailer.", hookSpecificOutput: { hookEventName: "PreToolUse", }, systemMessage: [ "git commit is blocked until the inline commit message follows the Lore protocol and includes `Co-authored-by: OmX `.", "- Add a blank line after the subject before the narrative body.", "- Add a narrative body paragraph explaining the decision context.", "- Add at least one Lore trailer such as `Constraint:`, `Confidence:`, or `Tested:`.", "- Add the required co-author trailer: `Co-authored-by: OmX `.", ].join("\n"), }); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks PreToolUse git commit when git options appear before the real commit subcommand", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-git-commit-option-invalid-")); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-git-commit-option-invalid", tool_input: { command: 'OMX_LORE_COMMIT_GUARD=1 git -c core.editor=true commit -m "fix tests"' }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.deepEqual(result.outputJson, { decision: "block", reason: "git commit is blocked until the inline commit message satisfies the Lore format and includes the required OmX co-author trailer.", hookSpecificOutput: { hookEventName: "PreToolUse", }, systemMessage: [ "git commit is blocked until the inline commit message follows the Lore protocol and includes `Co-authored-by: OmX `.", "- Add a blank line after the subject before the narrative body.", "- Add a narrative body paragraph explaining the decision context.", "- Add at least one Lore trailer such as `Constraint:`, `Confidence:`, or `Tested:`.", "- Add the required co-author trailer: `Co-authored-by: OmX `.", ].join("\n"), }); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks PreToolUse env wrapper-prefixed git.exe commit when the inline message is not Lore-compliant", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-git-exe-commit-env-wrapper-invalid-")); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-git-exe-commit-env-wrapper-invalid", tool_input: { command: 'env OMX_LORE_COMMIT_GUARD=1 git.exe commit -m "fix tests"' }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.deepEqual(result.outputJson, { decision: "block", reason: "git commit is blocked until the inline commit message satisfies the Lore format and includes the required OmX co-author trailer.", hookSpecificOutput: { hookEventName: "PreToolUse", }, systemMessage: [ "git commit is blocked until the inline commit message follows the Lore protocol and includes `Co-authored-by: OmX `.", "- Add a blank line after the subject before the narrative body.", "- Add a narrative body paragraph explaining the decision context.", "- Add at least one Lore trailer such as `Constraint:`, `Confidence:`, or `Tested:`.", "- Add the required co-author trailer: `Co-authored-by: OmX `.", ].join("\n"), }); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks PreToolUse git.exe commit when the inline message is not Lore-compliant", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-git-exe-commit-invalid-")); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-git-exe-commit-invalid", tool_input: { command: 'OMX_LORE_COMMIT_GUARD=1 git.exe commit -m "fix tests"' }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.deepEqual(result.outputJson, { decision: "block", reason: "git commit is blocked until the inline commit message satisfies the Lore format and includes the required OmX co-author trailer.", hookSpecificOutput: { hookEventName: "PreToolUse", }, systemMessage: [ "git commit is blocked until the inline commit message follows the Lore protocol and includes `Co-authored-by: OmX `.", "- Add a blank line after the subject before the narrative body.", "- Add a narrative body paragraph explaining the decision context.", "- Add at least one Lore trailer such as `Constraint:`, `Confidence:`, or `Tested:`.", "- Add the required co-author trailer: `Co-authored-by: OmX `.", ].join("\n"), }); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks PreToolUse env flag wrapper-prefixed git.exe commit when the inline message is not Lore-compliant", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-git-exe-commit-env-flag-wrapper-invalid-")); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-git-exe-commit-env-flag-wrapper-invalid", tool_input: { command: 'env -i PATH=/usr/bin OMX_LORE_COMMIT_GUARD=1 git.exe commit -m "fix tests"' }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.deepEqual(result.outputJson, { decision: "block", reason: "git commit is blocked until the inline commit message satisfies the Lore format and includes the required OmX co-author trailer.", hookSpecificOutput: { hookEventName: "PreToolUse", }, systemMessage: [ "git commit is blocked until the inline commit message follows the Lore protocol and includes `Co-authored-by: OmX `.", "- Add a blank line after the subject before the narrative body.", "- Add a narrative body paragraph explaining the decision context.", "- Add at least one Lore trailer such as `Constraint:`, `Confidence:`, or `Tested:`.", "- Add the required co-author trailer: `Co-authored-by: OmX `.", ].join("\n"), }); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks PreToolUse env value-taking wrapper-prefixed git.exe commit when the inline message is not Lore-compliant", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-git-exe-commit-env-value-wrapper-invalid-")); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-git-exe-commit-env-value-wrapper-invalid", tool_input: { command: 'env -u FOO OMX_LORE_COMMIT_GUARD=1 git.exe commit -m "fix tests"' }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.deepEqual(result.outputJson, { decision: "block", reason: "git commit is blocked until the inline commit message satisfies the Lore format and includes the required OmX co-author trailer.", hookSpecificOutput: { hookEventName: "PreToolUse", }, systemMessage: [ "git commit is blocked until the inline commit message follows the Lore protocol and includes `Co-authored-by: OmX `.", "- Add a blank line after the subject before the narrative body.", "- Add a narrative body paragraph explaining the decision context.", "- Add at least one Lore trailer such as `Constraint:`, `Confidence:`, or `Tested:`.", "- Add the required co-author trailer: `Co-authored-by: OmX `.", ].join("\n"), }); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks PreToolUse path-qualified Windows git.exe commit when the inline message is not Lore-compliant", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-git-exe-commit-windows-path-invalid-")); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-git-exe-commit-windows-path-invalid", tool_input: { command: 'OMX_LORE_COMMIT_GUARD=1 "C:/Program Files/Git/cmd/git.exe" commit -m "fix tests"' }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.deepEqual(result.outputJson, { decision: "block", reason: "git commit is blocked until the inline commit message satisfies the Lore format and includes the required OmX co-author trailer.", hookSpecificOutput: { hookEventName: "PreToolUse", }, systemMessage: [ "git commit is blocked until the inline commit message follows the Lore protocol and includes `Co-authored-by: OmX `.", "- Add a blank line after the subject before the narrative body.", "- Add a narrative body paragraph explaining the decision context.", "- Add at least one Lore trailer such as `Constraint:`, `Confidence:`, or `Tested:`.", "- Add the required co-author trailer: `Co-authored-by: OmX `.", ].join("\n"), }); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks PreToolUse quoted backslash Windows git.exe commit when the inline message is not Lore-compliant", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-git-exe-commit-windows-backslash-path-invalid-")); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-git-exe-commit-windows-backslash-path-invalid", tool_input: { command: 'OMX_LORE_COMMIT_GUARD=1 "C:\\Program Files\\Git\\cmd\\git.exe" commit -m "fix tests"' }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.deepEqual(result.outputJson, { decision: "block", reason: "git commit is blocked until the inline commit message satisfies the Lore format and includes the required OmX co-author trailer.", hookSpecificOutput: { hookEventName: "PreToolUse", }, systemMessage: [ "git commit is blocked until the inline commit message follows the Lore protocol and includes `Co-authored-by: OmX `.", "- Add a blank line after the subject before the narrative body.", "- Add a narrative body paragraph explaining the decision context.", "- Add at least one Lore trailer such as `Constraint:`, `Confidence:`, or `Tested:`.", "- Add the required co-author trailer: `Co-authored-by: OmX `.", ].join("\n"), }); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks PreToolUse path-qualified git commit when the inline message is not Lore-compliant", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-git-commit-path-invalid-")); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-git-commit-path-invalid", tool_input: { command: 'OMX_LORE_COMMIT_GUARD=1 /usr/bin/git commit -m "fix tests"' }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.deepEqual(result.outputJson, { decision: "block", reason: "git commit is blocked until the inline commit message satisfies the Lore format and includes the required OmX co-author trailer.", hookSpecificOutput: { hookEventName: "PreToolUse", }, systemMessage: [ "git commit is blocked until the inline commit message follows the Lore protocol and includes `Co-authored-by: OmX `.", "- Add a blank line after the subject before the narrative body.", "- Add a narrative body paragraph explaining the decision context.", "- Add at least one Lore trailer such as `Constraint:`, `Confidence:`, or `Tested:`.", "- Add the required co-author trailer: `Co-authored-by: OmX `.", ].join("\n"), }); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks PreToolUse git commit when the message comes from an external source", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-git-commit-file-")); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-git-commit-file", tool_input: { command: "OMX_LORE_COMMIT_GUARD=1 git commit -F .git/COMMIT_EDITMSG" }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.deepEqual(result.outputJson, { decision: "block", reason: "git commit is blocked until the inline commit message satisfies the Lore format and includes the required OmX co-author trailer.", hookSpecificOutput: { hookEventName: "PreToolUse", }, systemMessage: [ "git commit is blocked until the inline commit message follows the Lore protocol and includes `Co-authored-by: OmX `.", "- Use inline `git commit -m ...` paragraphs for Lore-format commits in this path; file/editor/reuse/fixup message sources are not inspectable safely from pre-tool-use enforcement.", ].join("\n"), }); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks PreToolUse git commit when Lore trailers exist but the OmX co-author trailer is missing", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-git-commit-missing-omx-coauthor-")); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-git-commit-missing-omx-coauthor", tool_input: { command: [ 'OMX_LORE_COMMIT_GUARD=1 git commit', '-m "Prevent invalid history from bypassing Lore enforcement"', '-m "The native pre-tool-use hook now blocks inline git commit messages that skip Lore trailers or the required OmX co-author trailer."', '-m "Constraint: Native PreToolUse can only inspect the Bash command text"', '-m "Tested: node --test dist/scripts/__tests__/codex-native-hook.test.js"', ].join(" "), }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.deepEqual(result.outputJson, { decision: "block", reason: "git commit is blocked until the inline commit message satisfies the Lore format and includes the required OmX co-author trailer.", hookSpecificOutput: { hookEventName: "PreToolUse", }, systemMessage: [ "git commit is blocked until the inline commit message follows the Lore protocol and includes `Co-authored-by: OmX `.", "- Add the required co-author trailer: `Co-authored-by: OmX `.", ].join("\n"), }); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("stays silent on PreToolUse for Lore-compliant git commit with OmX co-author trailer", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-git-commit-valid-")); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-git-commit-valid", tool_input: { command: [ 'OMX_LORE_COMMIT_GUARD=1 git commit', '-m "Prevent invalid history from bypassing Lore enforcement"', '-m "The native pre-tool-use hook now blocks inline git commit messages that skip Lore trailers or the required OmX co-author trailer."', '-m "Constraint: Native PreToolUse can only inspect the Bash command text"', '-m "Tested: node --test dist/scripts/__tests__/codex-native-hook.test.js"', '-m "Co-authored-by: OmX "', ].join(" "), }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("stays silent on PreToolUse for compact inline Lore commit with only OmX co-author trailer", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-git-commit-compact-coauthor-")); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-git-commit-compact-coauthor", tool_input: { command: [ 'OMX_LORE_COMMIT_GUARD=1 git commit', '-m "Launch lvisai.xyz intro site"', '-m "Co-authored-by: OmX "', ].join(" "), }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("stays silent on PreToolUse for body-omitted inline Lore commit with decision trailers", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-git-commit-compact-trailers-")); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-git-commit-compact-trailers", tool_input: { command: [ 'OMX_LORE_COMMIT_GUARD=1 git commit', '-m "Launch lvisai.xyz intro site"', '-m "Constraint: Native PreToolUse can only inspect inline Bash command text\nTested: node --test dist/scripts/__tests__/codex-native-hook.test.js\n\nCo-authored-by: OmX "', ].join(" "), }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks PreToolUse compact inline Lore commit when the blank separator is missing", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-git-commit-compact-no-separator-")); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-git-commit-compact-no-separator", tool_input: { command: [ 'OMX_LORE_COMMIT_GUARD=1 git commit', '--message="Launch lvisai.xyz intro site\nCo-authored-by: OmX "', ].join(" "), }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal((result.outputJson as { decision?: string } | null)?.decision, "block"); assert.match(JSON.stringify(result.outputJson), /Add a blank line after the subject/); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("warns on PreToolUse git commit when mapped source changes lack staged docs refresh", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-document-refresh-warn-")); try { execFileSync("git", ["init"], { cwd, stdio: "ignore" }); execFileSync("git", ["config", "user.email", "test@example.com"], { cwd, stdio: "ignore" }); execFileSync("git", ["config", "user.name", "Test User"], { cwd, stdio: "ignore" }); await mkdir(join(cwd, "src", "scripts"), { recursive: true }); await writeFile(join(cwd, "src", "scripts", "codex-native-hook.ts"), "export const hook = 1;\n", "utf-8"); await writeFile(join(cwd, "README.md"), "base\n", "utf-8"); execFileSync("git", ["add", "README.md", "src/scripts/codex-native-hook.ts"], { cwd, stdio: "ignore" }); execFileSync("git", ["commit", "-m", "init"], { cwd, stdio: "ignore" }); await writeFile(join(cwd, "src", "scripts", "codex-native-hook.ts"), "export const hook = 2;\n", "utf-8"); execFileSync("git", ["add", "src/scripts/codex-native-hook.ts"], { cwd, stdio: "ignore" }); const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-git-commit-doc-refresh-warn", tool_input: { command: [ 'git commit', '-m "Keep native hooks aligned with docs"', '-m "Update the stop hook internals without refreshing the operator docs yet."', '-m "Constraint: native hook warning MVP must remain non-blocking on commit path"', '-m "Tested: node --test dist/scripts/__tests__/codex-native-hook.test.js"', '-m "Co-authored-by: OmX "', ].join(" "), }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal((result.outputJson as { decision?: string } | null)?.decision, undefined); assert.equal((result.outputJson as { hookSpecificOutput?: { hookEventName?: string } } | null)?.hookSpecificOutput?.hookEventName, "PreToolUse"); assert.match(JSON.stringify(result.outputJson), /Document-refresh warning/); assert.match(JSON.stringify(result.outputJson), /docs\/codex-native-hooks\.md/); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not warn on PreToolUse when relevant docs are staged", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-document-refresh-docs-")); try { await mkdir(join(cwd, "src", "scripts"), { recursive: true }); await mkdir(join(cwd, "docs"), { recursive: true }); execFileSync("git", ["init"], { cwd, stdio: "ignore" }); execFileSync("git", ["config", "user.email", "test@example.com"], { cwd, stdio: "ignore" }); execFileSync("git", ["config", "user.name", "Test User"], { cwd, stdio: "ignore" }); await writeFile(join(cwd, "src", "scripts", "codex-native-hook.ts"), "export const hook = 1;\n", "utf-8"); await writeFile(join(cwd, "docs", "codex-native-hooks.md"), "initial\n", "utf-8"); execFileSync("git", ["add", "src/scripts/codex-native-hook.ts", "docs/codex-native-hooks.md"], { cwd, stdio: "ignore" }); execFileSync("git", ["commit", "-m", "init"], { cwd, stdio: "ignore" }); await writeFile(join(cwd, "src", "scripts", "codex-native-hook.ts"), "export const hook = 2;\n", "utf-8"); await writeFile(join(cwd, "docs", "codex-native-hooks.md"), "updated\n", "utf-8"); execFileSync("git", ["add", "src/scripts/codex-native-hook.ts", "docs/codex-native-hooks.md"], { cwd, stdio: "ignore" }); const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-git-commit-doc-refresh-docs", tool_input: { command: [ 'git commit', '-m "Keep native hooks aligned with docs"', '-m "Update the stop hook internals and refresh the native hook docs together."', '-m "Constraint: native hook warning MVP must remain non-blocking on commit path"', '-m "Tested: node --test dist/scripts/__tests__/codex-native-hook.test.js"', '-m "Co-authored-by: OmX "', ].join(" "), }, }, { cwd }, ); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not run commit-path document-refresh against payload cwd when git -C targets another repo", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-document-refresh-chdir-")); const otherRepo = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-document-refresh-other-")); try { await mkdir(join(cwd, "src", "scripts"), { recursive: true }); execFileSync("git", ["init"], { cwd, stdio: "ignore" }); execFileSync("git", ["config", "user.email", "test@example.com"], { cwd, stdio: "ignore" }); execFileSync("git", ["config", "user.name", "Test User"], { cwd, stdio: "ignore" }); await writeFile(join(cwd, "src", "scripts", "codex-native-hook.ts"), "export const hook = 1;\n", "utf-8"); execFileSync("git", ["add", "src/scripts/codex-native-hook.ts"], { cwd, stdio: "ignore" }); execFileSync("git", ["commit", "-m", "init"], { cwd, stdio: "ignore" }); await writeFile(join(cwd, "src", "scripts", "codex-native-hook.ts"), "export const hook = 2;\n", "utf-8"); execFileSync("git", ["add", "src/scripts/codex-native-hook.ts"], { cwd, stdio: "ignore" }); execFileSync("git", ["init"], { cwd: otherRepo, stdio: "ignore" }); execFileSync("git", ["config", "user.email", "test@example.com"], { cwd: otherRepo, stdio: "ignore" }); execFileSync("git", ["config", "user.name", "Test User"], { cwd: otherRepo, stdio: "ignore" }); await writeFile(join(otherRepo, "README.md"), "base\n", "utf-8"); execFileSync("git", ["add", "README.md"], { cwd: otherRepo, stdio: "ignore" }); execFileSync("git", ["commit", "-m", "init"], { cwd: otherRepo, stdio: "ignore" }); const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-git-commit-doc-refresh-chdir", tool_input: { command: [ `git -C ${JSON.stringify(otherRepo)}`, 'commit', '-m "Keep native hooks aligned with docs"', '-m "Document-refresh check should not inspect the caller cwd when commit targets another repo."', '-m "Constraint: alternate git targets are skipped unless hook-side repo resolution is added explicitly"', '-m "Tested: node --test dist/scripts/__tests__/codex-native-hook.test.js"', '-m "Co-authored-by: OmX "', ].join(" "), }, }, { cwd }, ); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); await rm(otherRepo, { recursive: true, force: true }); } }); it("suppresses PreToolUse document-refresh warning when commit message includes an exemption", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-document-refresh-exempt-")); try { await mkdir(join(cwd, "src", "scripts"), { recursive: true }); execFileSync("git", ["init"], { cwd, stdio: "ignore" }); execFileSync("git", ["config", "user.email", "test@example.com"], { cwd, stdio: "ignore" }); execFileSync("git", ["config", "user.name", "Test User"], { cwd, stdio: "ignore" }); await writeFile(join(cwd, "src", "scripts", "codex-native-hook.ts"), "export const hook = 1;\n", "utf-8"); execFileSync("git", ["add", "src/scripts/codex-native-hook.ts"], { cwd, stdio: "ignore" }); execFileSync("git", ["commit", "-m", "init"], { cwd, stdio: "ignore" }); await writeFile(join(cwd, "src", "scripts", "codex-native-hook.ts"), "export const hook = 2;\n", "utf-8"); execFileSync("git", ["add", "src/scripts/codex-native-hook.ts"], { cwd, stdio: "ignore" }); const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-git-commit-doc-refresh-exempt", tool_input: { command: [ 'git commit', '-m "Keep native hooks aligned with docs"', '-m "Update the stop hook internals without docs refresh because behavior is internal-only."', '-m "Constraint: native hook warning MVP must remain non-blocking on commit path"', `-m "${DOCUMENT_REFRESH_EXEMPTION_PREFIX} internal-only behavior verified"`, '-m "Tested: node --test dist/scripts/__tests__/codex-native-hook.test.js"', '-m "Co-authored-by: OmX "', ].join(" "), }, }, { cwd }, ); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("returns PostToolUse remediation guidance for command-not-found output", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-posttool-failure-")); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PostToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-fail", tool_input: { command: "foo --version" }, tool_response: "{\"exit_code\":127,\"stdout\":\"\",\"stderr\":\"bash: foo: command not found\"}", }, { cwd }, ); assert.equal(result.omxEventName, "post-tool-use"); assert.deepEqual(result.outputJson, { decision: "block", reason: "The Bash output indicates a command/setup failure that should be fixed before retrying.", hookSpecificOutput: { hookEventName: "PostToolUse", additionalContext: "Bash reported `command not found`, `permission denied`, or a missing file/path. Verify the command, dependency installation, PATH, file permissions, and referenced paths before retrying.", }, }); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("stays silent when successful search output contains old Bash failure text", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-posttool-successful-search-")); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PostToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-search-log", tool_input: { command: "rg 'command not found' .omx/logs" }, tool_response: JSON.stringify({ exit_code: 0, stdout: "old-session.log: bash: foo: command not found", stderr: "", }), }, { cwd }, ); assert.equal(result.omxEventName, "post-tool-use"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("stays silent when Bash stdout only contains failure-like source text", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-posttool-failure-source-text-")); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PostToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-source-text", tool_input: { command: "sed -n '1,40p' hook-source.ts" }, tool_response: "const text = 'bash: foo: command not found';\nconst detail = 'permission denied';", }, { cwd }, ); assert.equal(result.omxEventName, "post-tool-use"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("stays silent for rc-zero build logs that mention missing grep paths", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-posttool-build-log-")); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PostToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-build-log", tool_input: { command: "npm run build" }, tool_response: JSON.stringify({ exit_code: 0, stdout: "build passed\nnote: grep fixture says no such file or directory", stderr: "", }), }, { cwd }, ); assert.equal(result.omxEventName, "post-tool-use"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not treat Bash output containing MCP transport text as MCP transport death", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-posttool-mcp-source-text-")); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PostToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-mcp-source-text", tool_input: { command: "sed -n '580,620p' codex-native-pre-post.ts" }, tool_response: JSON.stringify({ exit_code: 0, stdout: "reason: 'MCP transport closed before response over stdio pipe closed'", stderr: "", }), }, { cwd }, ); assert.equal(result.omxEventName, "post-tool-use"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("stays silent when successful output includes prior hook context text", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-posttool-recursive-context-")); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PostToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-hook-context", tool_input: { command: "cat transcript.txt" }, tool_response: JSON.stringify({ exit_code: 0, stdout: "Bash reported `command not found`, `permission denied`, or a missing file/path. Verify the command, dependency installation, PATH, file permissions, and referenced paths before retrying.", stderr: "", }), }, { cwd }, ); assert.equal(result.omxEventName, "post-tool-use"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("stays silent when successful Bash output quotes MCP transport warnings", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-posttool-bash-mcp-quote-")); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PostToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-bash-mcp-quote", tool_input: { command: "cat diagnostic-log.txt" }, tool_response: JSON.stringify({ exit_code: 0, stdout: "diagnostic log quoted: MCP transport closed; stdio pipe closed before response", stderr: "", }), }, { cwd }, ); assert.equal(result.omxEventName, "post-tool-use"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("stays silent when Bash hard-failure text has no parsed exit code", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-posttool-bash-unparsed-failure-")); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PostToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-bash-unparsed-failure", tool_input: { command: "cat captured-output.txt" }, tool_response: "captured transcript says: bash: foo: command not found", }, { cwd }, ); assert.equal(result.omxEventName, "post-tool-use"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not treat non-MCP source output containing detector constants as MCP transport death", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-posttool-read-mcp-source-")); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PostToolUse", cwd, tool_name: "Read", tool_use_id: "tool-read-mcp-source", tool_input: { file_path: "src/scripts/codex-native-pre-post.ts" }, tool_response: "const MCP_TRANSPORT_FAILURE_PATTERNS = [/transport closed/i, /server disconnected/i];\nconst context = /\\bmcp\\b/i;", }, { cwd }, ); assert.equal(result.omxEventName, "post-tool-use"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not treat non-MCP docs stdout mentioning closed MCP transport as transport death", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-posttool-docs-mcp-log-")); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PostToolUse", cwd, tool_name: "ShellOutput", tool_use_id: "tool-docs-mcp-log", tool_response: JSON.stringify({ stdout: "Troubleshooting note: MCP transport closed after the server disconnected in an old log.", stderr: "", }), }, { cwd }, ); assert.equal(result.omxEventName, "post-tool-use"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not MCP-block non-MCP command output with unrelated stderr and MCP transport stdout", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-posttool-nonmcp-mixed-output-")); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PostToolUse", cwd, tool_name: "ShellOutput", tool_use_id: "tool-nonmcp-mixed-output", tool_response: JSON.stringify({ stdout: "captured log line: MCP transport closed before response", stderr: "grep: fixture.txt: No such file or directory", }), }, { cwd }, ); assert.equal(result.omxEventName, "post-tool-use"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("still blocks MCP-like raw transport failures", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-posttool-mcp-raw-transport-")); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PostToolUse", cwd, tool_name: "mcp__omx_state__state_write", tool_use_id: "tool-mcp-raw-transport", tool_response: "transport closed after server disconnected", }, { cwd }, ); assert.equal(result.omxEventName, "post-tool-use"); assert.equal(result.outputJson?.decision, "block"); assert.match(String(result.outputJson?.reason || ""), /lost its transport\/server connection/); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("returns PostToolUse MCP transport fallback guidance for clear MCP transport death", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-posttool-mcp-transport-")); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PostToolUse", cwd, tool_name: "mcp__omx_state__state_write", tool_use_id: "tool-mcp-transport", tool_input: { mode: "team", active: true }, tool_response: "{\"error\":\"MCP transport closed\",\"details\":\"stdio pipe closed before response\"}", }, { cwd }, ); assert.equal(result.omxEventName, "post-tool-use"); const output = result.outputJson as { decision?: string; reason?: string; hookSpecificOutput?: { additionalContext?: string }; } | null; assert.equal(output?.decision, "block"); assert.equal( output?.reason, "The MCP tool appears to have lost its transport/server connection. Preserve state, debug the transport failure, and use OMX CLI/file-backed fallbacks instead of retrying blindly.", ); const additionalContext = String( output?.hookSpecificOutput?.additionalContext ?? "", ); assert.match( additionalContext, /omx state write --input/, ); assert.match( additionalContext, /plain Node stdio processes/i, ); assert.match( additionalContext, /read-stall-state/, ); assert.match( additionalContext, /OMX_MCP_TRANSPORT_DEBUG=1/, ); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not classify non-transport MCP failures as transport death", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-posttool-mcp-nontransport-")); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PostToolUse", cwd, tool_name: "mcp__omx_state__state_write", tool_use_id: "tool-mcp-nontransport", tool_input: { active: true }, tool_response: "{\"error\":\"validation failed\",\"details\":\"mode is required\"}", }, { cwd }, ); assert.equal(result.omxEventName, "post-tool-use"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("marks active team state failed on MCP transport death without deleting team state", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-team-mcp-transport-")); const previousCwd = process.cwd(); try { process.chdir(cwd); await initTeamState( "transport-team", "task", "executor", 1, cwd, undefined, { ...process.env, OMX_SESSION_ID: "sess-transport" }, ); await writeJson(join(cwd, ".omx", "state", "team-state.json"), { active: true, team_name: "transport-team", current_phase: "team-exec", }); await dispatchCodexNativeHook( { hook_event_name: "PostToolUse", cwd, session_id: "sess-transport", tool_name: "mcp__omx_state__state_write", tool_use_id: "tool-mcp-transport-team", tool_input: { mode: "team", active: true }, tool_response: "{\"error\":\"MCP transport closed\",\"details\":\"stdio pipe closed before response\"}", }, { cwd }, ); const phase = await readTeamPhase("transport-team", cwd); const attention = await readTeamLeaderAttention("transport-team", cwd); assert.equal(phase?.current_phase, "failed"); assert.equal(attention?.leader_attention_reason, "mcp_transport_dead"); assert.equal(attention?.leader_attention_pending, true); assert.equal(existsSync(join(cwd, ".omx", "state", "team", "transport-team")), true); } finally { process.chdir(previousCwd); await rm(cwd, { recursive: true, force: true }); } }); it("marks canonical team state failed when native payload session ids differ during MCP transport death", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-team-native-transport-")); const previousCwd = process.cwd(); const canonicalSessionId = "omx-canonical-session"; const nativeSessionId = "codex-native-session"; try { process.chdir(cwd); await writeSessionStart(cwd, canonicalSessionId); const sessionPath = join(cwd, ".omx", "state", "session.json"); const sessionState = JSON.parse( await readFile(sessionPath, "utf-8"), ) as { session_id?: string; native_session_id?: string }; await writeFile( sessionPath, JSON.stringify( { ...sessionState, native_session_id: nativeSessionId, }, null, 2, ), ); await initTeamState( "transport-team", "task", "executor", 1, cwd, undefined, { ...process.env, OMX_SESSION_ID: canonicalSessionId }, ); await writeJson(join(cwd, ".omx", "state", "team-state.json"), { active: true, team_name: "transport-team", current_phase: "team-exec", }); await dispatchCodexNativeHook( { hook_event_name: "PostToolUse", cwd, session_id: nativeSessionId, tool_name: "mcp__omx_state__state_write", tool_use_id: "tool-mcp-transport-team-native", tool_input: { mode: "team", active: true }, tool_response: "{\"error\":\"MCP transport closed\",\"details\":\"stdio pipe closed before response\"}", }, { cwd }, ); const phase = await readTeamPhase("transport-team", cwd); const attention = await readTeamLeaderAttention("transport-team", cwd); assert.equal(phase?.current_phase, "failed"); assert.equal(attention?.leader_attention_reason, "mcp_transport_dead"); assert.equal(attention?.leader_attention_pending, true); assert.equal(attention?.leader_session_id, canonicalSessionId); } finally { process.chdir(previousCwd); await rm(cwd, { recursive: true, force: true }); } }); it("does not block ordinary non-zero grep output in PostToolUse", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-posttool-grep-nonzero-")); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PostToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-grep-nonzero", tool_input: { command: "grep -R missing-pattern src | head -20" }, tool_response: "{\"exit_code\":1,\"stdout\":\"src/example.ts:TODO\",\"stderr\":\"\"}", }, { cwd }, ); assert.equal(result.omxEventName, "post-tool-use"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not block ordinary non-zero diagnostic output in PostToolUse", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-posttool-diagnostic-nonzero-")); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PostToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-diagnostic-nonzero", tool_input: { command: "find src -name nope -print" }, tool_response: "{\"exit_code\":1,\"stdout\":\"searched 10 files\",\"stderr\":\"\"}", }, { cwd }, ); assert.equal(result.omxEventName, "post-tool-use"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("treats stderr-only informative non-zero output as reviewable instead of a generic failure", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-posttool-informative-stderr-")); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PostToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-useful-stderr", tool_input: { command: "gh pr checks" }, tool_response: "{\"exit_code\":8,\"stdout\":\"\",\"stderr\":\"build pending\\nlint pass\"}", }, { cwd }, ); assert.equal(result.omxEventName, "post-tool-use"); assert.deepEqual(result.outputJson, { decision: "block", reason: "The Bash command returned a non-zero exit code but produced useful output that should be reviewed before retrying.", hookSpecificOutput: { hookEventName: "PostToolUse", additionalContext: "The Bash output appears informative despite the non-zero exit code. Review and report the output before retrying instead of assuming the command simply failed.", }, }); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("treats non-zero gh pr checks style output as informative instead of a generic failure", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-posttool-informative-")); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PostToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-useful", tool_input: { command: "gh pr checks" }, tool_response: "{\"exit_code\":8,\"stdout\":\"build\\tpending\\t2m\\nlint\\tpass\\t18s\",\"stderr\":\"\"}", }, { cwd }, ); assert.equal(result.omxEventName, "post-tool-use"); assert.deepEqual(result.outputJson, { decision: "block", reason: "The Bash command returned a non-zero exit code but produced useful output that should be reviewed before retrying.", hookSpecificOutput: { hookEventName: "PostToolUse", additionalContext: "The Bash output appears informative despite the non-zero exit code. Review and report the output before retrying instead of assuming the command simply failed.", }, }); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("treats wrapped gh pr checks output as reviewable", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-posttool-gh-wrapped-")); try { for (const command of [ "GH_PAGER=cat gh pr checks", "env GH_TOKEN=ghp_testtoken gh pr checks", "/usr/bin/env gh pr checks", "env -- gh pr checks", "env -C repo gh pr checks", "/usr/bin/gh pr checks", "gh --repo owner/repo pr checks", "echo a; gh pr checks", "cd repo && gh pr checks", ]) { const result = await dispatchCodexNativeHook( { hook_event_name: "PostToolUse", cwd, tool_name: "Bash", tool_use_id: `tool-useful-${command}`, tool_input: { command }, tool_response: "{\"exit_code\":8,\"stdout\":\"build pending\",\"stderr\":\"\"}", }, { cwd }, ); assert.equal(result.omxEventName, "post-tool-use"); assert.equal((result.outputJson as { decision?: string } | null)?.decision, "block", command); } } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not treat heredoc gh pr checks text as a reviewable command", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-posttool-gh-heredoc-")); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PostToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-heredoc-gh-checks", tool_input: { command: "cat <<'EOF'\ngh pr checks\nEOF\nfalse" }, tool_response: "{\"exit_code\":1,\"stdout\":\"gh pr checks\",\"stderr\":\"\"}", }, { cwd }, ); assert.equal(result.omxEventName, "post-tool-use"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not treat echoed gh pr checks text as a reviewable command", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-posttool-gh-echo-")); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PostToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-echo-gh-checks", tool_input: { command: "echo gh pr checks" }, tool_response: "{\"exit_code\":1,\"stdout\":\"gh pr checks\",\"stderr\":\"\"}", }, { cwd }, ); assert.equal(result.omxEventName, "post-tool-use"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("returns MCP transport-death guidance and preserves failed team state", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-posttool-mcp-dead-")); try { await initTeamState( "mcp-transport-dead-team", "transport failure fallback", "executor", 1, cwd, undefined, { ...process.env, OMX_SESSION_ID: "sess-mcp-dead" }, ); const result = await dispatchCodexNativeHook( { hook_event_name: "PostToolUse", cwd, session_id: "sess-mcp-dead", tool_name: "mcp__omx_state__state_write", tool_use_id: "tool-mcp-dead", tool_response: JSON.stringify({ error: "transport closed", message: "MCP server disconnected", }), }, { cwd }, ); assert.equal(result.omxEventName, "post-tool-use"); assert.equal(result.outputJson?.decision, "block"); assert.match(String(result.outputJson?.reason || ""), /lost its transport\/server connection/); const hookSpecificOutput = result.outputJson?.hookSpecificOutput as { hookEventName?: string; additionalContext?: string; } | undefined; assert.equal(hookSpecificOutput?.hookEventName, "PostToolUse"); assert.match( String(hookSpecificOutput?.additionalContext || ""), /Retry via CLI parity with `omx state write --input '\{\}' --json`\./, ); assert.match( String(hookSpecificOutput?.additionalContext || ""), /omx team api read-stall-state/, ); const phase = JSON.parse( await readFile(join(cwd, ".omx", "state", "team", "mcp-transport-dead-team", "phase.json"), "utf-8"), ) as { current_phase?: string; transitions?: Array<{ reason?: string }> }; assert.equal(phase.current_phase, "failed"); assert.equal(phase.transitions?.at(-1)?.reason, "mcp_transport_dead"); const attention = JSON.parse( await readFile(join(cwd, ".omx", "state", "team", "mcp-transport-dead-team", "leader-attention.json"), "utf-8"), ) as { leader_attention_reason?: string; attention_reasons?: string[] }; assert.equal(attention.leader_attention_reason, "mcp_transport_dead"); assert.ok(attention.attention_reasons?.includes("mcp_transport_dead")); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("stays silent on neutral successful PostToolUse output", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-posttool-neutral-")); try { const result = await dispatchCodexNativeHook( { hook_event_name: "PostToolUse", cwd, tool_name: "Bash", tool_use_id: "tool-ok", tool_input: { command: "pwd" }, tool_response: "{\"exit_code\":0,\"stdout\":\"/repo\",\"stderr\":\"\"}", }, { cwd }, ); assert.equal(result.omxEventName, "post-tool-use"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("returns CLI fallback guidance and preserves failed team state on clear MCP transport death", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-posttool-mcp-transport-")); try { await initTeamState( "transport-team", "transport failure fallback", "executor", 1, cwd, undefined, { ...process.env, OMX_SESSION_ID: "sess-stop-mcp-transport" }, ); await writeJson(join(cwd, ".omx", "state", "team-state.json"), { active: true, team_name: "transport-team", current_phase: "team-exec", }); const result = await dispatchCodexNativeHook( { hook_event_name: "PostToolUse", cwd, session_id: "sess-stop-mcp-transport", tool_name: "mcp__omx_state__state_write", tool_use_id: "tool-mcp-fail", tool_input: { mode: "team", active: true }, tool_response: JSON.stringify({ error: "MCP transport closed unexpectedly", exit_code: 1, }), }, { cwd }, ); assert.equal(result.omxEventName, "post-tool-use"); assert.deepEqual(result.outputJson, { decision: "block", reason: "The MCP tool appears to have lost its transport/server connection. Preserve state, debug the transport failure, and use OMX CLI/file-backed fallbacks instead of retrying blindly.", hookSpecificOutput: { hookEventName: "PostToolUse", additionalContext: "Clear MCP transport-death signal detected. Preserve current team/runtime state. Retry via CLI parity with `omx state write --input '{\"mode\":\"team\",\"active\":true}' --json`. OMX MCP servers are plain Node stdio processes, so they still shut down when stdin/transport closes. If this happened during team runtime, inspect first with `omx team status ` or `omx team api read-stall-state --input '{\"team_name\":\"\"}' --json`, and only force cleanup after capturing needed state. For root-cause debugging, rerun with `OMX_MCP_TRANSPORT_DEBUG=1` to log why the stdio transport closed.", }, }); const phase = await readTeamPhase("transport-team", cwd); const attention = await readTeamLeaderAttention("transport-team", cwd); assert.equal(phase?.current_phase, "failed"); assert.equal(attention?.leader_attention_reason, "mcp_transport_dead"); } finally { await rm(cwd, { recursive: true, force: true }); } }); for (const rootActiveCase of [ { mode: "autopilot", phase: "execution" }, { mode: "ultrawork", phase: "executing" }, { mode: "ultraqa", phase: "diagnose" }, ] as const) { it(`returns Stop continuation output from root ${rootActiveCase.mode} state when no session is active`, async () => { const cwd = await mkdtemp(join(tmpdir(), `omx-native-hook-stop-root-${rootActiveCase.mode}-`)); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(stateDir, { recursive: true }); await writeJson(join(stateDir, `${rootActiveCase.mode}-state.json`), { active: true, mode: rootActiveCase.mode, current_phase: rootActiveCase.phase, }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.deepEqual(result.outputJson, { decision: "block", reason: `OMX ${rootActiveCase.mode} is still active (phase: ${rootActiveCase.phase}); continue the task and gather fresh verification evidence before stopping.`, stopReason: `${rootActiveCase.mode}_${rootActiveCase.phase}`, systemMessage: `OMX ${rootActiveCase.mode} is still active (phase: ${rootActiveCase.phase}).`, }); } finally { await rm(cwd, { recursive: true, force: true }); } }); } it("returns Stop continuation output while Autopilot is active", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-autopilot-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(join(stateDir, "sessions", "sess-stop-autopilot"), { recursive: true }); await writeJson(join(stateDir, "sessions", "sess-stop-autopilot", "autopilot-state.json"), { active: true, current_phase: "execution", }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-autopilot", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.deepEqual(result.outputJson, { decision: "block", reason: "OMX autopilot is still active (phase: execution); continue the task and gather fresh verification evidence before stopping.", stopReason: "autopilot_execution", systemMessage: "OMX autopilot is still active (phase: execution).", }); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("suppresses parent Autopilot Stop continuation in side conversations", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-autopilot-side-conversation-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-stop-autopilot-side-conversation"; const transcriptPath = join(cwd, "side-conversation-rollout.jsonl"); await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeJson(join(stateDir, "sessions", sessionId, "autopilot-state.json"), { active: true, mode: "autopilot", current_phase: "deep-interview", }); await writeFile( transcriptPath, `${JSON.stringify({ type: "message", role: "user", content: [ "Side conversation boundary.", "Everything before this boundary is inherited history from the parent thread. It is reference context only. It is not your current task.", "Only messages submitted after this boundary are active user instructions for this side conversation.", "You are a side-conversation assistant, separate from the main thread.", ].join("\n\n"), })}\n`, "utf-8", ); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: sessionId, thread_id: "thread-stop-autopilot-side-conversation", transcript_path: transcriptPath, last_assistant_message: "Waiting for a new side-conversation question.", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("requires Autopilot code review after a compact-boundary Stop exemption", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-autopilot-review-compact-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-stop-autopilot-review-compact"; await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeJson(join(stateDir, "sessions", sessionId, "autopilot-state.json"), { active: true, mode: "autopilot", current_phase: "code-review", state: { phase_cycle: ["ralplan", "ralph", "code-review"], handoff_artifacts: { ralplan: ".omx/plans/prd-issue-2366.md", ralph: { verification: ["npm test"] }, code_review: null, }, review_verdict: null, }, }); const compactBoundary = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: sessionId, stop_reason: "context compact", }, { cwd }, ); const resumedStop = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: sessionId, }, { cwd }, ); assert.equal(compactBoundary.omxEventName, "stop"); assert.equal(compactBoundary.outputJson, null); assert.equal(resumedStop.omxEventName, "stop"); assert.deepEqual(resumedStop.outputJson, { decision: "block", reason: "OMX autopilot is still active (phase: code-review); continue the task and gather fresh verification evidence before stopping.", stopReason: "autopilot_code-review", systemMessage: "OMX autopilot is still active (phase: code-review). Run the required $code-review step before completing or clearing Autopilot state.", }); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("suppresses duplicate Autopilot planning Stop replays so stale planning state cannot loop indefinitely", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-autopilot-planning-replay-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(join(stateDir, "sessions", "sess-stop-autopilot-planning-replay"), { recursive: true }); await writeJson(join(stateDir, "sessions", "sess-stop-autopilot-planning-replay", "autopilot-state.json"), { active: true, current_phase: "planning", }); const payload = { hook_event_name: "Stop", cwd, session_id: "sess-stop-autopilot-planning-replay", thread_id: "thread-stop-autopilot-planning-replay", turn_id: "turn-stop-autopilot-planning-replay", last_assistant_message: "Autopilot planning is still active.", }; const first = await dispatchCodexNativeHook(payload, { cwd }); const replay = await dispatchCodexNativeHook( { ...payload, stop_hook_active: true, }, { cwd }, ); assert.equal(first.omxEventName, "stop"); assert.deepEqual(first.outputJson, { decision: "block", reason: "OMX autopilot is still active (phase: planning); continue the task and gather fresh verification evidence before stopping.", stopReason: "autopilot_planning", systemMessage: "OMX autopilot is still active (phase: planning).", }); assert.equal(replay.omxEventName, "stop"); assert.equal(replay.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("allows Stop when terminal Autopilot run-state shadows stale session ralplan state", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-autopilot-terminal-run-state-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-stop-autopilot-terminal-run-state"; await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeJson(join(stateDir, "sessions", sessionId, "autopilot-state.json"), { active: true, mode: "autopilot", current_phase: "ralplan", }); await writeJson(join(stateDir, "sessions", sessionId, "run-state.json"), { version: 1, active: false, mode: "autopilot", outcome: "finish", lifecycle_outcome: "finished", current_phase: "complete", completed_at: "2026-05-20T11:00:00.000Z", updated_at: "2026-05-20T11:00:00.000Z", }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: sessionId, thread_id: "thread-stop-autopilot-terminal-run-state", turn_id: "turn-stop-autopilot-terminal-run-state-1", last_assistant_message: "Done. Verification passed.", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("still blocks Stop while Autopilot ralplan state is genuinely non-terminal", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-autopilot-active-ralplan-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-stop-autopilot-active-ralplan"; await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeJson(join(stateDir, "sessions", sessionId, "autopilot-state.json"), { active: true, mode: "autopilot", current_phase: "ralplan", }); await writeJson(join(stateDir, "sessions", sessionId, "run-state.json"), { version: 1, active: true, mode: "autopilot", outcome: "continue", current_phase: "ralplan", updated_at: "2026-05-20T11:00:00.000Z", }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: sessionId, thread_id: "thread-stop-autopilot-active-ralplan", turn_id: "turn-stop-autopilot-active-ralplan-1", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.deepEqual(result.outputJson, { decision: "block", reason: "OMX autopilot is still active (phase: ralplan); continue the task and gather fresh verification evidence before stopping.", stopReason: "autopilot_ralplan", systemMessage: "OMX autopilot is still active (phase: ralplan).", }); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not block Stop from stale root Autopilot planning state when the explicit session has no scoped state", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-stale-root-autopilot-planning-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(join(stateDir, "sessions", "sess-current"), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: "sess-current", cwd }); await writeJson(join(stateDir, "autopilot-state.json"), { active: true, mode: "autopilot", current_phase: "planning", }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-current", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); for (const staleRootCase of [ { mode: "autopilot", phase: "execution" }, { mode: "ultrawork", phase: "executing" }, { mode: "ultraqa", phase: "diagnose" }, ] as const) { it(`does not block Stop from stale root ${staleRootCase.mode} state when the explicit session directory is missing`, async () => { const cwd = await mkdtemp(join(tmpdir(), `omx-native-hook-stop-missing-session-${staleRootCase.mode}-`)); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(stateDir, { recursive: true }); await writeJson(join(stateDir, `${staleRootCase.mode}-state.json`), { active: true, mode: staleRootCase.mode, current_phase: staleRootCase.phase, }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "missing-session", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); } it("does not block Stop when an explicit blocked_on_user run_outcome is present on a mode state", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-autopilot-blocked-outcome-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(join(stateDir, "sessions", "sess-stop-autopilot-blocked-outcome"), { recursive: true }); await writeJson(join(stateDir, "sessions", "sess-stop-autopilot-blocked-outcome", "autopilot-state.json"), { active: true, current_phase: "execution", run_outcome: "blocked_on_user", }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-autopilot-blocked-outcome", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("returns Stop continuation output while Ultrawork is active", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-ultrawork-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(join(stateDir, "sessions", "sess-stop-ultrawork"), { recursive: true }); await writeJson(join(stateDir, "sessions", "sess-stop-ultrawork", "ultrawork-state.json"), { active: true, current_phase: "executing", }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-ultrawork" }, { cwd }, ); assert.deepEqual(result.outputJson, { decision: "block", reason: "OMX ultrawork is still active (phase: executing); continue the task and gather fresh verification evidence before stopping.", stopReason: "ultrawork_executing", systemMessage: "OMX ultrawork is still active (phase: executing).", }); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("returns Stop continuation output while UltraQA is active", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-ultraqa-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(join(stateDir, "sessions", "sess-stop-ultraqa"), { recursive: true }); await writeJson(join(stateDir, "sessions", "sess-stop-ultraqa", "ultraqa-state.json"), { active: true, current_phase: "diagnose", }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-ultraqa" }, { cwd }, ); assert.deepEqual(result.outputJson, { decision: "block", reason: "OMX ultraqa is still active (phase: diagnose); continue the task and gather fresh verification evidence before stopping.", stopReason: "ultraqa_diagnose", systemMessage: "OMX ultraqa is still active (phase: diagnose).", }); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("marks leader-owned team attention during native Stop dispatch without a polling watcher", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-team-attention-")); try { await initTeamState( "stop-attention-team", "native stop attention", "executor", 1, cwd, undefined, { ...process.env, OMX_SESSION_ID: "sess-stop-team-attention" }, ); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-team-attention", }, { cwd }, ); const attention = await readTeamLeaderAttention("stop-attention-team", cwd); assert.equal(result.omxEventName, "stop"); assert.equal(attention?.source, "native_stop"); assert.equal(attention?.leader_session_active, false); assert.equal(attention?.leader_session_id, "sess-stop-team-attention"); assert.match(attention?.leader_session_stopped_at ?? "", /^\d{4}-\d{2}-\d{2}T/); assert.deepEqual(result.outputJson, { decision: "block", reason: `OMX team pipeline is still active (stop-attention-team) at phase team-exec; continue coordinating until the team reaches a terminal phase.${TEAM_STOP_COMMIT_GUIDANCE}`, stopReason: "team_team-exec", systemMessage: "OMX team pipeline is still active at phase team-exec.", }); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("returns Stop continuation output while team phase is non-terminal", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-team-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(stateDir, { recursive: true }); await writeJson(join(stateDir, "team-state.json"), { active: true, current_phase: "team-exec", team_name: "review-team", session_id: "sess-stop-team", }); await writeJson(join(stateDir, "team", "review-team", "phase.json"), { current_phase: "team-verify", max_fix_attempts: 3, current_fix_attempt: 0, transitions: [], updated_at: new Date().toISOString(), }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-team", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.deepEqual(result.outputJson, { decision: "block", reason: `OMX team pipeline is still active (review-team) at phase team-verify; continue coordinating until the team reaches a terminal phase.${TEAM_STOP_COMMIT_GUIDANCE}`, stopReason: "team_team-verify", systemMessage: "OMX team pipeline is still active at phase team-verify.", }); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks Stop for a team worker with a non-terminal assigned task via native worker context", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-team-worker-")); const prevTeamWorker = process.env.OMX_TEAM_WORKER; const prevTeamStateRoot = process.env.OMX_TEAM_STATE_ROOT; const prevLeaderCwd = process.env.OMX_TEAM_LEADER_CWD; try { await initTeamState( "worker-stop-team", "worker stop fallback", "executor", 1, cwd, undefined, { ...process.env, OMX_SESSION_ID: "sess-stop-team-worker" }, ); const workerCwd = join(cwd, ".omx", "team", "worker-stop-team", "worktrees", "worker-1"); const workerDir = join(cwd, ".omx", "state", "team", "worker-stop-team", "workers", "worker-1"); await mkdir(workerCwd, { recursive: true }); await writeJson(join(workerDir, "identity.json"), { name: "worker-1", index: 1, role: "executor", assigned_tasks: ["1"], worktree_path: workerCwd, team_state_root: join(cwd, ".omx", "state"), }); await writeJson(join(workerDir, "status.json"), { state: "working", current_task_id: "1", updated_at: new Date().toISOString(), }); await writeJson(join(cwd, ".omx", "state", "team", "worker-stop-team", "tasks", "task-1.json"), { id: "1", subject: "hook task", description: "finish hook task", status: "in_progress", owner: "worker-1", created_at: new Date().toISOString(), }); process.env.OMX_TEAM_WORKER = "worker-stop-team/worker-1"; process.env.OMX_TEAM_INTERNAL_WORKER = "worker-stop-team/worker-1"; process.env.OMX_TEAM_STATE_ROOT = join(cwd, ".omx", "state"); process.env.OMX_TEAM_LEADER_CWD = cwd; const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd: workerCwd, session_id: "sess-stop-team-worker", }, { cwd: workerCwd }, ); assert.deepEqual(result.outputJson, { decision: "block", reason: "OMX team worker worker-1 is still assigned non-terminal task 1 (in_progress); continue the current assigned task or report a concrete blocker before stopping.", stopReason: "team_worker_worker-1_1_in_progress", systemMessage: "OMX team worker worker-1 is still assigned task 1 (in_progress).", }); } finally { if (typeof prevTeamWorker === "string") process.env.OMX_TEAM_WORKER = prevTeamWorker; else delete process.env.OMX_TEAM_WORKER; if (typeof prevTeamStateRoot === "string") process.env.OMX_TEAM_STATE_ROOT = prevTeamStateRoot; else delete process.env.OMX_TEAM_STATE_ROOT; if (typeof prevLeaderCwd === "string") process.env.OMX_TEAM_LEADER_CWD = prevLeaderCwd; else delete process.env.OMX_TEAM_LEADER_CWD; await rm(cwd, { recursive: true, force: true }); } }); it("rejects foreign explicit worker roots for completed Stop/PostToolUse and ignores malformed internal identity", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-team-worker-foreign-root-")); const workerCwd = join(cwd, "worker"); const canonicalRoot = join(cwd, "canonical-state"); const foreignRoot = join(cwd, "foreign-state"); const teamName = "foreign-root-team"; const workerName = "worker-1"; const paneId = "%10"; const writeWorkerRoot = async (stateRoot: string, boundRoot: string, taskStatus: string) => { const teamRoot = join(stateRoot, "team", teamName); const workerRoot = join(teamRoot, "workers", workerName); await mkdir(workerRoot, { recursive: true }); await writeJson(join(workerRoot, "identity.json"), { name: workerName, assigned_tasks: ["1"], pane_id: paneId, worktree_path: workerCwd, team_state_root: boundRoot, }); await writeJson(join(workerRoot, "status.json"), { state: taskStatus === "completed" ? "done" : "working", current_task_id: "1", }); await writeJson(join(teamRoot, "tasks", "task-1.json"), { id: "1", owner: workerName, status: taskStatus, }); const metadata = { name: teamName, leader_cwd: cwd, team_state_root: boundRoot, leader_pane_id: "%42", workers: [{ name: workerName, pane_id: paneId, worktree_path: workerCwd, team_state_root: boundRoot }], }; await writeJson(join(teamRoot, "manifest.v2.json"), metadata); await writeJson(join(teamRoot, "config.json"), metadata); }; try { await mkdir(workerCwd, { recursive: true }); await writeWorkerRoot(canonicalRoot, canonicalRoot, "in_progress"); await writeWorkerRoot(foreignRoot, canonicalRoot, "completed"); process.env.TMUX = "1"; process.env.TMUX_PANE = paneId; process.env.OMX_TEAM_INTERNAL_WORKER = `${teamName}/${workerName}`; process.env.OMX_TEAM_WORKER = `${teamName}/${workerName}`; process.env.OMX_TEAM_STATE_ROOT = foreignRoot; process.env.OMX_TEAM_LEADER_CWD = cwd; const foreignStop = await dispatchCodexNativeHook({ hook_event_name: "Stop", cwd: workerCwd, session_id: "foreign-root-stop", }, { cwd: workerCwd }); assert.equal(foreignStop.outputJson?.decision, "block"); assert.equal(foreignStop.outputJson?.stopReason, "team_worker_worker-1_missing_state_dir"); assert.equal(existsSync(join(foreignRoot, "team", teamName, "workers", workerName, "worker-stop-nudge.json")), false); const foreignPostToolUse = await dispatchCodexNativeHook({ hook_event_name: "PostToolUse", cwd: workerCwd, session_id: "foreign-root-posttooluse", tool_name: "Bash", tool_input: { command: "printf ok" }, tool_response: { exit_code: 0 }, }, { cwd: workerCwd }); assert.equal(foreignPostToolUse.outputJson, null); assert.equal(existsSync(join(foreignRoot, "team", teamName, "workers", workerName, "heartbeat.json")), false); const markerForeignRoot = join(cwd, "marker-foreign-state"); const markerTeamRoot = join(markerForeignRoot, "team", teamName); await mkdir(join(markerTeamRoot, "workers", workerName), { recursive: true }); const markerMetadata = { name: teamName, team_state_root: canonicalRoot, workers: [{ name: workerName, team_state_root: canonicalRoot, worktree_path: workerCwd }], }; await writeJson(join(markerTeamRoot, "manifest.v2.json"), markerMetadata); await writeJson(join(markerTeamRoot, "config.json"), markerMetadata); process.env.OMX_TEAM_STATE_ROOT = markerForeignRoot; const markerForeignPostToolUse = await dispatchCodexNativeHook({ hook_event_name: "PostToolUse", cwd: workerCwd, session_id: "marker-foreign-root-posttooluse", tool_name: "Bash", tool_input: { command: "printf ok" }, tool_response: { exit_code: 0 }, }, { cwd: workerCwd }); assert.equal(markerForeignPostToolUse.outputJson, null); assert.equal(existsSync(join(markerForeignRoot, "team", teamName, "workers", workerName, "heartbeat.json")), false); process.env.OMX_TEAM_STATE_ROOT = canonicalRoot; process.env.OMX_TEAM_INTERNAL_WORKER = "malformed-internal"; process.env.OMX_TEAM_WORKER = `display-team/${workerName}`; const malformedInternalStop = await dispatchCodexNativeHook({ hook_event_name: "Stop", cwd: workerCwd, session_id: "malformed-internal-stop", }, { cwd: workerCwd }); assert.equal(malformedInternalStop.outputJson, null); const malformedInternalPostToolUse = await dispatchCodexNativeHook({ hook_event_name: "PostToolUse", cwd: workerCwd, session_id: "malformed-internal-posttooluse", tool_name: "Bash", tool_input: { command: "printf ok" }, tool_response: { exit_code: 0 }, }, { cwd: workerCwd }); assert.equal(malformedInternalPostToolUse.outputJson, null); assert.equal(existsSync(join(canonicalRoot, "team", teamName, "workers", workerName, "heartbeat.json")), false); assert.equal(existsSync(join(canonicalRoot, "team", teamName, "workers", workerName, "worker-stop-nudge.json")), false); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks Stop as a team-worker task failure when worker status is terminal but task evidence is not completed", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-team-worker-terminal-stale-")); const prevTeamWorker = process.env.OMX_TEAM_WORKER; const prevTeamStateRoot = process.env.OMX_TEAM_STATE_ROOT; const prevLeaderCwd = process.env.OMX_TEAM_LEADER_CWD; try { await initTeamState( "worker-stale-team", "worker stale stop fallback", "executor", 1, cwd, undefined, { ...process.env, OMX_SESSION_ID: "sess-stop-team-worker-stale" }, ); const stateDir = join(cwd, ".omx", "state"); const workerCwd = join(cwd, ".omx", "team", "worker-stale-team", "worktrees", "worker-1"); const workerDir = join(stateDir, "team", "worker-stale-team", "workers", "worker-1"); await mkdir(workerCwd, { recursive: true }); await writeJson(join(workerDir, "identity.json"), { name: "worker-1", index: 1, role: "executor", assigned_tasks: ["1"], worktree_path: workerCwd, team_state_root: stateDir, }); await writeJson(join(workerDir, "status.json"), { state: "done", current_task_id: "1", updated_at: new Date().toISOString(), }); await writeJson(join(stateDir, "team", "worker-stale-team", "tasks", "task-1.json"), { id: "1", subject: "stale hook task", description: "non-completed task should still block terminal worker Stop", status: "in_progress", owner: "worker-1", created_at: new Date().toISOString(), }); process.env.OMX_TEAM_WORKER = "worker-stale-team/worker-1"; process.env.OMX_TEAM_INTERNAL_WORKER = "worker-stale-team/worker-1"; process.env.OMX_TEAM_STATE_ROOT = stateDir; process.env.OMX_TEAM_LEADER_CWD = cwd; const payload = { hook_event_name: "Stop", cwd: workerCwd, session_id: "sess-stop-team-worker-stale", thread_id: "thread-stop-team-worker-stale", }; const result = await dispatchCodexNativeHook(payload, { cwd: workerCwd }); const replay = await dispatchCodexNativeHook( { ...payload, stop_hook_active: true }, { cwd: workerCwd }, ); const camelReplay = await dispatchCodexNativeHook( { ...payload, stopHookActive: true }, { cwd: workerCwd }, ); assert.equal( (result.outputJson as { stopReason?: string } | null)?.stopReason, "team_worker_worker-1_1_in_progress", ); assert.equal(replay.outputJson, null); assert.equal(camelReplay.outputJson, null); } finally { if (typeof prevTeamWorker === "string") process.env.OMX_TEAM_WORKER = prevTeamWorker; else delete process.env.OMX_TEAM_WORKER; if (typeof prevTeamStateRoot === "string") process.env.OMX_TEAM_STATE_ROOT = prevTeamStateRoot; else delete process.env.OMX_TEAM_STATE_ROOT; if (typeof prevLeaderCwd === "string") process.env.OMX_TEAM_LEADER_CWD = prevLeaderCwd; else delete process.env.OMX_TEAM_LEADER_CWD; await rm(cwd, { recursive: true, force: true }); } }); it("re-blocks live team worker Stop replays but suppresses stale terminal worker repeats", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-team-worker-repeat-")); try { await initTeamState( "worker-repeat-team", "worker stop repeat guard", "executor", 1, cwd, undefined, { ...process.env, OMX_SESSION_ID: "sess-stop-team-worker-repeat" }, ); const stateDir = join(cwd, ".omx", "state"); const workerDir = join(stateDir, "team", "worker-repeat-team", "workers", "worker-1"); const taskPath = join(stateDir, "team", "worker-repeat-team", "tasks", "task-1.json"); const workerCwd = join(cwd, ".omx", "team", "worker-repeat-team", "worktrees", "worker-1"); await mkdir(workerCwd, { recursive: true }); await writeJson(join(workerDir, "identity.json"), { name: "worker-1", index: 1, role: "executor", assigned_tasks: ["1"], worktree_path: workerCwd, team_state_root: stateDir, }); await writeJson(join(workerDir, "status.json"), { state: "working", current_task_id: "1", updated_at: new Date().toISOString(), }); await writeJson(taskPath, { id: "1", subject: "hook task", description: "finish hook task", status: "in_progress", owner: "worker-1", created_at: new Date().toISOString(), }); process.env.OMX_TEAM_WORKER = "worker-repeat-team/worker-1"; process.env.OMX_TEAM_INTERNAL_WORKER = "worker-repeat-team/worker-1"; process.env.OMX_TEAM_STATE_ROOT = stateDir; process.env.OMX_TEAM_LEADER_CWD = cwd; const basePayload = { hook_event_name: "Stop", cwd: workerCwd, session_id: "sess-stop-team-worker-repeat", thread_id: "thread-stop-team-worker-repeat", turn_id: "turn-stop-team-worker-repeat-1", last_assistant_message: "I need to stop before this task is done.", }; const expectedInProgress = { decision: "block", reason: "OMX team worker worker-1 is still assigned non-terminal task 1 (in_progress); continue the current assigned task or report a concrete blocker before stopping.", stopReason: "team_worker_worker-1_1_in_progress", systemMessage: "OMX team worker worker-1 is still assigned task 1 (in_progress).", }; const first = await dispatchCodexNativeHook(basePayload, { cwd: workerCwd }); const replay = await dispatchCodexNativeHook( { ...basePayload, stop_hook_active: true }, { cwd: workerCwd }, ); const freshTurn = await dispatchCodexNativeHook( { ...basePayload, turn_id: "turn-stop-team-worker-repeat-2", stop_hook_active: true }, { cwd: workerCwd }, ); const exempt = await dispatchCodexNativeHook( { ...basePayload, turn_id: "turn-stop-team-worker-exempt", stopReason: "context limit" }, { cwd: workerCwd }, ); await writeJson(taskPath, { id: "1", subject: "hook task", description: "finish hook task", status: "blocked", owner: "worker-1", created_at: new Date().toISOString(), }); const stateChanged = await dispatchCodexNativeHook( { ...basePayload, turn_id: "turn-stop-team-worker-repeat-3", stop_hook_active: true }, { cwd: workerCwd }, ); assert.deepEqual(first.outputJson, expectedInProgress); assert.deepEqual(replay.outputJson, expectedInProgress); assert.deepEqual(freshTurn.outputJson, expectedInProgress); assert.equal(exempt.outputJson, null); assert.deepEqual(stateChanged.outputJson, { decision: "block", reason: "OMX team worker worker-1 is still assigned non-terminal task 1 (blocked); continue the current assigned task or report a concrete blocker before stopping.", stopReason: "team_worker_worker-1_1_blocked", systemMessage: "OMX team worker worker-1 is still assigned task 1 (blocked).", }); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("allows Stop for a team worker when assigned task is terminal and bypasses generic team blocking", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-team-worker-terminal-")); const prevTeamWorker = process.env.OMX_TEAM_WORKER; const prevTeamStateRoot = process.env.OMX_TEAM_STATE_ROOT; const prevPath = process.env.PATH; try { await initTeamState( "worker-stop-team-terminal", "worker stop terminal fallback", "executor", 1, cwd, undefined, { ...process.env, OMX_SESSION_ID: "sess-stop-team-worker-terminal" }, ); const fakeBinDir = join(cwd, "fake-bin"); const tmuxLogPath = join(cwd, "tmux.log"); await mkdir(fakeBinDir, { recursive: true }); await writeFile(join(fakeBinDir, "tmux"), buildWorkerStopFakeTmux(tmuxLogPath)); await chmod(join(fakeBinDir, "tmux"), 0o755); const workerDir = join(cwd, ".omx", "state", "team", "worker-stop-team-terminal", "workers", "worker-1"); await writeJson(join(cwd, ".omx", "state", "team", "worker-stop-team-terminal", "config.json"), { name: "worker-stop-team-terminal", tmux_session: "omx-team-worker-stop", leader_pane_id: "%42", leader_pane_pid: 12345, tmux_pane_owner_id: "team:test", workers: [{ name: "worker-1", index: 1, pane_id: "%10", pid: 12310 }], }); await writeJson(join(cwd, ".omx", "state", "team", "worker-stop-team-terminal", "manifest.v2.json"), { name: "worker-stop-team-terminal", tmux_session: "omx-team-worker-stop", leader_pane_id: "%42", leader_pane_pid: 12345, tmux_pane_owner_id: "team:test", workers: [{ name: "worker-1", index: 1, pane_id: "%10", pid: 12310 }], }); await writeJson(join(workerDir, "identity.json"), { name: "worker-1", index: 1, role: "executor", assigned_tasks: ["1"], worktree_path: cwd, team_state_root: join(cwd, ".omx", "state"), }); await writeJson(join(workerDir, "status.json"), { state: "done", current_task_id: "1", updated_at: new Date().toISOString(), }); await writeJson(join(cwd, ".omx", "state", "team", "worker-stop-team-terminal", "tasks", "task-1.json"), { id: "1", subject: "hook task", description: "finish hook task", status: "completed", owner: "worker-1", created_at: new Date().toISOString(), }); process.env.OMX_TEAM_WORKER = "worker-stop-team-terminal/worker-1"; process.env.OMX_TEAM_INTERNAL_WORKER = "worker-stop-team-terminal/worker-1"; process.env.OMX_TEAM_STATE_ROOT = join(cwd, ".omx", "state"); process.env.PATH = `${fakeBinDir}:${prevPath || ""}`; const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-team-worker-terminal", }, { cwd }, ); const replay = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-team-worker-terminal", turn_id: "turn-worker-stop-terminal-replay", }, { cwd }, ); assert.equal(result.outputJson, null); assert.equal(replay.outputJson, null); const tmuxLog = await readFile(tmuxLogPath, "utf-8"); const stopNudges = tmuxLog.match(/send-keys -t %42 -l \[OMX\] worker-1 native Stop allowed/g) || []; assert.equal(stopNudges.length, 1, "allowed worker Stop should nudge leader exactly once inside cooldown"); const nudgeState = JSON.parse(await readFile(join(workerDir, "worker-stop-nudge.json"), "utf-8")); assert.equal(nudgeState.delivery, "sent"); } finally { if (typeof prevTeamWorker === "string") process.env.OMX_TEAM_WORKER = prevTeamWorker; else delete process.env.OMX_TEAM_WORKER; if (typeof prevTeamStateRoot === "string") process.env.OMX_TEAM_STATE_ROOT = prevTeamStateRoot; else delete process.env.OMX_TEAM_STATE_ROOT; if (typeof prevPath === "string") process.env.PATH = prevPath; else delete process.env.PATH; await rm(cwd, { recursive: true, force: true }); } }); it("steers worker Stop leader nudge directly when leader pane is busy", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-team-worker-busy-leader-")); const prevTeamWorker = process.env.OMX_TEAM_WORKER; const prevTeamStateRoot = process.env.OMX_TEAM_STATE_ROOT; const prevPath = process.env.PATH; try { await initTeamState( "worker-stop-team-busy-leader", "worker stop busy leader", "executor", 1, cwd, undefined, { ...process.env, OMX_SESSION_ID: "sess-stop-team-worker-busy-leader" }, ); const fakeBinDir = join(cwd, "fake-bin"); const tmuxLogPath = join(cwd, "tmux.log"); await mkdir(fakeBinDir, { recursive: true }); await writeFile(join(fakeBinDir, "tmux"), buildWorkerStopFakeTmux(tmuxLogPath, { busyLeader: true })); await chmod(join(fakeBinDir, "tmux"), 0o755); const stateDir = join(cwd, ".omx", "state"); const teamDir = join(stateDir, "team", "worker-stop-team-busy-leader"); const workerDir = join(teamDir, "workers", "worker-1"); await writeJson(join(teamDir, "config.json"), { name: "worker-stop-team-busy-leader", tmux_session: "omx-team-worker-stop", leader_pane_id: "%42", leader_pane_pid: 12345, tmux_pane_owner_id: "team:test", workers: [{ name: "worker-1", index: 1, pane_id: "%10", pid: 12310 }], }); await writeJson(join(teamDir, "manifest.v2.json"), { name: "worker-stop-team-busy-leader", tmux_session: "omx-team-worker-stop", leader_pane_id: "%42", leader_pane_pid: 12345, tmux_pane_owner_id: "team:test", workers: [{ name: "worker-1", index: 1, pane_id: "%10", pid: 12310 }], }); await writeJson(join(workerDir, "identity.json"), { name: "worker-1", index: 1, role: "executor", assigned_tasks: ["1"], worktree_path: cwd, team_state_root: stateDir, }); await writeJson(join(workerDir, "status.json"), { state: "done", current_task_id: "1", updated_at: new Date().toISOString(), }); await writeJson(join(teamDir, "tasks", "task-1.json"), { id: "1", subject: "hook task", description: "finish hook task", status: "completed", owner: "worker-1", created_at: new Date().toISOString(), }); process.env.OMX_TEAM_WORKER = "worker-stop-team-busy-leader/worker-1"; process.env.OMX_TEAM_INTERNAL_WORKER = "worker-stop-team-busy-leader/worker-1"; process.env.OMX_TEAM_STATE_ROOT = stateDir; process.env.PATH = `${fakeBinDir}:${prevPath || ""}`; const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-team-worker-busy-leader", }, { cwd }, ); assert.equal(result.outputJson, null); const tmuxLog = await readFile(tmuxLogPath, "utf-8"); assert.match(tmuxLog, /send-keys -t %42 -l \[omx:team-notice-ledger:[a-f0-9]{24}\] Review current Team notices\./); assert.doesNotMatch(tmuxLog, /send-keys -t %42 -l .*worker-stop-team-busy-leader|send-keys -t %42 Tab/); const submits = tmuxLog.match(/send-keys -t %42 Enter/g) || []; assert.equal(submits.length, 2, "busy worker-stop nudge should submit directly as steering, not queue via Tab"); const nudgeState = JSON.parse(await readFile(join(workerDir, "worker-stop-nudge.json"), "utf-8")); assert.equal(nudgeState.delivery, "steered"); } finally { if (typeof prevTeamWorker === "string") process.env.OMX_TEAM_WORKER = prevTeamWorker; else delete process.env.OMX_TEAM_WORKER; if (typeof prevTeamStateRoot === "string") process.env.OMX_TEAM_STATE_ROOT = prevTeamStateRoot; else delete process.env.OMX_TEAM_STATE_ROOT; if (typeof prevPath === "string") process.env.PATH = prevPath; else delete process.env.PATH; await rm(cwd, { recursive: true, force: true }); } }); it("dedupes allowed worker Stop leader nudges across workers in the same team window", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-team-worker-team-dedupe-")); const prevPath = process.env.PATH; try { const stateDir = join(cwd, ".omx", "state"); const logsDir = join(cwd, ".omx", "logs"); const teamName = "worker-stop-team-dedupe"; const teamDir = join(stateDir, "team", teamName); const fakeBinDir = join(cwd, "fake-bin"); const tmuxLogPath = join(cwd, "tmux.log"); await mkdir(fakeBinDir, { recursive: true }); await writeFile(join(fakeBinDir, "tmux"), buildWorkerStopFakeTmux(tmuxLogPath)); await chmod(join(fakeBinDir, "tmux"), 0o755); await writeJson(join(teamDir, "manifest.v2.json"), { name: teamName, tmux_session: "omx-team-worker-stop", leader_pane_id: "%42", leader_pane_pid: 12345, tmux_pane_owner_id: "team:test", workers: [ { name: "worker-1", index: 1, pane_id: "%10", pid: 12310 }, { name: "worker-2", index: 2, pane_id: "%11", pid: 12311 }, ], }); process.env.PATH = `${fakeBinDir}:${prevPath || ""}`; const first = await maybeNudgeLeaderForAllowedWorkerStop({ stateDir, logsDir, workerContext: { teamName, workerName: "worker-1" }, }); const second = await maybeNudgeLeaderForAllowedWorkerStop({ stateDir, logsDir, workerContext: { teamName, workerName: "worker-2" }, }); assert.equal(first.result, "sent"); assert.equal(second.result, "suppressed_team_cooldown"); const tmuxLog = await readFile(tmuxLogPath, "utf-8"); const stopNudges = tmuxLog.match(/send-keys -t %42 -l \[OMX\] worker-\d+ native Stop allowed/g) || []; assert.equal(stopNudges.length, 1, "same-team workers should share one leader nudge cooldown window"); const teamNudgeState = JSON.parse(await readFile(join(teamDir, "worker-stop-nudge.json"), "utf-8")); assert.equal(teamNudgeState.worker, "worker-1"); assert.equal(teamNudgeState.delivery, "sent"); } finally { if (typeof prevPath === "string") process.env.PATH = prevPath; else delete process.env.PATH; await rm(cwd, { recursive: true, force: true }); } }); it("serializes concurrent allowed worker Stop leader nudges with a team lock", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-team-worker-concurrent-dedupe-")); const prevPath = process.env.PATH; try { const stateDir = join(cwd, ".omx", "state"); const logsDir = join(cwd, ".omx", "logs"); const teamName = "worker-stop-concurrent"; const teamDir = join(stateDir, "team", teamName); const fakeBinDir = join(cwd, "fake-bin"); const tmuxLogPath = join(cwd, "tmux.log"); await mkdir(fakeBinDir, { recursive: true }); await writeFile(join(fakeBinDir, "tmux"), buildWorkerStopFakeTmux(tmuxLogPath, { sendDelayMs: 100 })); await chmod(join(fakeBinDir, "tmux"), 0o755); await writeJson(join(teamDir, "manifest.v2.json"), { name: teamName, tmux_session: "omx-team-worker-stop", leader_pane_id: "%42", leader_pane_pid: 12345, tmux_pane_owner_id: "team:test", workers: [ { name: "worker-1", index: 1, pane_id: "%10", pid: 12310 }, { name: "worker-2", index: 2, pane_id: "%11", pid: 12311 }, ], }); process.env.PATH = `${fakeBinDir}:${prevPath || ""}`; const results = await Promise.all([ maybeNudgeLeaderForAllowedWorkerStop({ stateDir, logsDir, workerContext: { teamName, workerName: "worker-1" }, }), maybeNudgeLeaderForAllowedWorkerStop({ stateDir, logsDir, workerContext: { teamName, workerName: "worker-2" }, }), ]); assert.equal(results.filter((result) => result.result === "sent").length, 1); assert.equal(results.filter((result) => result.result === "suppressed_team_lock_held").length, 1); const tmuxLog = await readFile(tmuxLogPath, "utf-8"); const stopNudges = tmuxLog.match(/send-keys -t %42 -l \[OMX\] worker-\d+ native Stop allowed/g) || []; assert.equal(stopNudges.length, 1, "concurrent same-team workers should emit only one leader nudge"); assert.equal(existsSync(join(teamDir, "worker-stop-nudge.lock")), false); } finally { if (typeof prevPath === "string") process.env.PATH = prevPath; else delete process.env.PATH; await rm(cwd, { recursive: true, force: true }); } }); it("skips worker Stop leader nudge when team state is missing or shut down", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-team-worker-missing-team-")); try { const stateDir = join(cwd, ".omx", "state"); const logsDir = join(cwd, ".omx", "logs"); const result = await maybeNudgeLeaderForAllowedWorkerStop({ stateDir, logsDir, workerContext: { teamName: "removed-team", workerName: "worker-1" }, }); assert.equal(result.result, "team_state_gone_or_shutdown"); assert.equal(existsSync(join(stateDir, "team", "removed-team", "worker-stop-nudge.json")), false); await writeJson(join(stateDir, "team", "shutdown-team", "shutdown.json"), { started_at: new Date().toISOString(), }); const shutdownResult = await maybeNudgeLeaderForAllowedWorkerStop({ stateDir, logsDir, workerContext: { teamName: "shutdown-team", workerName: "worker-1" }, }); assert.equal(shutdownResult.result, "team_state_gone_or_shutdown"); assert.equal(existsSync(join(stateDir, "team", "shutdown-team", "worker-stop-nudge.json")), false); const deliveryLogPath = join(logsDir, `team-delivery-${new Date().toISOString().split("T")[0]}.jsonl`); const deliveryEvents = (await readFile(deliveryLogPath, "utf-8")) .trim() .split("\n") .map((line) => JSON.parse(line)); const suppressedEvents = deliveryEvents.filter((event) => event.reason === "team_state_gone_or_shutdown"); assert.equal(suppressedEvents.length, 2, "late closed-team Stop nudges should be diagnostics, not queued prompts"); assert.equal(suppressedEvents.every((event) => event.result === "suppressed" && event.transport === "none"), true); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not treat old visible worker Stop transcript as pending queue state", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-team-worker-queue-dedupe-")); const prevPath = process.env.PATH; try { const stateDir = join(cwd, ".omx", "state"); const logsDir = join(cwd, ".omx", "logs"); const teamName = "queued-stop-dedupe"; const teamDir = join(stateDir, "team", teamName); const fakeBinDir = join(cwd, "fake-bin"); const tmuxLogPath = join(cwd, "tmux.log"); await mkdir(fakeBinDir, { recursive: true }); await writeFile( join(fakeBinDir, "tmux"), buildWorkerStopFakeTmux(tmuxLogPath, { busyLeader: true, captureText: `[OMX] worker-1 native Stop allowed. Run \`omx team status ${teamName}\`, read worker messages/results, then assign next task, reconcile completion, or shut down. [OMX_TMUX_INJECT]\n` + "• Working… (esc to interrupt)", }), ); await chmod(join(fakeBinDir, "tmux"), 0o755); await writeJson(join(teamDir, "manifest.v2.json"), { name: teamName, tmux_session: "omx-team-worker-stop", leader_pane_id: "%42", leader_pane_pid: 12345, tmux_pane_owner_id: "team:test", workers: [{ name: "worker-2", index: 2, pane_id: "%11", pid: 12311 }], }); process.env.PATH = `${fakeBinDir}:${prevPath || ""}`; const result = await maybeNudgeLeaderForAllowedWorkerStop({ stateDir, logsDir, workerContext: { teamName, workerName: "worker-2" }, }); assert.equal(result.result, "steered"); const tmuxLog = await readFile(tmuxLogPath, "utf-8"); assert.match(tmuxLog, /send-keys -t %42 -l \[omx:team-notice-ledger:[a-f0-9]{24}\] Review current Team notices\./); assert.doesNotMatch(tmuxLog, /send-keys -t %42 -l .*worker-2 native Stop allowed|send-keys -t %42 Tab/); const teamNudgeState = JSON.parse(await readFile(join(teamDir, "worker-stop-nudge.json"), "utf-8")); assert.equal(teamNudgeState.worker, "worker-2"); assert.equal(teamNudgeState.delivery, "steered"); } finally { if (typeof prevPath === "string") process.env.PATH = prevPath; else delete process.env.PATH; await rm(cwd, { recursive: true, force: true }); } }); it("reports deferred when non-teardown persistence failure prevents worker Stop nudge cooldown state", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-team-worker-persist-fail-")); const prevPath = process.env.PATH; try { const stateDir = join(cwd, ".omx", "state"); const logsDir = join(cwd, ".omx", "logs"); const teamName = "worker-stop-persist-fail"; const teamDir = join(stateDir, "team", teamName); const fakeBinDir = join(cwd, "fake-bin"); const tmuxLogPath = join(cwd, "tmux.log"); await mkdir(fakeBinDir, { recursive: true }); await writeJson(join(teamDir, "manifest.v2.json"), { name: teamName, tmux_session: "omx-team-worker-stop", leader_pane_id: "%42", leader_pane_pid: 12345, tmux_pane_owner_id: "team:test", workers: [{ name: "worker-1", index: 1, pane_id: "%10", pid: 12310 }], }); await writeFile(join(teamDir, "workers"), "not a directory"); await writeFile(join(fakeBinDir, "tmux"), buildWorkerStopFakeTmux(tmuxLogPath)); await chmod(join(fakeBinDir, "tmux"), 0o755); process.env.PATH = `${fakeBinDir}:${prevPath || ""}`; const result = await maybeNudgeLeaderForAllowedWorkerStop({ stateDir, logsDir, workerContext: { teamName, workerName: "worker-1" }, }); assert.equal(result.result, "deferred"); assert.equal(existsSync(join(teamDir, "worker-stop-nudge.json")), false); assert.equal(existsSync(join(teamDir, "workers", "worker-1", "worker-stop-nudge.json")), false); const tmuxLog = await readFile(tmuxLogPath, "utf-8"); assert.match(tmuxLog, /send-keys -t %42 -l \[OMX\] worker-1 native Stop allowed/); const deliveryLogPath = join(logsDir, `team-delivery-${new Date().toISOString().split("T")[0]}.jsonl`); const deliveryEvents = (await readFile(deliveryLogPath, "utf-8")) .trim() .split("\n") .map((line) => JSON.parse(line)); const deferredEvent = deliveryEvents.find((event) => event.event === "nudge_triggered" && event.result === "deferred"); assert.equal(deferredEvent?.team, teamName); assert.equal(deferredEvent?.from_worker, "worker-1"); assert.match(String(deferredEvent?.reason || ""), /EEXIST|ENOTDIR|not a directory|file already exists/); } finally { if (typeof prevPath === "string") process.env.PATH = prevPath; else delete process.env.PATH; await rm(cwd, { recursive: true, force: true }); } }); it("does not recreate team state when teardown removes it during worker Stop delivery", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-team-worker-teardown-race-")); const prevPath = process.env.PATH; try { const stateDir = join(cwd, ".omx", "state"); const logsDir = join(cwd, ".omx", "logs"); const teamName = "worker-stop-teardown-race"; const teamDir = join(stateDir, "team", teamName); const fakeBinDir = join(cwd, "fake-bin"); const tmuxLogPath = join(cwd, "tmux.log"); await mkdir(fakeBinDir, { recursive: true }); await writeJson(join(teamDir, "manifest.v2.json"), { name: teamName, tmux_session: "omx-team-worker-stop", leader_pane_id: "%42", leader_pane_pid: 12345, tmux_pane_owner_id: "team:test", workers: [{ name: "worker-1", index: 1, pane_id: "%10", pid: 12310 }], }); await writeFile(join(fakeBinDir, "tmux"), buildWorkerStopFakeTmux(tmuxLogPath, { removePathOnSend: teamDir })); await chmod(join(fakeBinDir, "tmux"), 0o755); process.env.PATH = `${fakeBinDir}:${prevPath || ""}`; const result = await maybeNudgeLeaderForAllowedWorkerStop({ stateDir, logsDir, workerContext: { teamName, workerName: "worker-1" }, }); assert.equal(result.result, "sent"); assert.equal(existsSync(teamDir), false, "worker Stop delivery must not recreate removed team state"); const tmuxLog = await readFile(tmuxLogPath, "utf-8"); assert.match(tmuxLog, /send-keys -t %42 -l \[OMX\] worker-1 native Stop allowed/); } finally { if (typeof prevPath === "string") process.env.PATH = prevPath; else delete process.env.PATH; await rm(cwd, { recursive: true, force: true }); } }); it("does not recreate team state when teardown removes it before deferred worker Stop recording", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-team-worker-deferred-teardown-")); const prevPath = process.env.PATH; try { const stateDir = join(cwd, ".omx", "state"); const logsDir = join(cwd, ".omx", "logs"); const teamName = "worker-stop-deferred-teardown"; const teamDir = join(stateDir, "team", teamName); const fakeBinDir = join(cwd, "fake-bin"); const tmuxLogPath = join(cwd, "tmux.log"); await mkdir(fakeBinDir, { recursive: true }); await writeJson(join(teamDir, "manifest.v2.json"), { name: teamName, tmux_session: "omx-team-worker-stop", leader_pane_id: "%42", leader_pane_pid: 12345, tmux_pane_owner_id: "team:test", workers: [{ name: "worker-1", index: 1, pane_id: "%10", pid: 12310 }], }); await writeFile( join(fakeBinDir, "tmux"), buildWorkerStopFakeTmux(tmuxLogPath, { currentCommand: "bash", captureText: "$ ", removePathOnCapture: teamDir, }), ); await chmod(join(fakeBinDir, "tmux"), 0o755); process.env.PATH = `${fakeBinDir}:${prevPath || ""}`; const result = await maybeNudgeLeaderForAllowedWorkerStop({ stateDir, logsDir, workerContext: { teamName, workerName: "worker-1" }, }); assert.equal(result.result, "team_state_gone_or_shutdown"); assert.equal(existsSync(teamDir), false, "deferred worker Stop recording must not recreate removed team state"); const tmuxLog = await readFile(tmuxLogPath, "utf-8"); assert.doesNotMatch(tmuxLog, /send-keys -t %42 -l \[OMX\] worker-1 native Stop allowed/); const deliveryLogPath = join(logsDir, `team-delivery-${new Date().toISOString().split("T")[0]}.jsonl`); const deliveryEvents = (await readFile(deliveryLogPath, "utf-8")) .trim() .split("\n") .map((line) => JSON.parse(line)); assert.equal( deliveryEvents.some((event) => event.team === teamName && event.result === "suppressed" && event.transport === "none" && event.reason === "team_state_gone_or_shutdown" ), true, "teardown-race worker Stop nudges should be diagnostic suppression events, not queued prompts", ); } finally { if (typeof prevPath === "string") process.env.PATH = prevPath; else delete process.env.PATH; await rm(cwd, { recursive: true, force: true }); } }); it("allows worker Stop when the Stop nudge helper cannot deliver", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-team-worker-helper-fail-")); const prevTeamWorker = process.env.OMX_TEAM_WORKER; const prevTeamStateRoot = process.env.OMX_TEAM_STATE_ROOT; const prevPath = process.env.PATH; try { await initTeamState( "worker-stop-helper-fail", "worker stop helper failure", "executor", 1, cwd, undefined, { ...process.env, OMX_SESSION_ID: "sess-stop-team-worker-helper-fail" }, ); const fakeBinDir = join(cwd, "fake-bin"); await mkdir(fakeBinDir, { recursive: true }); await writeFile(join(fakeBinDir, "tmux"), buildWorkerStopFakeTmux(join(cwd, "tmux.log"), { failSend: true })); await chmod(join(fakeBinDir, "tmux"), 0o755); const stateDir = join(cwd, ".omx", "state"); const workerDir = join(stateDir, "team", "worker-stop-helper-fail", "workers", "worker-1"); await writeJson(join(stateDir, "team", "worker-stop-helper-fail", "config.json"), { name: "worker-stop-helper-fail", tmux_session: "omx-team-worker-stop", leader_pane_id: "%42", leader_pane_pid: 12345, tmux_pane_owner_id: "team:test", workers: [{ name: "worker-1", index: 1, pane_id: "%10", pid: 12310 }], }); await writeJson(join(stateDir, "team", "worker-stop-helper-fail", "manifest.v2.json"), { name: "worker-stop-helper-fail", tmux_session: "omx-team-worker-stop", leader_pane_id: "%42", leader_pane_pid: 12345, tmux_pane_owner_id: "team:test", workers: [{ name: "worker-1", index: 1, pane_id: "%10", pid: 12310 }], }); await writeJson(join(workerDir, "identity.json"), { name: "worker-1", assigned_tasks: ["1"], team_state_root: stateDir, }); await writeJson(join(workerDir, "status.json"), { state: "done", current_task_id: "1", updated_at: new Date().toISOString(), }); await writeJson(join(stateDir, "team", "worker-stop-helper-fail", "tasks", "task-1.json"), { id: "1", status: "completed", owner: "worker-1", }); process.env.OMX_TEAM_WORKER = "worker-stop-helper-fail/worker-1"; process.env.OMX_TEAM_INTERNAL_WORKER = "worker-stop-helper-fail/worker-1"; process.env.OMX_TEAM_STATE_ROOT = stateDir; process.env.PATH = `${fakeBinDir}:${prevPath || ""}`; const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-team-worker-helper-fail" }, { cwd }, ); assert.equal(result.outputJson, null); const nudgeState = JSON.parse(await readFile(join(workerDir, "worker-stop-nudge.json"), "utf-8")); assert.equal(nudgeState.delivery, "deferred"); } finally { if (typeof prevTeamWorker === "string") process.env.OMX_TEAM_WORKER = prevTeamWorker; else delete process.env.OMX_TEAM_WORKER; if (typeof prevTeamStateRoot === "string") process.env.OMX_TEAM_STATE_ROOT = prevTeamStateRoot; else delete process.env.OMX_TEAM_STATE_ROOT; if (typeof prevPath === "string") process.env.PATH = prevPath; else delete process.env.PATH; await rm(cwd, { recursive: true, force: true }); } }); it("does not treat failed or ambiguous worker task state as completed Stop evidence", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-team-worker-failed-")); const prevTeamWorker = process.env.OMX_TEAM_WORKER; const prevInternalTeamWorker = process.env.OMX_TEAM_INTERNAL_WORKER; const prevTeamStateRoot = process.env.OMX_TEAM_STATE_ROOT; const prevPath = process.env.PATH; try { await initTeamState( "worker-stop-failed-task", "worker stop failed task", "executor", 1, cwd, undefined, { ...process.env, OMX_SESSION_ID: "sess-stop-team-worker-failed" }, ); const fakeBinDir = join(cwd, "fake-bin"); const tmuxLogPath = join(cwd, "tmux.log"); await mkdir(fakeBinDir, { recursive: true }); await writeFile(join(fakeBinDir, "tmux"), buildWorkerStopFakeTmux(tmuxLogPath)); await chmod(join(fakeBinDir, "tmux"), 0o755); const stateDir = join(cwd, ".omx", "state"); const workerDir = join(stateDir, "team", "worker-stop-failed-task", "workers", "worker-1"); await writeJson(join(stateDir, "team", "worker-stop-failed-task", "config.json"), { name: "worker-stop-failed-task", tmux_session: "omx-team-worker-stop", leader_pane_id: "%42", leader_pane_pid: 12345, tmux_pane_owner_id: "team:test", workers: [{ name: "worker-1", index: 1, pane_id: "%10", pid: 12310 }], }); await writeJson(join(workerDir, "identity.json"), { name: "worker-1", assigned_tasks: ["1"], team_state_root: stateDir, }); await writeJson(join(workerDir, "status.json"), { state: "failed", current_task_id: "1", updated_at: new Date().toISOString(), }); await writeJson(join(stateDir, "team", "worker-stop-failed-task", "tasks", "task-1.json"), { id: "1", status: "failed", owner: "worker-1", }); process.env.OMX_TEAM_WORKER = "worker-stop-failed-task/worker-1"; process.env.OMX_TEAM_INTERNAL_WORKER = "worker-stop-failed-task/worker-1"; process.env.OMX_TEAM_STATE_ROOT = stateDir; process.env.PATH = `${fakeBinDir}:${prevPath || ""}`; const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-team-worker-failed", thread_id: "thread-stop-team-worker-failed", turn_id: "turn-stop-team-worker-failed", }, { cwd }, ); assert.equal(result.outputJson?.decision, "block"); assert.match(String(result.outputJson?.stopReason || ""), /non_completed_task_1_failed/); assert.match(JSON.stringify(result.outputJson), /team/i); assert.equal(existsSync(join(workerDir, "worker-stop-nudge.json")), false); const tmuxLog = existsSync(tmuxLogPath) ? await readFile(tmuxLogPath, "utf-8") : ""; assert.doesNotMatch(tmuxLog, /native Stop allowed/); } finally { if (typeof prevTeamWorker === "string") process.env.OMX_TEAM_WORKER = prevTeamWorker; else delete process.env.OMX_TEAM_WORKER; if (typeof prevInternalTeamWorker === "string") process.env.OMX_TEAM_INTERNAL_WORKER = prevInternalTeamWorker; else delete process.env.OMX_TEAM_INTERNAL_WORKER; if (typeof prevTeamStateRoot === "string") process.env.OMX_TEAM_STATE_ROOT = prevTeamStateRoot; else delete process.env.OMX_TEAM_STATE_ROOT; if (typeof prevPath === "string") process.env.PATH = prevPath; else delete process.env.PATH; await rm(cwd, { recursive: true, force: true }); } }); it("blocks worker Stop on missing task assignment without relying on generic team state", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-team-worker-missing-assignment-")); const prevTeamWorker = process.env.OMX_TEAM_WORKER; const prevInternalTeamWorker = process.env.OMX_TEAM_INTERNAL_WORKER; const prevTeamStateRoot = process.env.OMX_TEAM_STATE_ROOT; try { const stateDir = join(cwd, ".omx", "state"); const workerDir = join(stateDir, "team", "worker-missing-assignment", "workers", "worker-1"); await mkdir(workerDir, { recursive: true }); await writeJson(join(workerDir, "identity.json"), { name: "worker-1", assigned_tasks: [], team_state_root: stateDir, }); await writeJson(join(workerDir, "status.json"), { state: "idle", updated_at: new Date().toISOString(), }); process.env.OMX_TEAM_WORKER = "worker-missing-assignment/worker-1"; process.env.OMX_TEAM_INTERNAL_WORKER = "worker-missing-assignment/worker-1"; process.env.OMX_TEAM_STATE_ROOT = stateDir; const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-team-worker-missing-assignment", thread_id: "thread-stop-team-worker-missing-assignment", turn_id: "turn-stop-team-worker-missing-assignment", }, { cwd }, ); assert.equal(result.outputJson?.decision, "block"); assert.equal(result.outputJson?.stopReason, "team_worker_worker-1_missing_task_assignment"); assert.equal(existsSync(join(workerDir, "worker-stop-nudge.json")), false); } finally { if (typeof prevTeamWorker === "string") process.env.OMX_TEAM_WORKER = prevTeamWorker; else delete process.env.OMX_TEAM_WORKER; if (typeof prevInternalTeamWorker === "string") process.env.OMX_TEAM_INTERNAL_WORKER = prevInternalTeamWorker; else delete process.env.OMX_TEAM_INTERNAL_WORKER; if (typeof prevTeamStateRoot === "string") process.env.OMX_TEAM_STATE_ROOT = prevTeamStateRoot; else delete process.env.OMX_TEAM_STATE_ROOT; await rm(cwd, { recursive: true, force: true }); } }); it("blocks unresolved worker Stop before generic auto-nudge can bypass it", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-team-worker-missing-state-")); const prevTeamWorker = process.env.OMX_TEAM_WORKER; const prevInternalTeamWorker = process.env.OMX_TEAM_INTERNAL_WORKER; const prevTeamStateRoot = process.env.OMX_TEAM_STATE_ROOT; const prevPath = process.env.PATH; try { const stateDir = join(cwd, ".omx", "state"); const fakeBinDir = join(cwd, "fake-bin"); const tmuxLogPath = join(cwd, "tmux.log"); await mkdir(fakeBinDir, { recursive: true }); await writeFile(join(fakeBinDir, "tmux"), buildWorkerStopFakeTmux(tmuxLogPath)); await chmod(join(fakeBinDir, "tmux"), 0o755); process.env.OMX_TEAM_WORKER = "worker-missing-state/worker-1"; process.env.OMX_TEAM_INTERNAL_WORKER = "worker-missing-state/worker-1"; process.env.OMX_TEAM_STATE_ROOT = stateDir; process.env.PATH = `${fakeBinDir}:${prevPath || ""}`; const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-team-worker-missing-state", thread_id: "thread-stop-team-worker-missing-state", turn_id: "turn-stop-team-worker-missing-state", last_assistant_message: "Should I proceed?", }, { cwd }, ); assert.equal(result.outputJson?.decision, "block"); assert.equal(result.outputJson?.stopReason, "team_worker_worker-1_missing_state_dir"); assert.doesNotMatch(JSON.stringify(result.outputJson), /auto_nudge/); const tmuxLog = existsSync(tmuxLogPath) ? await readFile(tmuxLogPath, "utf-8") : ""; assert.doesNotMatch(tmuxLog, /native Stop allowed/); } finally { if (typeof prevTeamWorker === "string") process.env.OMX_TEAM_WORKER = prevTeamWorker; else delete process.env.OMX_TEAM_WORKER; if (typeof prevInternalTeamWorker === "string") process.env.OMX_TEAM_INTERNAL_WORKER = prevInternalTeamWorker; else delete process.env.OMX_TEAM_INTERNAL_WORKER; if (typeof prevTeamStateRoot === "string") process.env.OMX_TEAM_STATE_ROOT = prevTeamStateRoot; else delete process.env.OMX_TEAM_STATE_ROOT; if (typeof prevPath === "string") process.env.PATH = prevPath; else delete process.env.PATH; await rm(cwd, { recursive: true, force: true }); } }); it("prefers canonical internal worker identity over public worker identity for Stop nudges", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-team-worker-internal-env-")); const prevTeamWorker = process.env.OMX_TEAM_WORKER; const prevInternalTeamWorker = process.env.OMX_TEAM_INTERNAL_WORKER; const prevTeamStateRoot = process.env.OMX_TEAM_STATE_ROOT; const prevPath = process.env.PATH; try { const stateDir = join(cwd, ".omx", "state"); const fakeBinDir = join(cwd, "fake-bin"); const tmuxLogPath = join(cwd, "tmux.log"); await mkdir(fakeBinDir, { recursive: true }); await writeFile(join(fakeBinDir, "tmux"), buildWorkerStopFakeTmux(tmuxLogPath)); await chmod(join(fakeBinDir, "tmux"), 0o755); const workerDir = join(stateDir, "team", "internal-stop-team", "workers", "worker-1"); await writeJson(join(stateDir, "team", "internal-stop-team", "config.json"), { name: "internal-stop-team", tmux_session: "omx-team-worker-stop", leader_pane_id: "%42", leader_pane_pid: 12345, tmux_pane_owner_id: "team:test", workers: [{ name: "worker-1", index: 1, pane_id: "%10", pid: 12310 }], }); await writeJson(join(workerDir, "identity.json"), { name: "worker-1", assigned_tasks: ["1"], team_state_root: stateDir, }); await writeJson(join(workerDir, "status.json"), { state: "done", current_task_id: "1", updated_at: new Date().toISOString(), }); await writeJson(join(stateDir, "team", "internal-stop-team", "tasks", "task-1.json"), { id: "1", status: "completed", owner: "worker-1", }); process.env.OMX_TEAM_WORKER = "public-stop-team/worker-1"; process.env.OMX_TEAM_INTERNAL_WORKER = "internal-stop-team/worker-1"; process.env.OMX_TEAM_STATE_ROOT = stateDir; process.env.PATH = `${fakeBinDir}:${prevPath || ""}`; const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-team-worker-internal-env", thread_id: "thread-stop-team-worker-internal-env", turn_id: "turn-stop-team-worker-internal-env", }, { cwd }, ); assert.equal(result.outputJson, null); const tmuxLog = await readFile(tmuxLogPath, "utf-8"); assert.match(tmuxLog, /send-keys -t %42 -l \[OMX\] worker-1 native Stop allowed/); assert.equal(existsSync(join(workerDir, "worker-stop-nudge.json")), true); } finally { if (typeof prevTeamWorker === "string") process.env.OMX_TEAM_WORKER = prevTeamWorker; else delete process.env.OMX_TEAM_WORKER; if (typeof prevInternalTeamWorker === "string") process.env.OMX_TEAM_INTERNAL_WORKER = prevInternalTeamWorker; else delete process.env.OMX_TEAM_INTERNAL_WORKER; if (typeof prevTeamStateRoot === "string") process.env.OMX_TEAM_STATE_ROOT = prevTeamStateRoot; else delete process.env.OMX_TEAM_STATE_ROOT; if (typeof prevPath === "string") process.env.PATH = prevPath; else delete process.env.PATH; await rm(cwd, { recursive: true, force: true }); } }); it("blocks worker Stop when canonical task ownership has a newer non-terminal task", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-team-worker-owned-task-")); const prevTeamWorker = process.env.OMX_TEAM_WORKER; const prevInternalTeamWorker = process.env.OMX_TEAM_INTERNAL_WORKER; const prevTeamStateRoot = process.env.OMX_TEAM_STATE_ROOT; const prevPath = process.env.PATH; try { const stateDir = join(cwd, ".omx", "state"); const fakeBinDir = join(cwd, "fake-bin"); const tmuxLogPath = join(cwd, "tmux.log"); await mkdir(fakeBinDir, { recursive: true }); await writeFile(join(fakeBinDir, "tmux"), buildWorkerStopFakeTmux(tmuxLogPath)); await chmod(join(fakeBinDir, "tmux"), 0o755); const workerDir = join(stateDir, "team", "worker-owned-task", "workers", "worker-1"); await writeJson(join(stateDir, "team", "worker-owned-task", "config.json"), { name: "worker-owned-task", tmux_session: "omx-team-worker-stop", leader_pane_id: "%42", leader_pane_pid: 12345, tmux_pane_owner_id: "team:test", workers: [{ name: "worker-1", index: 1, pane_id: "%10", pid: 12310 }], }); await writeJson(join(workerDir, "identity.json"), { name: "worker-1", assigned_tasks: ["1"], team_state_root: stateDir, }); await writeJson(join(workerDir, "status.json"), { state: "done", current_task_id: "1", updated_at: new Date().toISOString(), }); await writeJson(join(stateDir, "team", "worker-owned-task", "tasks", "task-1.json"), { id: "1", status: "completed", owner: "worker-1", }); await writeJson(join(stateDir, "team", "worker-owned-task", "tasks", "task-2.json"), { id: "2", status: "in_progress", owner: "worker-1", }); process.env.OMX_TEAM_WORKER = "worker-owned-task/worker-1"; process.env.OMX_TEAM_INTERNAL_WORKER = "worker-owned-task/worker-1"; process.env.OMX_TEAM_STATE_ROOT = stateDir; process.env.PATH = `${fakeBinDir}:${prevPath || ""}`; const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-team-worker-owned-task", thread_id: "thread-stop-team-worker-owned-task", turn_id: "turn-stop-team-worker-owned-task", }, { cwd }, ); assert.equal(result.outputJson?.decision, "block"); assert.equal(result.outputJson?.stopReason, "team_worker_worker-1_2_in_progress"); assert.equal(existsSync(join(workerDir, "worker-stop-nudge.json")), false); const tmuxLog = existsSync(tmuxLogPath) ? await readFile(tmuxLogPath, "utf-8") : ""; assert.doesNotMatch(tmuxLog, /native Stop allowed/); } finally { if (typeof prevTeamWorker === "string") process.env.OMX_TEAM_WORKER = prevTeamWorker; else delete process.env.OMX_TEAM_WORKER; if (typeof prevInternalTeamWorker === "string") process.env.OMX_TEAM_INTERNAL_WORKER = prevInternalTeamWorker; else delete process.env.OMX_TEAM_INTERNAL_WORKER; if (typeof prevTeamStateRoot === "string") process.env.OMX_TEAM_STATE_ROOT = prevTeamStateRoot; else delete process.env.OMX_TEAM_STATE_ROOT; if (typeof prevPath === "string") process.env.PATH = prevPath; else delete process.env.PATH; await rm(cwd, { recursive: true, force: true }); } }); it("returns Stop continuation output from canonical team state when coarse mode state is missing", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-team-canonical-")); try { await initTeamState( "canonical-team", "canonical stop fallback", "executor", 1, cwd, undefined, { ...process.env, OMX_SESSION_ID: "sess-stop-team-canonical" }, ); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-team-canonical", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.deepEqual(result.outputJson, { decision: "block", reason: `OMX team pipeline is still active (canonical-team) at phase team-exec; continue coordinating until the team reaches a terminal phase.${TEAM_STOP_COMMIT_GUIDANCE}`, stopReason: "team_team-exec", systemMessage: "OMX team pipeline is still active at phase team-exec.", }); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not block Stop from canonical team state owned by another thread", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-team-canonical-other-thread-")); try { await initTeamState( "canonical-other-thread-team", "canonical other-thread stop fallback", "executor", 1, cwd, undefined, { ...process.env, OMX_SESSION_ID: "sess-stop-team-canonical-thread" }, ); const manifestPath = join(cwd, ".omx", "state", "team", "canonical-other-thread-team", "manifest.v2.json"); const manifest = JSON.parse(await readFile(manifestPath, "utf-8")) as Record; await writeJson(manifestPath, { ...manifest, leader: { ...(manifest.leader as Record | undefined), thread_id: "thread-other", }, }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-team-canonical-thread", thread_id: "thread-current", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks Stop from canonical team state owned by the current thread", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-team-canonical-current-thread-")); try { await initTeamState( "canonical-current-thread-team", "canonical current-thread stop fallback", "executor", 1, cwd, undefined, { ...process.env, OMX_SESSION_ID: "sess-stop-team-canonical-current-thread" }, ); const manifestPath = join(cwd, ".omx", "state", "team", "canonical-current-thread-team", "manifest.v2.json"); const manifest = JSON.parse(await readFile(manifestPath, "utf-8")) as Record; await writeJson(manifestPath, { ...manifest, leader: { ...(manifest.leader as Record | undefined), thread_id: "thread-current", }, }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-team-canonical-current-thread", thread_id: "thread-current", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.deepEqual(result.outputJson, { decision: "block", reason: `OMX team pipeline is still active (canonical-current-thread-team) at phase team-exec; continue coordinating until the team reaches a terminal phase.${TEAM_STOP_COMMIT_GUIDANCE}`, stopReason: "team_team-exec", systemMessage: "OMX team pipeline is still active at phase team-exec.", }); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("emits one concise final decision summary and auto-finalize guidance when release-readiness already has a stable final recommendation and no active worker tasks", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-release-readiness-finalize-")); try { await initTeamState( "release-ready-team", "release readiness finalize", "executor", 1, cwd, undefined, { ...process.env, OMX_SESSION_ID: "sess-stop-release-ready" }, ); await writeReleaseReadinessLeaderAttention( "release-ready-team", "sess-stop-release-ready", cwd, { workRemaining: false }, ); await writeReleaseReadinessStateMarker( "sess-stop-release-ready", "release-ready-team", cwd, ); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-release-ready", thread_id: "thread-stop-release-ready", turn_id: "turn-stop-release-ready-1", mode: "release-readiness", last_assistant_message: "Launch-ready: yes", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.deepEqual(result.outputJson, { decision: "block", reason: 'Stable final recommendation already reached with no active worker tasks. Emit exactly one concise final decision summary aligned to "Launch-ready: yes." with no filler or residual acknowledgements (for example "yes"), then stop.', stopReason: "release_readiness_auto_finalize", systemMessage: "OMX release-readiness detected a stable final recommendation with no active worker tasks; emit one concise final decision summary and finalize.", }); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not auto-finalize non-release team stops that happen to contain a stable recommendation summary", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-non-release-readiness-control-")); try { await initTeamState( "general-review-team", "general team stop control", "executor", 1, cwd, undefined, { ...process.env, OMX_SESSION_ID: "sess-stop-general-review" }, ); await writeReleaseReadinessLeaderAttention( "general-review-team", "sess-stop-general-review", cwd, { workRemaining: false }, ); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-general-review", thread_id: "thread-stop-general-review", turn_id: "turn-stop-general-review-1", last_assistant_message: "Launch-ready: yes", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.deepEqual(result.outputJson, { decision: "block", reason: `OMX team pipeline is still active (general-review-team) at phase team-exec; continue coordinating until the team reaches a terminal phase.${TEAM_STOP_COMMIT_GUIDANCE}`, stopReason: "team_team-exec", systemMessage: "OMX team pipeline is still active at phase team-exec.", }); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("honors terminal team run-state before later canonical-team Stop fallback", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-team-terminal-run-state-canonical-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-stop-team-terminal-run-state"; await initTeamState( "terminal-run-state-team", "terminal team stop canonical fallback regression", "executor", 1, cwd, undefined, { ...process.env, OMX_SESSION_ID: sessionId }, ); await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId, cwd }); await writeJson(join(stateDir, "sessions", sessionId, "run-state.json"), { version: 1, mode: "team", active: false, outcome: "finish", lifecycle_outcome: "finished", current_phase: "complete", completed_at: "2026-04-27T12:00:00.000Z", updated_at: "2026-04-27T12:00:00.000Z", }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: sessionId, thread_id: "thread-stop-team-terminal-run-state", turn_id: "turn-stop-team-terminal-run-state-1", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("re-fires canonical-team Stop output for a later fresh Stop reply when coarse mode state is missing", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-team-canonical-refire-")); try { await initTeamState( "canonical-team-refire", "canonical stop fallback refire", "executor", 1, cwd, undefined, { ...process.env, OMX_SESSION_ID: "sess-stop-team-canonical-refire" }, ); await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-team-canonical-refire", thread_id: "thread-stop-team-canonical-refire", turn_id: "turn-stop-team-canonical-refire-1", }, { cwd }, ); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-team-canonical-refire", thread_id: "thread-stop-team-canonical-refire", turn_id: "turn-stop-team-canonical-refire-2", stop_hook_active: true, }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.deepEqual(result.outputJson, { decision: "block", reason: `OMX team pipeline is still active (canonical-team-refire) at phase team-exec; continue coordinating until the team reaches a terminal phase.${TEAM_STOP_COMMIT_GUIDANCE}`, stopReason: "team_team-exec", systemMessage: "OMX team pipeline is still active at phase team-exec.", }); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not block Stop from canonical team state alone when the canonical phase is terminal", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-team-terminal-")); try { await initTeamState( "terminal-team", "terminal stop fallback", "executor", 1, cwd, undefined, { ...process.env, OMX_SESSION_ID: "sess-stop-team-terminal" }, ); await writeJson(join(cwd, ".omx", "state", "team", "terminal-team", "phase.json"), { current_phase: "complete", max_fix_attempts: 3, current_fix_attempt: 0, transitions: [], updated_at: new Date().toISOString(), }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-team-terminal", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("returns Stop continuation output from canonical team state when manifest session ownership is missing", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-team-legacy-")); try { await initTeamState( "legacy-team", "legacy stop fallback", "executor", 1, cwd, undefined, { ...process.env, OMX_SESSION_ID: "sess-stop-team-legacy" }, ); const manifestPath = join(cwd, ".omx", "state", "team", "legacy-team", "manifest.v2.json"); const manifest = JSON.parse(await readFile(manifestPath, "utf-8")) as Record; await writeJson(manifestPath, { ...manifest, leader: { ...(manifest.leader as Record | undefined), session_id: "", }, }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-team-legacy", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.deepEqual(result.outputJson, { decision: "block", reason: `OMX team pipeline is still active (legacy-team) at phase team-exec; continue coordinating until the team reaches a terminal phase.${TEAM_STOP_COMMIT_GUIDANCE}`, stopReason: "team_team-exec", systemMessage: "OMX team pipeline is still active at phase team-exec.", }); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("reads canonical Stop fallback team state from OMX_TEAM_STATE_ROOT when configured", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-team-root-")); const sharedRoot = join(cwd, "shared-root"); const priorTeamStateRoot = process.env.OMX_TEAM_STATE_ROOT; try { process.env.OMX_TEAM_STATE_ROOT = sharedRoot; await initTeamState( "canonical-root-team", "canonical stop root fallback", "executor", 1, cwd, undefined, { ...process.env, OMX_SESSION_ID: "sess-stop-team-root", OMX_TEAM_STATE_ROOT: sharedRoot }, ); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-team-root", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.deepEqual(result.outputJson, { decision: "block", reason: `OMX team pipeline is still active (canonical-root-team) at phase team-exec; continue coordinating until the team reaches a terminal phase.${TEAM_STOP_COMMIT_GUIDANCE}`, stopReason: "team_team-exec", systemMessage: "OMX team pipeline is still active at phase team-exec.", }); assert.equal(existsSync(join(sharedRoot, "team", "canonical-root-team", "phase.json")), true); } finally { if (typeof priorTeamStateRoot === "string") process.env.OMX_TEAM_STATE_ROOT = priorTeamStateRoot; else delete process.env.OMX_TEAM_STATE_ROOT; await rm(cwd, { recursive: true, force: true }); } }); it("ignores stale source-root team Stop fallback when OMX_TEAM_STATE_ROOT is authoritative", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-team-stale-source-root-")); const teamStateRoot = join(cwd, "shared-team-state"); const priorTeamStateRoot = process.env.OMX_TEAM_STATE_ROOT; try { process.env.OMX_TEAM_STATE_ROOT = teamStateRoot; await mkdir(join(cwd, ".omx", "state"), { recursive: true }); await mkdir(join(teamStateRoot, "team", "stale-source-team"), { recursive: true }); await writeJson(join(cwd, ".omx", "state", "team-state.json"), { active: true, team_name: "stale-source-team", current_phase: "team-exec", }); await writeJson(join(teamStateRoot, "team", "stale-source-team", "phase.json"), { current_phase: "team-exec", }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stale-source-team", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson, null); } finally { if (typeof priorTeamStateRoot === "string") process.env.OMX_TEAM_STATE_ROOT = priorTeamStateRoot; else delete process.env.OMX_TEAM_STATE_ROOT; await rm(cwd, { recursive: true, force: true }); } }); it("returns Stop continuation output from canonical team state rooted via OMX_TEAM_STATE_ROOT", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-team-env-root-")); const teamStateRoot = join(cwd, "shared-team-state"); const previousTeamStateRoot = process.env.OMX_TEAM_STATE_ROOT; try { process.env.OMX_TEAM_STATE_ROOT = teamStateRoot; await initTeamState( "env-root-team", "env root stop fallback", "executor", 1, cwd, undefined, { ...process.env, OMX_SESSION_ID: "sess-stop-team-env-root", OMX_TEAM_STATE_ROOT: teamStateRoot, }, ); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-team-env-root", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.deepEqual(result.outputJson, { decision: "block", reason: `OMX team pipeline is still active (env-root-team) at phase team-exec; continue coordinating until the team reaches a terminal phase.${TEAM_STOP_COMMIT_GUIDANCE}`, stopReason: "team_team-exec", systemMessage: "OMX team pipeline is still active at phase team-exec.", }); } finally { if (typeof previousTeamStateRoot === "string") process.env.OMX_TEAM_STATE_ROOT = previousTeamStateRoot; else delete process.env.OMX_TEAM_STATE_ROOT; await rm(cwd, { recursive: true, force: true }); } }); it("silently ignores Stop when a session-scoped team id is not bound to session.json", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-team-session-mismatch-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(join(stateDir, "sessions", "sess-live-team"), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: "sess-other-team" }); await writeJson(join(stateDir, "sessions", "sess-live-team", "team-state.json"), { active: true, mode: "team", current_phase: "team-exec", team_name: "session-live-team", }); await writeJson(join(stateDir, "team", "session-live-team", "phase.json"), { current_phase: "team-exec", max_fix_attempts: 3, current_fix_attempt: 0, transitions: [], updated_at: new Date().toISOString(), }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-live-team", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("returns Stop continuation output for active ralplan skill with matching active mode state and without active subagents", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-skill-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(join(stateDir, "sessions", "sess-stop-skill"), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: "sess-stop-skill" }); await writeJson(join(stateDir, "sessions", "sess-stop-skill", "skill-active-state.json"), { active: true, skill: "ralplan", phase: "planning", }); await writeJson(join(stateDir, "sessions", "sess-stop-skill", "ralplan-state.json"), { active: true, current_phase: "planning", }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-skill", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson?.decision, "block"); assert.match(String(result.outputJson?.reason ?? ""), /Status: continue_from_artifact/); assert.match(String(result.outputJson?.reason ?? ""), /ralplan is still active \(phase: planning\)/); assert.match(String(result.outputJson?.reason ?? ""), /continue from the current ralplan artifact/i); assert.equal(result.outputJson?.stopReason, "skill_ralplan_planning_continue_artifact"); assert.match(String(result.outputJson?.systemMessage ?? ""), /complete, paused for review, waiting for input, or still continuing/); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks Stop release after owner-session ralplan CLI completion when only local lifecycle evidence is available (#3497: Stop continuation not hard-locked by deleted planning gates)", async () => { // Former ralplan Stop hard-lock retired with PreToolUse/planning hard gates. // Stop may still emit advisory continuation for active workflows, but must not // reintroduce unverifiable hard gates. assert.ok(true); }); it("does not block on stale ralplan skill-active state when the matching mode state is absent", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-stale-skill-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(join(stateDir, "sessions", "sess-stop-stale-skill"), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: "sess-stop-stale-skill" }); await writeJson(join(stateDir, "sessions", "sess-stop-stale-skill", "skill-active-state.json"), { active: true, skill: "ralplan", phase: "planning", session_id: "sess-stop-stale-skill", active_skills: [{ skill: "ralplan", phase: "planning", active: true, session_id: "sess-stop-stale-skill", }], }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-stale-skill", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not block when canonical root ralplan state is inactive but session ralplan state is stale active", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-stale-session-ralplan-root-inactive-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-stop-stale-session-ralplan"; await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId }); await writeJson(join(stateDir, "skill-active-state.json"), { active: false, skill: "ralplan", phase: "reviewing", active_skills: [], }); await writeJson(join(stateDir, "ralplan-state.json"), { active: false, mode: "ralplan", current_phase: "complete", session_id: sessionId, cwd, }); await writeJson(join(stateDir, "sessions", sessionId, "skill-active-state.json"), { active: true, skill: "ralplan", phase: "planning", session_id: sessionId, active_skills: [{ skill: "ralplan", phase: "planning", active: true, session_id: sessionId, }], }); await writeJson(join(stateDir, "sessions", sessionId, "ralplan-state.json"), { active: true, mode: "ralplan", current_phase: "planning", session_id: sessionId, }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: sessionId, }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("clears stale ralplan Stop cache when authoritative state is terminal inactive", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-ralplan-terminal-cache-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-stop-ralplan-terminal-cache"; const threadId = "thread-stop-ralplan-terminal-cache"; await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId, cwd }); await writeJson(join(stateDir, "skill-active-state.json"), { active: false, skill: "ralplan", phase: "complete", session_id: sessionId, active_skills: [], }); await writeJson(join(stateDir, "ralplan-state.json"), { active: false, mode: "ralplan", current_phase: "complete", status: "complete", run_outcome: "complete", session_id: sessionId, cwd, }); await writeJson(join(stateDir, "sessions", sessionId, "skill-active-state.json"), { active: false, skill: "ralplan", phase: "complete", session_id: sessionId, active_skills: [], }); await writeJson(join(stateDir, "sessions", sessionId, "ralplan-state.json"), { active: false, mode: "ralplan", current_phase: "complete", status: "complete", run_outcome: "complete", session_id: sessionId, cwd, }); await writeJson(join(stateDir, "native-stop-state.json"), { sessions: { [sessionId]: { last_signature: `skill-stop|${sessionId}|${threadId}|no-message|skill_ralplan_planning_continue_artifact`, updated_at: "2026-07-04T00:00:00.000Z", }, [threadId]: { last_signature: `skill-stop|${sessionId}|${threadId}|no-message|skill_ralplan_planning_continue_artifact`, updated_at: "2026-07-04T00:00:00.000Z", }, }, }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: sessionId, thread_id: threadId, stop_hook_active: true, }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson, null); const stopState = JSON.parse(await readFile(join(stateDir, "native-stop-state.json"), "utf-8")) as { sessions?: Record }; assert.deepEqual(stopState.sessions, {}); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("keeps blocking current session ralplan when canonical root inactive ralplan state lacks project context", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-ralplan-canonical-root-no-cwd-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-stop-ralplan-canonical-root-no-cwd"; await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId }); await writeJson(join(stateDir, "skill-active-state.json"), { active: false, skill: "ralplan", phase: "complete", session_id: sessionId, active_skills: [], }); await writeJson(join(stateDir, "ralplan-state.json"), { active: false, mode: "ralplan", current_phase: "complete", session_id: sessionId, }); await writeJson(join(stateDir, "sessions", sessionId, "skill-active-state.json"), { active: true, skill: "ralplan", phase: "planning", session_id: sessionId, active_skills: [{ skill: "ralplan", phase: "planning", active: true, session_id: sessionId, }], }); await writeJson(join(stateDir, "sessions", sessionId, "ralplan-state.json"), { active: true, mode: "ralplan", current_phase: "planning", session_id: sessionId, cwd, }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: sessionId, }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson?.decision, "block"); assert.equal(result.outputJson?.stopReason, "skill_ralplan_planning_continue_artifact"); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("keeps blocking current session ralplan when root inactive ralplan state belongs to another session", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-session-ralplan-root-other-session-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-stop-current-active-ralplan"; await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId }); await writeJson(join(stateDir, "skill-active-state.json"), { active: false, skill: "ralplan", phase: "complete", session_id: "sess-stop-old-ralplan", active_skills: [], }); await writeJson(join(stateDir, "ralplan-state.json"), { active: false, mode: "ralplan", current_phase: "complete", session_id: "sess-stop-old-ralplan", }); await writeJson(join(stateDir, "sessions", sessionId, "skill-active-state.json"), { active: true, skill: "ralplan", phase: "planning", session_id: sessionId, active_skills: [{ skill: "ralplan", phase: "planning", active: true, session_id: sessionId, }], }); await writeJson(join(stateDir, "sessions", sessionId, "ralplan-state.json"), { active: true, mode: "ralplan", current_phase: "planning", session_id: sessionId, }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: sessionId, }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson?.decision, "block"); assert.equal(result.outputJson?.stopReason, "skill_ralplan_planning_continue_artifact"); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("keeps blocking current session ralplan when root inactive ralplan state is unscoped", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-session-ralplan-root-unscoped-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-stop-unscoped-root-current-active"; await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId }); await writeJson(join(stateDir, "skill-active-state.json"), { active: false, skill: "ralplan", phase: "complete", active_skills: [], }); await writeJson(join(stateDir, "ralplan-state.json"), { active: false, mode: "ralplan", current_phase: "complete", }); await writeJson(join(stateDir, "sessions", sessionId, "skill-active-state.json"), { active: true, skill: "ralplan", phase: "planning", session_id: sessionId, active_skills: [{ skill: "ralplan", phase: "planning", active: true, session_id: sessionId, }], }); await writeJson(join(stateDir, "sessions", sessionId, "ralplan-state.json"), { active: true, mode: "ralplan", current_phase: "planning", session_id: sessionId, }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: sessionId, }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson?.decision, "block"); assert.equal(result.outputJson?.stopReason, "skill_ralplan_planning_continue_artifact"); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("keeps blocking current session ralplan when unscoped root completion lacks a plan artifact", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-session-ralplan-root-no-plan-artifact-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-stop-unscoped-root-no-plan-artifact"; await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId }); await writeJson(join(stateDir, "skill-active-state.json"), { active: false, skill: "ralplan", phase: "complete", active_skills: [], }); await writeJson(join(stateDir, "ralplan-state.json"), { active: false, mode: "ralplan", current_phase: "complete", planning_complete: true, updated_at: "2026-06-21T08:05:00.000Z", }); await writeJson(join(stateDir, "sessions", sessionId, "skill-active-state.json"), { active: true, skill: "ralplan", phase: "planning", session_id: sessionId, active_skills: [{ skill: "ralplan", phase: "planning", active: true, session_id: sessionId, }], }); await writeJson(join(stateDir, "sessions", sessionId, "ralplan-state.json"), { active: true, mode: "ralplan", current_phase: "planning", session_id: sessionId, updated_at: "2026-06-21T08:00:00.000Z", }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: sessionId, }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson?.decision, "block"); assert.equal(result.outputJson?.stopReason, "skill_ralplan_planning_continue_artifact"); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not loop when newer unscoped root ralplan complete state shadows stale session planning", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-stale-session-ralplan-newer-root-complete-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-stop-stale-session-ralplan-newer-root"; await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId }); await writeJson(join(stateDir, "sessions", sessionId, "skill-active-state.json"), { active: true, skill: "ralplan", phase: "planning", session_id: sessionId, active_skills: [{ skill: "ralplan", phase: "planning", active: true, session_id: sessionId, }], }); await writeJson(join(stateDir, "sessions", sessionId, "ralplan-state.json"), { active: true, mode: "ralplan", current_phase: "planning", session_id: sessionId, updated_at: "2026-06-21T08:00:00.000Z", }); await mkdir(join(cwd, ".omx", "plans"), { recursive: true }); await writeFile(join(cwd, ".omx", "plans", "prd-issue-2923.md"), "# PRD\n"); await writeFile(join(cwd, ".omx", "plans", "test-spec-issue-2923.md"), "# Test spec\n"); await writeJson(join(stateDir, "skill-active-state.json"), { active: false, skill: "ralplan", phase: "complete", active_skills: [], }); await writeJson(join(stateDir, "ralplan-state.json"), { active: false, mode: "ralplan", current_phase: "complete", cwd, planning_complete: true, latest_plan_path: ".omx/plans/prd-issue-2923.md", updated_at: "2026-06-21T08:05:00.000Z", }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: sessionId, tool_name: "apply_patch", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("keeps blocking current session ralplan when newer unscoped root completion belongs to another worktree", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-ralplan-cross-worktree-root-complete-")); try { const otherWorktree = join(tmpdir(), "omx-native-hook-other-worktree-2925"); const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-stop-ralplan-cross-worktree-root"; await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId }); await writeJson(join(stateDir, "sessions", sessionId, "skill-active-state.json"), { active: true, skill: "ralplan", phase: "planning", session_id: sessionId, active_skills: [{ skill: "ralplan", phase: "planning", active: true, session_id: sessionId, }], }); await writeJson(join(stateDir, "sessions", sessionId, "ralplan-state.json"), { active: true, mode: "ralplan", current_phase: "planning", session_id: sessionId, cwd, updated_at: "2026-06-21T08:00:00.000Z", }); await writeJson(join(stateDir, "skill-active-state.json"), { active: false, skill: "ralplan", phase: "complete", active_skills: [], }); await writeJson(join(stateDir, "ralplan-state.json"), { active: false, mode: "ralplan", current_phase: "complete", cwd: otherWorktree, planning_complete: true, latest_plan_path: ".omx/plans/prd-issue-2925.md", updated_at: "2026-06-21T08:05:00.000Z", }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: sessionId, tool_name: "apply_patch", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson?.decision, "block"); assert.equal(result.outputJson?.stopReason, "skill_ralplan_planning_continue_artifact"); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("keeps blocking current session ralplan when newer unscoped root completion lacks project context", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-ralplan-root-complete-no-cwd-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-stop-ralplan-root-complete-no-cwd"; await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId }); await writeJson(join(stateDir, "sessions", sessionId, "skill-active-state.json"), { active: true, skill: "ralplan", phase: "planning", session_id: sessionId, active_skills: [{ skill: "ralplan", phase: "planning", active: true, session_id: sessionId, }], }); await writeJson(join(stateDir, "sessions", sessionId, "ralplan-state.json"), { active: true, mode: "ralplan", current_phase: "planning", session_id: sessionId, cwd, updated_at: "2026-06-21T08:00:00.000Z", }); await writeJson(join(stateDir, "skill-active-state.json"), { active: false, skill: "ralplan", phase: "complete", active_skills: [], }); await writeJson(join(stateDir, "ralplan-state.json"), { active: false, mode: "ralplan", current_phase: "complete", planning_complete: true, latest_plan_path: ".omx/plans/prd-issue-2925.md", updated_at: "2026-06-21T08:05:00.000Z", }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: sessionId, tool_name: "apply_patch", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson?.decision, "block"); assert.equal(result.outputJson?.stopReason, "skill_ralplan_planning_continue_artifact"); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not block stale session ralplan when root ralplan is terminal and another root skill is active", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-stale-ralplan-other-root-skill-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-stop-stale-ralplan-other-root-skill"; await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId }); await writeJson(join(stateDir, "skill-active-state.json"), { active: true, skill: "deep-interview", phase: "intent-first", session_id: sessionId, active_skills: [{ skill: "deep-interview", phase: "intent-first", active: true, session_id: sessionId, }], }); await writeJson(join(stateDir, "ralplan-state.json"), { active: false, mode: "ralplan", current_phase: "complete", session_id: sessionId, cwd, }); await writeJson(join(stateDir, "sessions", sessionId, "skill-active-state.json"), { active: true, skill: "ralplan", phase: "planning", session_id: sessionId, active_skills: [{ skill: "ralplan", phase: "planning", active: true, session_id: sessionId, }], }); await writeJson(join(stateDir, "sessions", sessionId, "ralplan-state.json"), { active: true, mode: "ralplan", current_phase: "planning", session_id: sessionId, }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: sessionId, }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("keeps blocking session ralplan when canonical root state is not inactive", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-session-ralplan-root-active-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-stop-session-ralplan-root-active"; await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId }); await writeJson(join(stateDir, "skill-active-state.json"), { active: true, skill: "ralplan", phase: "planning", session_id: sessionId, active_skills: [{ skill: "ralplan", phase: "planning", active: true, session_id: sessionId, }], }); await writeJson(join(stateDir, "sessions", sessionId, "skill-active-state.json"), { active: true, skill: "ralplan", phase: "planning", session_id: sessionId, active_skills: [{ skill: "ralplan", phase: "planning", active: true, session_id: sessionId, }], }); await writeJson(join(stateDir, "sessions", sessionId, "ralplan-state.json"), { active: true, mode: "ralplan", current_phase: "planning", session_id: sessionId, }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: sessionId, }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson?.decision, "block"); assert.equal(result.outputJson?.stopReason, "skill_ralplan_planning_continue_artifact"); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not block on stale ralplan skill-active when canonical run-state is terminal", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-terminal-ralplan-run-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-stop-terminal-ralplan"; await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId }); await writeJson(join(stateDir, "sessions", sessionId, "skill-active-state.json"), { active: true, skill: "ralplan", phase: "planning", session_id: sessionId, active_skills: [{ skill: "ralplan", phase: "planning", active: true, session_id: sessionId, }], }); await writeJson(join(stateDir, "sessions", sessionId, "ralplan-state.json"), { active: true, mode: "ralplan", current_phase: "planning", session_id: sessionId, }); await writeJson(join(stateDir, "sessions", sessionId, "run-state.json"), { version: 1, mode: "ralplan", active: false, outcome: "finish", lifecycle_outcome: "finished", current_phase: "complete", completed_at: "2026-05-01T00:00:00.000Z", updated_at: "2026-05-01T00:00:00.000Z", }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: sessionId, }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not block on stale ralplan skill-active when pinned mode state belongs to another session", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-foreign-ralplan-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-stop-current-ralplan"; await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId }); await writeJson(join(stateDir, "sessions", sessionId, "skill-active-state.json"), { active: true, skill: "ralplan", phase: "planning", session_id: sessionId, active_skills: [{ skill: "ralplan", phase: "planning", active: true, session_id: sessionId, }], }); await writeJson(join(stateDir, "sessions", sessionId, "ralplan-state.json"), { active: true, mode: "ralplan", current_phase: "planning", session_id: "sess-other-ralplan", }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: sessionId, }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("returns an explicit ralplan waiting status while subagents are still active", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-skill-subagent-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(join(stateDir, "sessions", "sess-stop-skill-subagent"), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: "sess-stop-skill-subagent" }); await writeJson(join(stateDir, "sessions", "sess-stop-skill-subagent", "skill-active-state.json"), { active: true, skill: "ralplan", phase: "planning", }); await writeJson(join(stateDir, "sessions", "sess-stop-skill-subagent", "ralplan-state.json"), { active: true, current_phase: "planning", }); await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { "sess-stop-skill-subagent": { session_id: "sess-stop-skill-subagent", leader_thread_id: "leader-1", updated_at: new Date().toISOString(), threads: { "leader-1": { thread_id: "leader-1", kind: "leader", first_seen_at: new Date().toISOString(), last_seen_at: new Date().toISOString(), turn_count: 1, }, "sub-1": { thread_id: "sub-1", kind: "subagent", first_seen_at: new Date().toISOString(), last_seen_at: new Date().toISOString(), turn_count: 1, }, }, }, }, }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-skill-subagent", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson?.decision, "block"); assert.match(String(result.outputJson?.reason ?? ""), /Status: waiting/); assert.match(String(result.outputJson?.reason ?? ""), /waiting for 1 active native subagent thread/); assert.match(String(result.outputJson?.reason ?? ""), /then continue from the current ralplan artifact/i); assert.equal(result.outputJson?.stopReason, "skill_ralplan_planning_waiting_subagent"); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not report ralplan subagent waiting when notify-fallback already recorded completion", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-skill-subagent-complete-")); try { const stateDir = join(cwd, ".omx", "state"); const now = new Date().toISOString(); await mkdir(join(stateDir, "sessions", "sess-stop-skill-subagent-complete"), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: "sess-stop-skill-subagent-complete" }); await writeJson(join(stateDir, "sessions", "sess-stop-skill-subagent-complete", "skill-active-state.json"), { active: true, skill: "ralplan", phase: "planning", }); await writeJson(join(stateDir, "sessions", "sess-stop-skill-subagent-complete", "ralplan-state.json"), { active: true, current_phase: "planning", }); await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { "sess-stop-skill-subagent-complete": { session_id: "sess-stop-skill-subagent-complete", leader_thread_id: "leader-1", updated_at: now, threads: { "leader-1": { thread_id: "leader-1", kind: "leader", first_seen_at: now, last_seen_at: now, turn_count: 1, }, "sub-1": { thread_id: "sub-1", kind: "subagent", first_seen_at: now, last_seen_at: now, completed_at: now, last_completed_turn_id: "turn-complete-1", completion_source: "notify-fallback-watcher", turn_count: 2, }, }, }, }, }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-skill-subagent-complete", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson?.decision, "block"); assert.doesNotMatch(String(result.outputJson?.reason ?? ""), /waiting for 1 active native subagent thread/); assert.equal(result.outputJson?.stopReason, "skill_ralplan_planning_continue_artifact"); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not block on stale root ralplan skill when the explicit session-scoped canonical skill state is absent", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-stale-root-skill-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(stateDir, { recursive: true }); await writeJson(join(stateDir, "skill-active-state.json"), { active: true, skill: "ralplan", phase: "planning", }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-stale-root-skill", thread_id: "thread-stop-stale-root-skill", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks Stop while autoresearch is active without validator completion", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-autoresearch-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(join(stateDir, "sessions", "sess-stop-autoresearch"), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: "sess-stop-autoresearch", cwd }); await writeJson(join(stateDir, "sessions", "sess-stop-autoresearch", "autoresearch-state.json"), { active: true, mode: "autoresearch", current_phase: "executing", session_id: "sess-stop-autoresearch", validation_mode: "mission-validator-script", mission_validator_command: "node scripts/validate.js", completion_artifact_path: '.omx/specs/autoresearch-demo/completion.json', }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-autoresearch", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.deepEqual(result.outputJson, { decision: "block", reason: "OMX autoresearch is still active (phase: executing); continue until validator evidence is complete before stopping.", stopReason: "autoresearch_executing", systemMessage: "OMX autoresearch is still active (phase: executing); continue until validator evidence is complete before stopping.", }); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("allows Stop once autoresearch validator evidence is complete", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-autoresearch-complete-")); try { const stateDir = join(cwd, ".omx", "state"); const specDir = join(cwd, '.omx', 'specs', 'autoresearch-demo'); await mkdir(join(stateDir, "sessions", "sess-stop-autoresearch-complete"), { recursive: true }); await mkdir(specDir, { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: "sess-stop-autoresearch-complete", cwd }); await writeJson(join(stateDir, "sessions", "sess-stop-autoresearch-complete", "autoresearch-state.json"), { active: true, mode: "autoresearch", current_phase: "reviewing", session_id: "sess-stop-autoresearch-complete", validation_mode: "mission-validator-script", mission_validator_command: "node scripts/validate.js", completion_artifact_path: '.omx/specs/autoresearch-demo/completion.json', }); await writeJson(join(specDir, 'completion.json'), { status: 'passed', passed: true }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-autoresearch-complete", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not block Stop from stale root autoresearch state when the explicit session has no scoped autoresearch state", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-stale-root-autoresearch-")); try { const stateDir = join(cwd, ".omx", "state"); const specDir = join(cwd, '.omx', 'specs', 'autoresearch-demo'); await mkdir(join(stateDir, 'sessions', 'sess-current'), { recursive: true }); await mkdir(specDir, { recursive: true }); await writeJson(join(stateDir, 'session.json'), { session_id: 'sess-current', cwd }); await writeJson(join(stateDir, 'autoresearch-state.json'), { active: true, mode: 'autoresearch', current_phase: 'executing', validation_mode: 'mission-validator-script', mission_validator_command: 'node scripts/validate.js', completion_artifact_path: '.omx/specs/autoresearch-demo/completion.json', }); const result = await dispatchCodexNativeHook( { hook_event_name: 'Stop', cwd, session_id: 'sess-current', }, { cwd }, ); assert.equal(result.omxEventName, 'stop'); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not block Stop from stale root autoresearch state when the explicit session directory is missing", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-missing-session-autoresearch-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(stateDir, { recursive: true }); await writeJson(join(stateDir, "autoresearch-state.json"), { active: true, mode: "autoresearch", current_phase: "executing", validation_mode: "mission-validator-script", mission_validator_command: "node scripts/validate.js", completion_artifact_path: ".omx/specs/autoresearch-demo/completion.json", }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "missing-session", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not block Stop solely because deep-interview is active", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-deep-interview-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(join(stateDir, "sessions", "sess-stop-deep-interview"), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: "sess-stop-deep-interview" }); await writeJson(join(stateDir, "sessions", "sess-stop-deep-interview", "skill-active-state.json"), { active: true, skill: "deep-interview", phase: "planning", }); await writeJson(join(stateDir, "sessions", "sess-stop-deep-interview", "deep-interview-state.json"), { active: true, current_phase: "planning", }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-deep-interview", }, { cwd }, ); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks Stop when deep-interview has a pending omx question obligation", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-deep-interview-question-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(join(stateDir, "sessions", "sess-stop-deep-interview-question"), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: "sess-stop-deep-interview-question" }); await writeJson(join(stateDir, "sessions", "sess-stop-deep-interview-question", "skill-active-state.json"), { version: 1, active: true, skill: "deep-interview", phase: "planning", session_id: "sess-stop-deep-interview-question", thread_id: "thread-stop-deep-interview-question", }); await writeJson(join(stateDir, "sessions", "sess-stop-deep-interview-question", "deep-interview-state.json"), { active: true, mode: "deep-interview", current_phase: "intent-first", session_id: "sess-stop-deep-interview-question", thread_id: "thread-stop-deep-interview-question", question_enforcement: { obligation_id: "obligation-1", source: "omx-question", status: "pending", requested_at: "2026-04-19T03:20:00.000Z", }, }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-deep-interview-question", thread_id: "thread-stop-deep-interview-question", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.deepEqual(result.outputJson, { decision: "block", reason: "Deep interview is still active (phase: intent-first) and has a pending structured question obligation; use `omx question` before stopping.", stopReason: "deep_interview_question_required", systemMessage: "OMX deep-interview is still active (phase: intent-first) and requires a structured question via omx question before stopping; read the returned answers[] JSON before continuing.", }); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks Stop when a same-session deep-interview question obligation is pending even after the mode marked itself inactive", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-deep-interview-question-inactive-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(join(stateDir, "sessions", "sess-stop-deep-interview-question-inactive"), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: "sess-stop-deep-interview-question-inactive" }); await writeJson(join(stateDir, "sessions", "sess-stop-deep-interview-question-inactive", "skill-active-state.json"), { version: 1, active: true, skill: "deep-interview", phase: "planning", session_id: "sess-stop-deep-interview-question-inactive", thread_id: "thread-stop-deep-interview-question-inactive", }); await writeJson(join(stateDir, "sessions", "sess-stop-deep-interview-question-inactive", "deep-interview-state.json"), { active: false, mode: "deep-interview", current_phase: "intent-first", lifecycle_outcome: "askuserQuestion", run_outcome: "blocked_on_user", completed_at: "2026-04-19T03:20:30.000Z", session_id: "sess-stop-deep-interview-question-inactive", thread_id: "thread-stop-deep-interview-question-inactive", question_enforcement: { obligation_id: "obligation-inactive", source: "omx-question", status: "pending", lifecycle_outcome: "askuserQuestion", requested_at: "2026-04-19T03:20:00.000Z", }, }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-deep-interview-question-inactive", thread_id: "thread-stop-deep-interview-question-inactive", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.deepEqual(result.outputJson, { decision: "block", reason: "Deep interview is still active (phase: intent-first) and has a pending structured question obligation; use `omx question` before stopping.", stopReason: "deep_interview_question_required", systemMessage: "OMX deep-interview is still active (phase: intent-first) and requires a structured question via omx question before stopping; read the returned answers[] JSON before continuing.", }); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not re-block Stop after a same-session deep-interview question record is already answered", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-deep-interview-question-answered-")); try { const sessionId = "sess-stop-deep-interview-question-answered"; const stateDir = join(cwd, ".omx", "state"); const sessionDir = join(stateDir, "sessions", sessionId); await mkdir(join(sessionDir, "questions"), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId }); await writeJson(join(sessionDir, "skill-active-state.json"), { version: 1, active: true, skill: "deep-interview", phase: "planning", session_id: sessionId, thread_id: "thread-stop-deep-interview-question-answered", }); await writeJson(join(sessionDir, "deep-interview-state.json"), { active: false, mode: "deep-interview", current_phase: "intent-first", lifecycle_outcome: "askuserQuestion", run_outcome: "blocked_on_user", completed_at: "2026-04-19T03:20:30.000Z", session_id: sessionId, thread_id: "thread-stop-deep-interview-question-answered", question_enforcement: { obligation_id: "obligation-answered", source: "omx-question", status: "pending", lifecycle_outcome: "askuserQuestion", requested_at: "2026-04-19T03:20:00.000Z", }, }); await writeJson(join(sessionDir, "questions", "question-answered.json"), { kind: "omx.question/v1", question_id: "question-answered", session_id: sessionId, created_at: "2026-04-19T03:20:05.000Z", updated_at: "2026-04-19T03:20:10.000Z", status: "answered", question: "What should happen next?", options: [{ label: "Continue", value: "continue" }], allow_other: false, other_label: "Other", multi_select: false, type: "single-answerable", source: "deep-interview", answer: { kind: "option", value: "continue", selected_labels: ["Continue"], selected_values: ["continue"], }, }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: sessionId, thread_id: "thread-stop-deep-interview-question-answered", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson, null); const state = JSON.parse( await readFile(join(sessionDir, "deep-interview-state.json"), "utf-8"), ) as { lifecycle_outcome?: string; question_enforcement?: { status?: string; question_id?: string; satisfied_at?: string }; run_outcome?: string; }; assert.equal(state.question_enforcement?.status, "satisfied"); assert.equal(state.question_enforcement?.question_id, "question-answered"); assert.ok(state.question_enforcement?.satisfied_at); assert.equal(state.lifecycle_outcome, undefined); assert.equal(state.run_outcome, undefined); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("keeps blocking pending deep-interview question Stop replays until the obligation changes", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-deep-interview-question-replay-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(join(stateDir, "sessions", "sess-stop-deep-interview-question-replay"), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: "sess-stop-deep-interview-question-replay" }); await writeJson(join(stateDir, "sessions", "sess-stop-deep-interview-question-replay", "skill-active-state.json"), { version: 1, active: true, skill: "deep-interview", phase: "planning", session_id: "sess-stop-deep-interview-question-replay", }); await writeJson(join(stateDir, "sessions", "sess-stop-deep-interview-question-replay", "deep-interview-state.json"), { active: true, mode: "deep-interview", current_phase: "intent-first", question_enforcement: { obligation_id: "obligation-replay", source: "omx-question", status: "pending", requested_at: "2026-04-19T03:20:00.000Z", }, }); const payload = { hook_event_name: "Stop", cwd, session_id: "sess-stop-deep-interview-question-replay", }; const expected = { decision: "block", reason: "Deep interview is still active (phase: intent-first) and has a pending structured question obligation; use `omx question` before stopping.", stopReason: "deep_interview_question_required", systemMessage: "OMX deep-interview is still active (phase: intent-first) and requires a structured question via omx question before stopping; read the returned answers[] JSON before continuing.", }; const first = await dispatchCodexNativeHook(payload, { cwd }); const replay = await dispatchCodexNativeHook({ ...payload, stop_hook_active: true }, { cwd }); assert.equal(first.omxEventName, "stop"); assert.deepEqual(first.outputJson, expected); assert.equal(replay.omxEventName, "stop"); assert.deepEqual(replay.outputJson, expected); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not block Stop once the deep-interview question obligation is satisfied or cleared", async () => { for (const status of ["satisfied", "cleared"] as const) { const cwd = await mkdtemp(join(tmpdir(), `omx-native-hook-stop-deep-interview-question-${status}-`)); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(join(stateDir, "sessions", `sess-stop-deep-interview-question-${status}`), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: `sess-stop-deep-interview-question-${status}` }); await writeJson(join(stateDir, "sessions", `sess-stop-deep-interview-question-${status}`, "skill-active-state.json"), { version: 1, active: true, skill: "deep-interview", phase: "planning", session_id: `sess-stop-deep-interview-question-${status}`, }); await writeJson(join(stateDir, "sessions", `sess-stop-deep-interview-question-${status}`, "deep-interview-state.json"), { active: true, mode: "deep-interview", current_phase: "intent-first", question_enforcement: { obligation_id: `obligation-${status}`, source: "omx-question", status, requested_at: "2026-04-19T03:20:00.000Z", ...(status === "satisfied" ? { question_id: "question-1", satisfied_at: "2026-04-19T03:21:00.000Z" } : { cleared_at: "2026-04-19T03:21:00.000Z", clear_reason: "error" }), }, }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: `sess-stop-deep-interview-question-${status}`, }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } } }); it("ignores pending deep-interview question obligations from another session", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-deep-interview-question-foreign-session-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(join(stateDir, "sessions", "sess-other"), { recursive: true }); await mkdir(join(stateDir, "sessions", "sess-current"), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: "sess-current" }); await writeJson(join(stateDir, "sessions", "sess-other", "skill-active-state.json"), { version: 1, active: true, skill: "deep-interview", phase: "planning", session_id: "sess-other", }); await writeJson(join(stateDir, "sessions", "sess-other", "deep-interview-state.json"), { active: true, mode: "deep-interview", current_phase: "intent-first", question_enforcement: { obligation_id: "obligation-foreign", source: "omx-question", status: "pending", requested_at: "2026-04-19T03:20:00.000Z", }, }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-current", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks a new same-session deep-interview question obligation even after an earlier round was satisfied", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-deep-interview-question-next-round-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(join(stateDir, "sessions", "sess-stop-deep-interview-question-next-round"), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: "sess-stop-deep-interview-question-next-round" }); await writeJson(join(stateDir, "sessions", "sess-stop-deep-interview-question-next-round", "skill-active-state.json"), { version: 1, active: true, skill: "deep-interview", phase: "planning", session_id: "sess-stop-deep-interview-question-next-round", }); await writeJson(join(stateDir, "sessions", "sess-stop-deep-interview-question-next-round", "deep-interview-state.json"), { active: true, mode: "deep-interview", current_phase: "intent-first", question_enforcement: { obligation_id: "obligation-next-round", source: "omx-question", status: "pending", requested_at: "2026-04-19T03:22:00.000Z", question_id: "question-old-round", satisfied_at: "2026-04-19T03:21:00.000Z", }, }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-deep-interview-question-next-round", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.deepEqual(result.outputJson, { decision: "block", reason: "Deep interview is still active (phase: intent-first) and has a pending structured question obligation; use `omx question` before stopping.", stopReason: "deep_interview_question_required", systemMessage: "OMX deep-interview is still active (phase: intent-first) and requires a structured question via omx question before stopping; read the returned answers[] JSON before continuing.", }); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("ignores root skill-active fallback from a different thread when evaluating Stop", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-foreign-thread-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(stateDir, { recursive: true }); await writeJson(join(stateDir, "skill-active-state.json"), { active: true, skill: "deep-interview", phase: "planning", session_id: "", thread_id: "other-thread", }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-main", thread_id: "main-thread", }, { cwd }, ); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("returns a non-blocking Stop document-refresh warning before auto-nudge when Ralph is not active", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-document-refresh-")); try { await mkdir(join(cwd, "src", "scripts"), { recursive: true }); execFileSync("git", ["init"], { cwd, stdio: "ignore" }); execFileSync("git", ["config", "user.email", "test@example.com"], { cwd, stdio: "ignore" }); execFileSync("git", ["config", "user.name", "Test User"], { cwd, stdio: "ignore" }); await writeFile(join(cwd, "src", "scripts", "codex-native-hook.ts"), "export const hook = 1;\n", "utf-8"); execFileSync("git", ["add", "src/scripts/codex-native-hook.ts"], { cwd, stdio: "ignore" }); execFileSync("git", ["commit", "-m", "init"], { cwd, stdio: "ignore" }); await writeFile(join(cwd, "src", "scripts", "codex-native-hook.ts"), "export const hook = 2;\n", "utf-8"); const output = parseSingleJsonStdout(runNativeHookCli({ hook_event_name: "Stop", cwd, session_id: "sess-stop-doc-refresh", last_assistant_message: "Launch-ready: yes", }, { cwd })); assert.deepEqual(Object.keys(output).sort(), ["systemMessage"]); assert.equal("hookSpecificOutput" in output, false); assert.equal("decision" in output, false); assert.equal("reason" in output, false); assert.equal("stopReason" in output, false); assert.match(String(output.systemMessage ?? ""), /Document-refresh warning/); assert.match(String(output.systemMessage ?? ""), /staged \+ unstaged changes/); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not warn on ordinary non-terminal Stop attempts before auto-nudge", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-document-refresh-nonterminal-")); try { await mkdir(join(cwd, "src", "scripts"), { recursive: true }); execFileSync("git", ["init"], { cwd, stdio: "ignore" }); execFileSync("git", ["config", "user.email", "test@example.com"], { cwd, stdio: "ignore" }); execFileSync("git", ["config", "user.name", "Test User"], { cwd, stdio: "ignore" }); await writeFile(join(cwd, "src", "scripts", "codex-native-hook.ts"), "export const hook = 1;\n", "utf-8"); execFileSync("git", ["add", "src/scripts/codex-native-hook.ts"], { cwd, stdio: "ignore" }); execFileSync("git", ["commit", "-m", "init"], { cwd, stdio: "ignore" }); await writeFile(join(cwd, "src", "scripts", "codex-native-hook.ts"), "export const hook = 2;\n", "utf-8"); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-doc-refresh-nonterminal", last_assistant_message: "Continuing implementation; next I will run focused tests.", }, { cwd }, ); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("dedupes identical Stop document-refresh warnings during active Stop-hook replays", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-document-refresh-dedupe-")); try { await mkdir(join(cwd, "src", "scripts"), { recursive: true }); execFileSync("git", ["init"], { cwd, stdio: "ignore" }); execFileSync("git", ["config", "user.email", "test@example.com"], { cwd, stdio: "ignore" }); execFileSync("git", ["config", "user.name", "Test User"], { cwd, stdio: "ignore" }); await writeFile(join(cwd, "src", "scripts", "codex-native-hook.ts"), "export const hook = 1;\n", "utf-8"); execFileSync("git", ["add", "src/scripts/codex-native-hook.ts"], { cwd, stdio: "ignore" }); execFileSync("git", ["commit", "-m", "init"], { cwd, stdio: "ignore" }); await writeFile(join(cwd, "src", "scripts", "codex-native-hook.ts"), "export const hook = 2;\n", "utf-8"); const payload = { hook_event_name: "Stop", cwd, session_id: "sess-stop-doc-refresh-dedupe", last_assistant_message: "Launch-ready: yes", } as const; const first = await dispatchCodexNativeHook(payload, { cwd }); const replay = await dispatchCodexNativeHook({ ...payload, stop_hook_active: true }, { cwd }); assert.match(JSON.stringify(first.outputJson), /Document-refresh warning/); assert.equal(replay.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("suppresses Stop document-refresh warning when the final handoff message includes an exemption", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-document-refresh-exempt-")); try { await mkdir(join(cwd, "src", "scripts"), { recursive: true }); execFileSync("git", ["init"], { cwd, stdio: "ignore" }); execFileSync("git", ["config", "user.email", "test@example.com"], { cwd, stdio: "ignore" }); execFileSync("git", ["config", "user.name", "Test User"], { cwd, stdio: "ignore" }); await writeFile(join(cwd, "src", "scripts", "codex-native-hook.ts"), "export const hook = 1;\n", "utf-8"); execFileSync("git", ["add", "src/scripts/codex-native-hook.ts"], { cwd, stdio: "ignore" }); execFileSync("git", ["commit", "-m", "init"], { cwd, stdio: "ignore" }); await writeFile(join(cwd, "src", "scripts", "codex-native-hook.ts"), "export const hook = 2;\n", "utf-8"); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-doc-refresh-exempt", last_assistant_message: `${DOCUMENT_REFRESH_EXEMPTION_PREFIX} internal-only behavior verified`, }, { cwd }, ); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks Codex App Stop when Ralph is marked complete without completion-audit evidence", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ralph-complete-audit-missing-")); try { const sessionId = "sess-ralph-complete-missing"; const statePath = join(cwd, ".omx", "state", "sessions", sessionId, "ralph-state.json"); await writeJson(join(cwd, ".omx", "state", "session.json"), { session_id: sessionId, native_session_id: sessionId, cwd }); await writeJson(statePath, { active: false, mode: "ralph", current_phase: "complete", session_id: sessionId, completed_at: "2026-05-10T12:00:00.000Z", }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: sessionId, last_assistant_message: "Done. Ralph complete.", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); const reason = String(result.outputJson?.reason); assert.match(reason, /Ralph completion audit is missing required evidence/); assert.match(reason, /set "completion_audit" on the Ralph state object/); assert.doesNotMatch(reason, /state\.completion_audit/); assert.match(reason, /repo-relative JSON file/); assert.match(reason, /Markdown artifacts and flat top-level checklist\/evidence fields are not accepted/); assert.equal(result.outputJson?.stopReason, "ralph_completion_audit_missing_completion_audit"); const reopened = JSON.parse(await readFile(statePath, "utf-8")) as Record; assert.equal(reopened.active, false); assert.equal(reopened.current_phase, "complete"); assert.equal(reopened.completion_audit_gate, "blocked"); assert.equal(reopened.completion_audit_missing_reason, "missing_completion_audit"); assert.equal(reopened.completed_at, "2026-05-10T12:00:00.000Z"); const repeat = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: sessionId, last_assistant_message: "Done. Ralph complete.", }, { cwd }, ); assert.equal(repeat.outputJson?.stopReason, "ralph_completion_audit_missing_completion_audit"); assert.doesNotMatch(String(repeat.outputJson?.reason), /Ralph is still active/); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("allows Codex App Stop when complete Ralph state carries checklist and verification evidence", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ralph-complete-audit-present-")); try { const sessionId = "sess-ralph-complete-present"; await writeJson(join(cwd, ".omx", "state", "session.json"), { session_id: sessionId, native_session_id: sessionId, cwd }); await writeJson(join(cwd, ".omx", "state", "sessions", sessionId, "ralph-state.json"), { active: false, mode: "ralph", current_phase: "complete", session_id: sessionId, completed_at: "2026-05-10T12:00:00.000Z", completion_audit: { passed: true, prompt_to_artifact_checklist: ["issue #2260 fixed", "tests added"], verification_evidence: ["node --test dist/scripts/__tests__/codex-native-hook.test.js"], }, }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: sessionId, last_assistant_message: "Done with completion audit evidence recorded.", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("returns Stop continuation output while Ralph is active without an explicit session pin", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(stateDir, { recursive: true }); await writeFile( join(stateDir, "ralph-state.json"), JSON.stringify({ active: true, current_phase: "executing", }), ); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.deepEqual(result.outputJson, { decision: "block", reason: "OMX Ralph is still active (phase: executing; state: .omx/state/ralph-state.json); continue the task and gather fresh verification evidence before stopping.", stopReason: "ralph_executing", systemMessage: "OMX Ralph is still active (phase: executing; state: .omx/state/ralph-state.json); continue the task and gather fresh verification evidence before stopping.", }); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("silently ignores Stop when a session-scoped Ralph id is not bound to session.json", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-ralph-session-mismatch-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(join(stateDir, "sessions", "sess-live-ralph"), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: "sess-other-ralph" }); await writeJson(join(stateDir, "sessions", "sess-live-ralph", "ralph-state.json"), { active: true, current_phase: "executing", session_id: "sess-live-ralph", }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-live-ralph", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not block Stop from stale session-scoped Ralph state that belongs to another session", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-stale-session-ralph-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(join(stateDir, "sessions", "sess-current"), { recursive: true }); await mkdir(join(stateDir, "sessions", "sess-stale"), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: "sess-current" }); await writeJson(join(stateDir, "sessions", "sess-stale", "ralph-state.json"), { active: true, current_phase: "starting", session_id: "sess-stale", }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-current", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("bounds Stop replays when session.json points to an identity-indeterminate owner", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-stale-current-session-ralph-")); try { const stateDir = join(cwd, ".omx", "state"); const nativeStopStatePath = join(stateDir, "native-stop-state.json"); await mkdir(join(stateDir, "sessions", "sess-dead"), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: "sess-dead", cwd, pid: Number.MAX_SAFE_INTEGER, started_at: "2026-01-01T00:00:00.000Z", }); await writeJson(join(stateDir, "sessions", "sess-dead", "ralph-state.json"), { active: true, current_phase: "verifying", session_id: "sess-dead", }); await writeJson(join(stateDir, "skill-active-state.json"), { active: true, skill: "team", phase: "team-exec", active_skills: [{ skill: "team", phase: "team-exec", active: true, session_id: "sess-dead" }], }); await writeJson(nativeStopStatePath, { sessions: { "sess-dead": { last_signature: "ralph-stop|sess-dead|thread-1|no-message|verifying", updated_at: "2026-04-20T21:00:00.000Z", }, }, }); const ralphStatePath = join(stateDir, "sessions", "sess-dead", "ralph-state.json"); const skillStatePath = join(stateDir, "skill-active-state.json"); const ralphStateBefore = await readFile(ralphStatePath, "utf-8"); const skillStateBefore = await readFile(skillStatePath, "utf-8"); const nativeStopStateBefore = await readFile(nativeStopStatePath, "utf-8"); const payload = { hook_event_name: "Stop" as const, cwd, session_id: "sess-dead", thread_id: "thread-1", }; const first = await dispatchCodexNativeHook(payload, { cwd }); const replay = await dispatchCodexNativeHook( { ...payload, stop_hook_active: true, }, { cwd }, ); assert.equal(first.omxEventName, "stop"); assert.equal(first.outputJson, null); assert.equal(replay.omxEventName, "stop"); assert.equal(replay.outputJson, null); assert.equal(await readFile(nativeStopStatePath, "utf-8"), nativeStopStateBefore); assert.equal(await readFile(ralphStatePath, "utf-8"), ralphStateBefore); assert.equal(await readFile(skillStatePath, "utf-8"), skillStateBefore); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("never reinjects identical Stop authorization failures", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-malformed-session-pointer-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(stateDir, { recursive: true }); await writeFile(join(stateDir, "session.json"), "{not-json"); const payload = { hook_event_name: "Stop" as const, cwd, session_id: "sess-current", last_assistant_message: "The provenance tools are denied, so the pointer cannot be repaired.", }; const attempts = []; for (let index = 0; index < 26; index += 1) { attempts.push(await dispatchCodexNativeHook(payload, { cwd })); } assert.deepEqual(attempts.map((attempt) => attempt.omxEventName), Array(26).fill("stop")); assert.deepEqual(attempts.map((attempt) => attempt.outputJson), Array(26).fill(null)); assert.equal(existsSync(join(stateDir, "native-stop-state.json")), false); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("keeps Stop authorization failures terminal across session, cwd, and reason changes", async () => { const root = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-auth-variants-")); try { const malformedCwd = join(root, "malformed"); const foreignCwd = join(root, "foreign"); const unmatchedCwd = join(root, "unmatched"); await mkdir(join(malformedCwd, ".omx", "state"), { recursive: true }); await mkdir(join(foreignCwd, ".omx", "state"), { recursive: true }); await writeFile(join(malformedCwd, ".omx", "state", "session.json"), "{not-json"); await writeJson(join(foreignCwd, ".omx", "state", "session.json"), { session_id: "foreign-owner", native_session_id: "foreign-owner", cwd: join(root, "other-worktree"), pid: process.pid, }); await writeSessionStart(unmatchedCwd, "selected-owner", { nativeSessionId: "selected-owner", pid: process.pid, }); const cases = [ { cwd: malformedCwd, session_id: "malformed-session" }, { cwd: foreignCwd, session_id: "foreign-owner" }, { cwd: unmatchedCwd, session_id: "unmatched-session" }, ]; for (const payload of cases) { const first = await dispatchCodexNativeHook({ hook_event_name: "Stop", ...payload }, { cwd: payload.cwd }); const changed = await dispatchCodexNativeHook({ hook_event_name: "Stop", ...payload, session_id: `${payload.session_id}-changed`, stop_hook_active: true, }, { cwd: payload.cwd }); assert.equal(first.outputJson, null); assert.equal(changed.outputJson, null); assert.equal(existsSync(join(payload.cwd, ".omx", "state", "native-stop-state.json")), false); } } finally { await rm(root, { recursive: true, force: true }); } }); it("does not inherit authority across sessions and restores ordinary Stop behavior after progress", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-auth-session-isolation-")); try { const stateDir = join(cwd, ".omx", "state"); const ownerSessionId = "selected-owner"; await writeSessionStart(cwd, ownerSessionId, { nativeSessionId: ownerSessionId, pid: process.pid, }); await writeSessionSkillActiveState(stateDir, ownerSessionId, "ralph", "executing"); const ralphStatePath = join(stateDir, "sessions", ownerSessionId, "ralph-state.json"); await writeJson(ralphStatePath, { active: true, mode: "ralph", current_phase: "executing", session_id: ownerSessionId, workingDirectory: cwd, }); const pointerBefore = await readFile(join(stateDir, "session.json"), "utf-8"); const foreignStop = await dispatchCodexNativeHook({ hook_event_name: "Stop", cwd, session_id: "foreign-session", }, { cwd }); assert.equal(foreignStop.outputJson, null); assert.equal(await readFile(join(stateDir, "session.json"), "utf-8"), pointerBefore); const authorizedBlocked = await dispatchCodexNativeHook({ hook_event_name: "Stop", cwd, session_id: ownerSessionId, }, { cwd }); assert.equal(authorizedBlocked.outputJson?.decision, "block"); assert.match(String(authorizedBlocked.outputJson?.stopReason), /^ralph_/); await writeJson(ralphStatePath, { active: false, mode: "ralph", current_phase: "complete", session_id: ownerSessionId, workingDirectory: cwd, completion_audit: { passed: true, prompt_to_artifact_checklist: ["Stop authorization remained session-scoped."], verification_evidence: ["The selected owner retained ordinary Stop enforcement."], }, }); await writeJson(join(stateDir, "sessions", ownerSessionId, "skill-active-state.json"), { active: false, skill: "ralph", phase: "complete", session_id: ownerSessionId, active_skills: [], }); const authorizedAfterProgress = await dispatchCodexNativeHook({ hook_event_name: "Stop", cwd, session_id: ownerSessionId, }, { cwd }); assert.equal(authorizedAfterProgress.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not hard-block Stop on stale session-scoped Ralph starting state after visible active modes are cleared", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-cleared-stale-ralph-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-cleared-ralph"; await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeJson(join(stateDir, "sessions", sessionId, "ralph-state.json"), { active: true, mode: "ralph", current_phase: "starting", session_id: sessionId, }); await writeJson(join(stateDir, "skill-active-state.json"), { active: false, skill: "ralph", active_skills: [], }); const listActive = await executeStateOperation("state_list_active", { workingDirectory: cwd, }); assert.deepEqual(listActive.payload, { active_modes: [] }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: sessionId, }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("allows Stop from stale orphaned session-scoped Ralph starting iteration zero state", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-stale-orphan-starting-ralph-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-stale-orphan-ralph"; await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId, native_session_id: sessionId, cwd }); await writeJson(join(stateDir, "sessions", sessionId, "ralph-state.json"), { active: true, mode: "ralph", current_phase: "starting", iteration: 0, session_id: sessionId, updated_at: "2000-01-01T00:00:00.000Z", }); await writeJson(join(stateDir, "sessions", sessionId, "skill-active-state.json"), { active: true, skill: "ralph", phase: "starting", session_id: sessionId, active_skills: [{ skill: "ralph", phase: "starting", active: true, session_id: sessionId }], }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: sessionId, thread_id: "thread-verifier-terminal", last_assistant_message: "APPROVE: read-only verifier evidence is fresh and sufficient.", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks Stop on visible active session-scoped Ralph starting state and reports its path", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-visible-starting-ralph-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-visible-ralph"; await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeJson(join(stateDir, "sessions", sessionId, "ralph-state.json"), { active: true, mode: "ralph", current_phase: "starting", session_id: sessionId, }); await writeJson(join(stateDir, "sessions", sessionId, "skill-active-state.json"), { active: true, skill: "ralph", phase: "starting", active_skills: [{ skill: "ralph", phase: "starting", active: true, session_id: sessionId }], }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: sessionId, }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.deepEqual(result.outputJson, { decision: "block", reason: "OMX Ralph is still active (phase: starting; state: .omx/state/sessions/sess-visible-ralph/ralph-state.json); continue the task and gather fresh verification evidence before stopping.", stopReason: "ralph_starting", systemMessage: "OMX Ralph is still active (phase: starting; state: .omx/state/sessions/sess-visible-ralph/ralph-state.json); continue the task and gather fresh verification evidence before stopping.", }); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("retires prompt-seeded Ralph starting state when canonical Ralph already completed with audit", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-ralph-shadowed-starting-")); try { const stateDir = join(cwd, ".omx", "state"); const nativeSessionId = "native-hook-seed"; const canonicalSessionId = "omx-runtime-session"; await mkdir(join(stateDir, "sessions", nativeSessionId), { recursive: true }); await mkdir(join(stateDir, "sessions", canonicalSessionId), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: canonicalSessionId, native_session_id: nativeSessionId, cwd, }); await writeJson(join(stateDir, "sessions", nativeSessionId, "ralph-state.json"), { active: true, mode: "ralph", current_phase: "starting", session_id: nativeSessionId, iteration: 0, task_slug: "mvp-h-local-method-preflight-execution", started_at: "2026-05-14T07:00:00.000Z", }); await writeJson(join(stateDir, "sessions", nativeSessionId, "skill-active-state.json"), { active: true, skill: "ralph", phase: "starting", session_id: nativeSessionId, active_skills: [{ skill: "ralph", phase: "starting", active: true, session_id: nativeSessionId }], }); await writeJson(join(stateDir, "sessions", canonicalSessionId, "ralph-state.json"), { active: false, mode: "ralph", current_phase: "complete", session_id: canonicalSessionId, completed_at: "2026-05-14T07:30:00.000Z", completion_audit: { passed: true, prompt_to_artifact_checklist: ["task evidence mapped"], verification_evidence: ["fresh verification evidence recorded"], }, }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: nativeSessionId, }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson, null); const retiredState = JSON.parse(await readFile(join(stateDir, "sessions", nativeSessionId, "ralph-state.json"), "utf-8")); assert.equal(retiredState.active, false); assert.equal(retiredState.current_phase, "complete"); assert.equal(retiredState.stop_reason, "shadowed_by_completed_canonical_ralph"); assert.equal(retiredState.shadowed_by_completed_canonical_ralph.session_id, canonicalSessionId); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not retire prompt-seeded Ralph starting state from a completed canonical Ralph owned by another thread", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-ralph-shadowed-thread-mismatch-")); try { const stateDir = join(cwd, ".omx", "state"); const nativeSessionId = "native-hook-seed"; const canonicalSessionId = "omx-runtime-session"; await mkdir(join(stateDir, "sessions", nativeSessionId), { recursive: true }); await mkdir(join(stateDir, "sessions", canonicalSessionId), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: canonicalSessionId, native_session_id: nativeSessionId, cwd, }); await writeJson(join(stateDir, "sessions", nativeSessionId, "ralph-state.json"), { active: true, mode: "ralph", current_phase: "starting", session_id: nativeSessionId, iteration: 0, task_slug: "mvp-h-local-method-preflight-execution", started_at: "2026-05-14T07:00:00.000Z", }); await writeJson(join(stateDir, "sessions", nativeSessionId, "skill-active-state.json"), { active: true, skill: "ralph", phase: "starting", session_id: nativeSessionId, active_skills: [{ skill: "ralph", phase: "starting", active: true, session_id: nativeSessionId }], }); await writeJson(join(stateDir, "sessions", canonicalSessionId, "ralph-state.json"), { active: false, mode: "ralph", current_phase: "complete", session_id: canonicalSessionId, owner_codex_thread_id: "thread-A", completed_at: "2026-05-14T07:30:00.000Z", completion_audit: { passed: true, prompt_to_artifact_checklist: ["task evidence mapped"], verification_evidence: ["fresh verification evidence recorded"], }, }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: nativeSessionId, thread_id: "thread-B", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.deepEqual(result.outputJson, { decision: "block", reason: "OMX Ralph is still active (phase: starting; state: .omx/state/sessions/native-hook-seed/ralph-state.json); continue the task and gather fresh verification evidence before stopping.", stopReason: "ralph_starting", systemMessage: "OMX Ralph is still active (phase: starting; state: .omx/state/sessions/native-hook-seed/ralph-state.json); continue the task and gather fresh verification evidence before stopping.", }); const preservedState = JSON.parse(await readFile(join(stateDir, "sessions", nativeSessionId, "ralph-state.json"), "utf-8")); assert.equal(preservedState.active, true); assert.equal(preservedState.current_phase, "starting"); assert.equal(preservedState.stop_reason, undefined); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not block Stop from another session-scoped Ralph state when an explicit session_id has no active Ralph state", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-explicit-session-ralph-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(join(stateDir, "sessions", "sess-other"), { recursive: true }); await writeJson(join(stateDir, "sessions", "sess-other", "ralph-state.json"), { active: true, current_phase: "starting", session_id: "sess-other", }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-current", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not block a question-only pane from Ralph state owned by another Codex session", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-ralph-question-pane-")); const previousTmuxPane = process.env.TMUX_PANE; try { const stateDir = join(cwd, ".omx", "state"); const questionSessionId = "sess-question-pane"; const questionNativeSessionId = "codex-question-pane"; await mkdir(join(stateDir, "sessions", questionSessionId), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: questionSessionId, native_session_id: questionNativeSessionId, cwd, }); await writeJson(join(stateDir, "sessions", questionSessionId, "ralph-state.json"), { active: true, mode: "ralph", current_phase: "executing", session_id: questionSessionId, owner_omx_session_id: "sess-ralph-owner", owner_codex_session_id: "codex-ralph-owner", thread_id: "thread-ralph-owner", tmux_pane_id: "%41", }); process.env.TMUX_PANE = "%99"; const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: questionNativeSessionId, thread_id: "thread-question-pane", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson, null); } finally { if (typeof previousTmuxPane === "string") process.env.TMUX_PANE = previousTmuxPane; else delete process.env.TMUX_PANE; await rm(cwd, { recursive: true, force: true }); } }); it("does not block Stop when Ralph skill-active initialization points at another session", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-ralph-stale-skill-active-")); try { const stateDir = join(cwd, ".omx", "state"); const currentSessionId = "sess-current-ralph"; await mkdir(join(stateDir, "sessions", currentSessionId), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: currentSessionId, native_session_id: currentSessionId, cwd, }); await writeJson(join(stateDir, "sessions", currentSessionId, "ralph-state.json"), { active: true, mode: "ralph", current_phase: "verifying", session_id: currentSessionId, owner_omx_session_id: currentSessionId, task_slug: "stale-rebound-task", }); await writeJson(join(stateDir, "sessions", currentSessionId, "skill-active-state.json"), { active: true, skill: "ralph", phase: "verifying", session_id: currentSessionId, initialized_mode: "ralph", initialized_state_path: ".omx/state/sessions/sess-old-ralph/ralph-state.json", active_skills: [{ skill: "ralph", phase: "verifying", active: true, session_id: currentSessionId }], }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: currentSessionId, }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks same-session Ralph Stop continuation when ownership identifiers match", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-ralph-owned-session-")); const previousTmuxPane = process.env.TMUX_PANE; try { const stateDir = join(cwd, ".omx", "state"); const omxSessionId = "sess-ralph-owned"; const nativeSessionId = "codex-ralph-owned"; await mkdir(join(stateDir, "sessions", omxSessionId), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: omxSessionId, native_session_id: nativeSessionId, cwd, }); await writeJson(join(stateDir, "sessions", omxSessionId, "ralph-state.json"), { active: true, mode: "ralph", current_phase: "executing", session_id: omxSessionId, owner_omx_session_id: omxSessionId, owner_codex_session_id: nativeSessionId, thread_id: "thread-ralph-owned", tmux_pane_id: "%42", }); process.env.TMUX_PANE = "%42"; const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: nativeSessionId, thread_id: "thread-ralph-owned", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.deepEqual(result.outputJson, { decision: "block", reason: "OMX Ralph is still active (phase: executing; state: .omx/state/sessions/sess-ralph-owned/ralph-state.json); continue the task and gather fresh verification evidence before stopping.", stopReason: "ralph_executing", systemMessage: "OMX Ralph is still active (phase: executing; state: .omx/state/sessions/sess-ralph-owned/ralph-state.json); continue the task and gather fresh verification evidence before stopping.", }); } finally { if (typeof previousTmuxPane === "string") process.env.TMUX_PANE = previousTmuxPane; else delete process.env.TMUX_PANE; await rm(cwd, { recursive: true, force: true }); } }); it("allows native verifier subagent Stop to complete while leader Ralph remains active", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-ralph-subagent-verdict-")); try { const stateDir = join(cwd, ".omx", "state"); const omxSessionId = "sess-ralph-leader-verifier"; const leaderNativeSessionId = "codex-ralph-leader-verifier"; const childNativeSessionId = "codex-verifier-child"; await mkdir(join(stateDir, "sessions", omxSessionId), { recursive: true }); await writeSessionStart(cwd, omxSessionId, { nativeSessionId: leaderNativeSessionId, }); await writeJson(join(stateDir, "sessions", omxSessionId, "ralph-state.json"), { active: true, mode: "ralph", current_phase: "verifying", session_id: omxSessionId, owner_omx_session_id: omxSessionId, owner_codex_session_id: leaderNativeSessionId, }); const transcriptPath = join(cwd, "verifier-subagent-rollout.jsonl"); await writeFile( transcriptPath, `${JSON.stringify({ type: "session_meta", payload: { id: childNativeSessionId, source: { subagent: { thread_spawn: { parent_thread_id: leaderNativeSessionId, depth: 1, agent_nickname: "Verifier", agent_role: "verifier", }, }, }, agent_nickname: "Verifier", agent_role: "verifier", }, })}\n`, ); await dispatchCodexNativeHook( { hook_event_name: "SessionStart", cwd, session_id: childNativeSessionId, transcript_path: transcriptPath, }, { cwd, sessionOwnerPid: process.pid }, ); const childStop = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: childNativeSessionId, thread_id: childNativeSessionId, last_assistant_message: "Verdict: APPROVED. Evidence is sufficient.", }, { cwd }, ); assert.equal(childStop.omxEventName, "stop"); assert.equal(childStop.outputJson, null); const leaderStop = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: leaderNativeSessionId, thread_id: leaderNativeSessionId, last_assistant_message: "Waiting on verification integration.", }, { cwd }, ); assert.equal(leaderStop.omxEventName, "stop"); assert.deepEqual(leaderStop.outputJson, { decision: "block", reason: "OMX Ralph is still active (phase: verifying; state: .omx/state/sessions/sess-ralph-leader-verifier/ralph-state.json); continue the task and gather fresh verification evidence before stopping.", stopReason: "ralph_verifying", systemMessage: "OMX Ralph is still active (phase: verifying; state: .omx/state/sessions/sess-ralph-leader-verifier/ralph-state.json); continue the task and gather fresh verification evidence before stopping.", }); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("prefers canonical run-state terminal lifecycle before stale session Ralph state during Stop", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-canonical-run-state-ralph-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-canonical-run-state-ralph"; await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId, cwd }); await writeJson(join(stateDir, "sessions", sessionId, "run-state.json"), { version: 1, mode: "ralph", active: false, outcome: "finish", lifecycle_outcome: "finished", current_phase: "complete", completed_at: "2026-04-27T12:00:00.000Z", updated_at: "2026-04-27T12:00:00.000Z", }); await writeJson(join(stateDir, "sessions", sessionId, "ralph-state.json"), { active: true, mode: "ralph", current_phase: "verifying", session_id: sessionId, }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: sessionId, }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not block Stop from root Ralph fallback when the current session has no scoped Ralph state", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-root-fallback-ralph-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(join(stateDir, "sessions", "sess-current"), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: "sess-current", cwd }); await writeJson(join(stateDir, "ralph-state.json"), { active: true, current_phase: "executing", }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-current", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not block Stop when the current session Ralph state is cancelled even if stale root fallback remains", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-cancelled-session-ralph-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(join(stateDir, "sessions", "sess-current"), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: "sess-current", cwd }); await writeJson(join(stateDir, "sessions", "sess-current", "ralph-state.json"), { active: false, current_phase: "cancelled", completed_at: "2026-04-10T23:30:38.000Z", session_id: "sess-current", }); await writeJson(join(stateDir, "ralph-state.json"), { active: true, current_phase: "starting", }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-current", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("no-ops Stop when session.json points to another worktree", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-root-fallback-cwd-mismatch-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(stateDir, { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: "sess-elsewhere", cwd: join(cwd, "..", "different-worktree"), }); await writeJson(join(stateDir, "ralph-state.json"), { active: true, current_phase: "executing", }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-current", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson, null); assert.equal(existsSync(join(stateDir, "native-stop-state.json")), false); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("keeps blocking Ralph Stop replays until the active task advances", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-ralph-replay-")); const previousOmxSessionId = process.env.OMX_SESSION_ID; try { const stateDir = join(cwd, ".omx", "state"); await mkdir(stateDir, { recursive: true }); await writeFile( join(stateDir, "ralph-state.json"), JSON.stringify({ active: true, current_phase: "executing", }), ); process.env.OMX_SESSION_ID = "sess-stop-ralph-replay"; const payload = { hook_event_name: "Stop", cwd, last_assistant_message: "Next active targets:\n\n1. scheduler integration\n\nI am continuing.", }; const expected = { decision: "block", reason: "OMX Ralph is still active (phase: executing; state: .omx/state/ralph-state.json); continue the task and gather fresh verification evidence before stopping.", stopReason: "ralph_executing", systemMessage: "OMX Ralph is still active (phase: executing; state: .omx/state/ralph-state.json); continue the task and gather fresh verification evidence before stopping.", }; const first = await dispatchCodexNativeHook(payload, { cwd }); const replay = await dispatchCodexNativeHook( { ...payload, stop_hook_active: true, }, { cwd }, ); assert.equal(first.omxEventName, "stop"); assert.deepEqual(first.outputJson, expected); assert.equal(replay.omxEventName, "stop"); assert.deepEqual(replay.outputJson, expected); } finally { if (typeof previousOmxSessionId === "string") process.env.OMX_SESSION_ID = previousOmxSessionId; else delete process.env.OMX_SESSION_ID; await rm(cwd, { recursive: true, force: true }); } }); it("lets dispatcher dedupe identical native stop hook replays after Stop payload normalization", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-ralph-hook-dedupe-")); const previousOmxSessionId = process.env.OMX_SESSION_ID; try { const stateDir = join(cwd, ".omx", "state"); await mkdir(join(stateDir, "sessions", "sess-stop-ralph-hook-dedupe"), { recursive: true }); await writeHookCounterPlugin(cwd); await writeFile( join(stateDir, "sessions", "sess-stop-ralph-hook-dedupe", "ralph-state.json"), JSON.stringify({ active: true, current_phase: "executing", session_id: "sess-stop-ralph-hook-dedupe", }), ); process.env.OMX_SESSION_ID = "sess-stop-ralph-hook-dedupe"; const payload = { hook_event_name: "Stop", cwd, session_id: "sess-stop-ralph-hook-dedupe", thread_id: "thread-stop-ralph-hook-dedupe", turn_id: "turn-stop-ralph-hook-dedupe-1", last_assistant_message: "Next active targets:\n\n1. scheduler integration\n\nI am continuing.", }; await dispatchCodexNativeHook(payload, { cwd }); await dispatchCodexNativeHook( { ...payload, stop_hook_active: true, }, { cwd }, ); const marker = JSON.parse( await readFile(join(cwd, ".omx", "stop-hook-counter.json"), "utf-8"), ) as { count: number }; assert.equal(marker.count, 1); } finally { if (typeof previousOmxSessionId === "string") process.env.OMX_SESSION_ID = previousOmxSessionId; else delete process.env.OMX_SESSION_ID; await rm(cwd, { recursive: true, force: true }); } }); it("preserves per-turn native stop hook delivery even when stop_hook_active remains true", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-ralph-hook-refire-")); const previousOmxSessionId = process.env.OMX_SESSION_ID; try { const stateDir = join(cwd, ".omx", "state"); await mkdir(join(stateDir, "sessions", "sess-stop-ralph-hook-refire"), { recursive: true }); await writeHookCounterPlugin(cwd); await writeFile( join(stateDir, "sessions", "sess-stop-ralph-hook-refire", "ralph-state.json"), JSON.stringify({ active: true, current_phase: "executing", session_id: "sess-stop-ralph-hook-refire", }), ); process.env.OMX_SESSION_ID = "sess-stop-ralph-hook-refire"; const payload = { hook_event_name: "Stop", cwd, session_id: "sess-stop-ralph-hook-refire", thread_id: "thread-stop-ralph-hook-refire", turn_id: "turn-stop-ralph-hook-refire-1", last_assistant_message: "Continuing current task.", }; await dispatchCodexNativeHook(payload, { cwd }); await dispatchCodexNativeHook( { ...payload, turn_id: "turn-stop-ralph-hook-refire-2", stop_hook_active: true, }, { cwd }, ); await writeFile( join(stateDir, "sessions", "sess-stop-ralph-hook-refire", "ralph-state.json"), JSON.stringify({ active: true, current_phase: "executing", session_id: "sess-stop-ralph-hook-refire", }), ); await dispatchCodexNativeHook( { ...payload, turn_id: "turn-stop-ralph-hook-refire-3", stop_hook_active: true, }, { cwd }, ); const marker = JSON.parse( await readFile(join(cwd, ".omx", "stop-hook-counter.json"), "utf-8"), ) as { count: number }; assert.equal(marker.count, 3); } finally { if (typeof previousOmxSessionId === "string") process.env.OMX_SESSION_ID = previousOmxSessionId; else delete process.env.OMX_SESSION_ID; await rm(cwd, { recursive: true, force: true }); } }); it("returns Stop continuation output for native auto-nudge stall prompts", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-auto-nudge-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(stateDir, { recursive: true }); process.env.OMX_SESSION_ID = "sess-stop-auto"; const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-auto", last_assistant_message: "Keep going and finish the cleanup.", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.deepEqual(result.outputJson, { decision: "block", reason: DEFAULT_AUTO_NUDGE_RESPONSE, stopReason: "auto_nudge", systemMessage: "OMX native Stop detected a stall/permission-style handoff and continued the turn automatically.", }); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("allows a native subagent Stop instead of auto-nudging it into another turn", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-auto-nudge-subagent-stop-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(stateDir, { recursive: true }); await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { "sess-stop-auto-child": { session_id: "sess-stop-auto-child", leader_thread_id: "thread-leader", updated_at: new Date().toISOString(), threads: { "thread-leader": { thread_id: "thread-leader", kind: "leader", first_seen_at: new Date().toISOString(), last_seen_at: new Date().toISOString(), turn_count: 1, }, "thread-child": { thread_id: "thread-child", kind: "subagent", first_seen_at: new Date().toISOString(), last_seen_at: new Date().toISOString(), turn_count: 1, mode: "executor", }, }, }, }, }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-auto-child", thread_id: "thread-child", last_assistant_message: "Keep going and finish the cleanup.", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("bounds repeated ordinary working Stop loops with a diagnostic summary", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-working-loop-")); try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); process.env.OMX_SESSION_ID = "sess-working-loop"; process.env.OMX_NATIVE_STOP_NO_PROGRESS_MAX_REPEATS = "2"; process.env.OMX_NATIVE_STOP_NO_PROGRESS_IDLE_MS = "0"; const payload = { hook_event_name: "Stop", cwd, session_id: "sess-working-loop", thread_id: "thread-working-loop", turn_id: "turn-working-loop-1", last_assistant_message: "Keep going and finish the cleanup.", }; const first = await dispatchCodexNativeHook(payload, { cwd }); assert.equal(first.outputJson?.stopReason, "auto_nudge"); const repeated = await dispatchCodexNativeHook( { ...payload, turn_id: "turn-working-loop-2", stop_hook_active: true, }, { cwd }, ); assert.equal(repeated.omxEventName, "stop"); assert.equal(repeated.outputJson?.decision, "block"); assert.equal(repeated.outputJson?.stopReason, "ordinary_task_no_progress_guard"); assert.match(String(repeated.outputJson?.systemMessage), /no-progress guard triggered/); assert.match(String(repeated.outputJson?.systemMessage), /diagnostic summary/); assert.match(String(repeated.outputJson?.systemMessage), /complete, blocked, failed, or needs missing information/); const persisted = JSON.parse( await readFile(join(cwd, ".omx", "state", "native-stop-state.json"), "utf-8"), ) as { sessions: Record }; assert.equal(persisted.sessions["sess-working-loop"]?.ordinary_no_progress_guard?.repeat_count, 2); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("re-blocks duplicate native auto-nudge replays for the same Stop reply", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-auto-nudge-once-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(stateDir, { recursive: true }); process.env.OMX_SESSION_ID = "sess-stop-auto-once"; await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-auto-once", thread_id: "thread-stop-auto", turn_id: "turn-stop-auto-1", last_assistant_message: "Keep going and finish the cleanup.", }, { cwd }, ); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-auto-once", thread_id: "thread-stop-auto", turn_id: "turn-stop-auto-1", stop_hook_active: true, last_assistant_message: "Keep going and finish the cleanup.", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.deepEqual(result.outputJson, { decision: "block", reason: DEFAULT_AUTO_NUDGE_RESPONSE, stopReason: "auto_nudge", systemMessage: "OMX native Stop detected a stall/permission-style handoff and continued the turn automatically.", }); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("re-blocks duplicate native auto-nudge replays across native/canonical session-id drift", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-auto-nudge-session-drift-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(stateDir, { recursive: true }); process.env.OMX_SESSION_ID = "omx-canonical"; await writeJson(join(stateDir, "session.json"), { session_id: "omx-canonical", native_session_id: "codex-native", }); await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "codex-native", thread_id: "thread-stop-auto-drift", turn_id: "turn-stop-auto-drift-1", last_assistant_message: "Keep going and finish the cleanup.", }, { cwd }, ); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "omx-canonical", thread_id: "thread-stop-auto-drift", turn_id: "turn-stop-auto-drift-1", stop_hook_active: true, last_assistant_message: "Keep going and finish the cleanup.", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.deepEqual(result.outputJson, { decision: "block", reason: DEFAULT_AUTO_NUDGE_RESPONSE, stopReason: "auto_nudge", systemMessage: "OMX native Stop detected a stall/permission-style handoff and continued the turn automatically.", }); const persisted = JSON.parse( await readFile(join(stateDir, "native-stop-state.json"), "utf-8"), ) as { sessions?: Record }; assert.deepEqual(Object.keys(persisted.sessions ?? {}), ["omx-canonical"]); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("dedupes native stop hook replay across owner launch SessionStart reconciliation drift", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-dispatch-session-drift-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(join(stateDir, "sessions", "omx-canonical"), { recursive: true }); await writeHookCounterPlugin(cwd); process.env.OMX_SESSION_ID = "omx-canonical"; await writeSessionStart(cwd, "omx-canonical"); await writeJson(join(stateDir, "sessions", "omx-canonical", "ralph-state.json"), { active: true, current_phase: "executing", session_id: "omx-canonical", }); await dispatchCodexNativeHook( { hook_event_name: "SessionStart", cwd, session_id: "codex-native-new", }, { cwd, sessionOwnerPid: process.pid }, ); await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "codex-native-new", thread_id: "thread-stop-hook-drift", turn_id: "turn-stop-hook-drift-1", last_assistant_message: "Keep going and finish the cleanup.", }, { cwd }, ); await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "omx-canonical", thread_id: "thread-stop-hook-drift", turn_id: "turn-stop-hook-drift-1", stop_hook_active: true, last_assistant_message: "Keep going and finish the cleanup.", }, { cwd }, ); const marker = JSON.parse( await readFile(join(cwd, ".omx", "stop-hook-counter.json"), "utf-8"), ) as { count: number }; assert.equal(marker.count, 1); const sessionState = JSON.parse( await readFile(join(stateDir, "session.json"), "utf-8"), ) as { session_id?: string; native_session_id?: string }; assert.equal(sessionState.session_id, "omx-canonical"); assert.equal(sessionState.native_session_id, "codex-native-new"); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("re-fires native auto-nudge for a later fresh Stop reply even when stop_hook_active is true", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-auto-nudge-refire-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(stateDir, { recursive: true }); process.env.OMX_SESSION_ID = "sess-stop-auto-refire"; await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-auto-refire", thread_id: "thread-stop-auto-refire", turn_id: "turn-stop-auto-refire-1", last_assistant_message: "Keep going and finish the cleanup.", }, { cwd }, ); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-auto-refire", thread_id: "thread-stop-auto-refire", turn_id: "turn-stop-auto-refire-2", stop_hook_active: true, last_assistant_message: "Continue with the cleanup from here.", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.deepEqual(result.outputJson, { decision: "block", reason: DEFAULT_AUTO_NUDGE_RESPONSE, stopReason: "auto_nudge", systemMessage: "OMX native Stop detected a stall/permission-style handoff and continued the turn automatically.", }); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("auto-continues native Stop on permission-seeking prompts", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-auto-nudge-permission-")); try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); process.env.OMX_SESSION_ID = "sess-stop-auto-permission"; const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-auto-permission", last_assistant_message: "Would you like me to continue with the cleanup?", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.deepEqual(result.outputJson, { decision: "block", reason: DEFAULT_AUTO_NUDGE_RESPONSE, stopReason: "auto_nudge", systemMessage: "OMX native Stop detected a stall/permission-style handoff and continued the turn automatically.", }); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("auto-continues native Stop on \"if you want\" permission-seeking prompts", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-auto-nudge-if-you-want-")); try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); process.env.OMX_SESSION_ID = "sess-stop-auto-if-you-want"; const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-auto-if-you-want", last_assistant_message: "If you want, I can continue with the cleanup from here.", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.deepEqual(result.outputJson, { decision: "block", reason: DEFAULT_AUTO_NUDGE_RESPONSE, stopReason: "auto_nudge", systemMessage: "OMX native Stop detected a stall/permission-style handoff and continued the turn automatically.", }); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not auto-continue native Stop while deep-interview is waiting on an intent-first question", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-auto-nudge-deep-interview-question-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(join(stateDir, "sessions", "sess-stop-auto-question"), { recursive: true }); process.env.OMX_SESSION_ID = "sess-stop-auto-question"; await writeJson(join(stateDir, "session.json"), { session_id: "sess-stop-auto-question" }); await writeJson(join(stateDir, "sessions", "sess-stop-auto-question", "skill-active-state.json"), { version: 1, active: true, skill: "deep-interview", phase: "planning", session_id: "sess-stop-auto-question", thread_id: "thread-stop-auto-question", input_lock: { active: true, scope: "deep-interview-auto-approval", blocked_inputs: ["yes", "proceed"], message: "Deep interview is active; auto-approval shortcuts are blocked until the interview finishes.", }, }); await writeJson(join(stateDir, "sessions", "sess-stop-auto-question", "deep-interview-state.json"), { active: true, mode: "deep-interview", current_phase: "intent-first", }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-auto-question", thread_id: "thread-stop-auto-question", turn_id: "turn-stop-auto-question-1", last_assistant_message: [ "Round 2 | Target: Decision boundary | Ambiguity: 24%", "", "If an existing project spider still declares session_mode = \"owned\", should ZenX fail loudly so the stale attribute is removed, or should it ignore the attribute and initialize the session pool anyway?", "Keep going once I have your answer.", ].join("\n"), }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("suppresses native auto-nudge re-fire while session-scoped deep-interview state is still active", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-auto-nudge-deep-interview-state-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(join(stateDir, "sessions", "sess-stop-auto-interview"), { recursive: true }); process.env.OMX_SESSION_ID = "sess-stop-auto-interview"; await writeJson(join(stateDir, "session.json"), { session_id: "sess-stop-auto-interview" }); await writeJson(join(stateDir, "sessions", "sess-stop-auto-interview", "deep-interview-state.json"), { active: true, mode: "deep-interview", current_phase: "intent-first", }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-auto-interview", thread_id: "thread-stop-auto-interview", turn_id: "turn-stop-auto-interview-2", stop_hook_active: true, last_assistant_message: "If you want, I can keep going from here.", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("suppresses native auto-nudge when root deep-interview mode state is active and no session is known", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-auto-nudge-deep-interview-mode-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(stateDir, { recursive: true }); await writeJson(join(stateDir, "deep-interview-state.json"), { active: true, mode: "deep-interview", current_phase: "intent-first", }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, turn_id: "turn-stop-auto-mode-1", last_assistant_message: "Would you like me to continue with the next step?", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("treats inherited OMX_SESSION_ID as session-aware for native auto-nudge Stop checks", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-auto-nudge-env-session-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(stateDir, { recursive: true }); process.env.OMX_SESSION_ID = "sess-stop-auto-mode"; const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, thread_id: "thread-stop-auto-env-session", turn_id: "turn-stop-auto-env-session-1", last_assistant_message: "Keep going and finish the cleanup.", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.deepEqual(result.outputJson, { decision: "block", reason: DEFAULT_AUTO_NUDGE_RESPONSE, stopReason: "auto_nudge", systemMessage: "OMX native Stop detected a stall/permission-style handoff and continued the turn automatically.", }); const stopState = JSON.parse(await readFile(join(stateDir, "native-stop-state.json"), "utf-8")) as Record; assert.ok((stopState.sessions as Record)["sess-stop-auto-mode"]); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("ignores generic SESSION_ID for native auto-nudge Stop session scoping", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-auto-nudge-generic-session-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(stateDir, { recursive: true }); process.env.SESSION_ID = "generic-shell-session"; const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, thread_id: "thread-stop-auto-generic-session", turn_id: "turn-stop-auto-generic-session-1", last_assistant_message: "Keep going and finish the cleanup.", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal((result.outputJson as { decision?: string } | null)?.decision, "block"); const stopState = JSON.parse(await readFile(join(stateDir, "native-stop-state.json"), "utf-8")) as Record; const sessions = stopState.sessions as Record; assert.equal(sessions["generic-shell-session"], undefined); assert.ok(sessions["thread-stop-auto-generic-session"]); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not suppress native auto-nudge from stale root deep-interview mode state when the explicit session-scoped mode state is absent", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-auto-nudge-stale-root-mode-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(stateDir, { recursive: true }); process.env.OMX_SESSION_ID = "sess-stop-auto-stale-root-mode"; await writeJson(join(stateDir, "deep-interview-state.json"), { active: true, mode: "deep-interview", current_phase: "intent-first", }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-auto-stale-root-mode", thread_id: "thread-stop-auto-stale-root-mode", turn_id: "turn-stop-auto-stale-root-mode-1", last_assistant_message: "Keep going and finish the cleanup.", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.deepEqual(result.outputJson, { decision: "block", reason: DEFAULT_AUTO_NUDGE_RESPONSE, stopReason: "auto_nudge", systemMessage: "OMX native Stop detected a stall/permission-style handoff and continued the turn automatically.", }); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not suppress native auto-nudge from stale root deep-interview skill state when the explicit session-scoped canonical skill state is absent", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-auto-nudge-stale-root-skill-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(stateDir, { recursive: true }); process.env.OMX_SESSION_ID = "sess-stop-auto-stale-root-skill"; await writeJson(join(stateDir, "skill-active-state.json"), { active: true, skill: "deep-interview", phase: "planning", }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-auto-stale-root-skill", thread_id: "thread-stop-auto-stale-root-skill", turn_id: "turn-stop-auto-stale-root-skill-1", last_assistant_message: "Keep going and finish the cleanup.", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.deepEqual(result.outputJson, { decision: "block", reason: DEFAULT_AUTO_NUDGE_RESPONSE, stopReason: "auto_nudge", systemMessage: "OMX native Stop detected a stall/permission-style handoff and continued the turn automatically.", }); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not suppress native auto-nudge from stale root deep-interview input lock when the explicit session-scoped canonical skill state is absent", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-auto-nudge-stale-root-lock-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(stateDir, { recursive: true }); process.env.OMX_SESSION_ID = "sess-stop-auto-stale-root-lock"; await writeJson(join(stateDir, "skill-active-state.json"), { active: true, skill: "deep-interview", phase: "planning", input_lock: { active: true, scope: "deep-interview-auto-approval", blocked_inputs: ["yes", "proceed"], message: "Deep interview is active; auto-approval shortcuts are blocked until the interview finishes.", }, }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-auto-stale-root-lock", thread_id: "thread-stop-auto-stale-root-lock", turn_id: "turn-stop-auto-stale-root-lock-1", last_assistant_message: "Keep going and finish the cleanup.", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.deepEqual(result.outputJson, { decision: "block", reason: DEFAULT_AUTO_NUDGE_RESPONSE, stopReason: "auto_nudge", systemMessage: "OMX native Stop detected a stall/permission-style handoff and continued the turn automatically.", }); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not suppress native auto-nudge from active root deep-interview state when the current scoped mode state is explicitly inactive", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-auto-nudge-inactive-scoped-mode-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(join(stateDir, "sessions", "sess-stop-auto-inactive-mode"), { recursive: true }); process.env.OMX_SESSION_ID = "sess-stop-auto-inactive-mode"; await writeJson(join(stateDir, "session.json"), { session_id: "sess-stop-auto-inactive-mode" }); await writeJson(join(stateDir, "sessions", "sess-stop-auto-inactive-mode", "deep-interview-state.json"), { active: false, mode: "deep-interview", current_phase: "completed", }); await writeJson(join(stateDir, "deep-interview-state.json"), { active: true, mode: "deep-interview", current_phase: "intent-first", }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-auto-inactive-mode", thread_id: "thread-stop-auto-inactive-mode", turn_id: "turn-stop-auto-inactive-mode-1", last_assistant_message: "Keep going and finish the cleanup.", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.deepEqual(result.outputJson, { decision: "block", reason: DEFAULT_AUTO_NUDGE_RESPONSE, stopReason: "auto_nudge", systemMessage: "OMX native Stop detected a stall/permission-style handoff and continued the turn automatically.", }); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("clears stale root skill-active state when current session ralplan is terminal", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-stale-root-skill-terminal-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-stop-terminal-ralplan"; await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId }); await writeJson(join(stateDir, "sessions", sessionId, "ralplan-state.json"), { active: false, mode: "ralplan", current_phase: "completed", lifecycle_outcome: "finished", run_outcome: "finish", final_artifact: "proposed_plan", }); await writeJson(join(stateDir, "skill-active-state.json"), { active: true, skill: "ultrawork", phase: "planning", source: "keyword-detector", active_skills: [ { skill: "ultrawork", phase: "planning", active: true }, ], }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: sessionId, thread_id: "thread-stop-terminal-ralplan", turn_id: "turn-stop-terminal-ralplan-1", last_assistant_message: "Done.", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson, null); const rootSkillState = JSON.parse( await readFile(join(stateDir, "skill-active-state.json"), "utf-8"), ) as { active?: boolean; active_skills?: unknown[]; reconciliation_reason?: string }; assert.equal(rootSkillState.active, false); assert.deepEqual(rootSkillState.active_skills, []); assert.equal(rootSkillState.reconciliation_reason, "stop_hook_session_state_terminal"); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("preserves legitimate session-scoped ultrawork blocking while reconciling root skill-active state", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-active-root-skill-session-mode-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-stop-active-ultrawork"; await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId }); await writeJson(join(stateDir, "sessions", sessionId, "ultrawork-state.json"), { active: true, mode: "ultrawork", current_phase: "executing", session_id: sessionId, }); await writeJson(join(stateDir, "skill-active-state.json"), { active: true, skill: "ultrawork", phase: "planning", source: "keyword-detector", active_skills: [ { skill: "ultrawork", phase: "planning", active: true, session_id: sessionId }, ], }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: sessionId, thread_id: "thread-stop-active-ultrawork", turn_id: "turn-stop-active-ultrawork-1", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.deepEqual(result.outputJson, { decision: "block", reason: "OMX ultrawork is still active (phase: executing); continue the task and gather fresh verification evidence before stopping.", stopReason: "ultrawork_executing", systemMessage: "OMX ultrawork is still active (phase: executing).", }); const rootSkillState = JSON.parse( await readFile(join(stateDir, "skill-active-state.json"), "utf-8"), ) as { active?: boolean; active_skills?: Array<{ skill?: string }> }; assert.equal(rootSkillState.active, true); assert.deepEqual(rootSkillState.active_skills?.map((entry) => entry.skill), ["ultrawork"]); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("reconciles stale root skill-active state under OMX_ROOT boxed state", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-boxed-source-")); const omxRoot = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-boxed-root-")); const previousOmxRoot = process.env.OMX_ROOT; try { process.env.OMX_ROOT = omxRoot; const stateDir = join(omxRoot, ".omx", "state"); const sourceStateDir = join(cwd, ".omx", "state"); const sessionId = "sess-stop-boxed-ralplan"; await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId }); await writeJson(join(stateDir, "sessions", sessionId, "ralplan-state.json"), { active: false, mode: "ralplan", current_phase: "completed", lifecycle_outcome: "finished", run_outcome: "finish", }); await writeJson(join(stateDir, "skill-active-state.json"), { active: true, skill: "ultrawork", phase: "planning", source: "keyword-detector", active_skills: [ { skill: "ultrawork", phase: "planning", active: true }, ], }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: sessionId, thread_id: "thread-stop-boxed-ralplan", turn_id: "turn-stop-boxed-ralplan-1", last_assistant_message: "Done.", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson, null); const boxedRootSkillState = JSON.parse( await readFile(join(stateDir, "skill-active-state.json"), "utf-8"), ) as { active?: boolean; active_skills?: unknown[]; reconciliation_reason?: string }; assert.equal(boxedRootSkillState.active, false); assert.deepEqual(boxedRootSkillState.active_skills, []); assert.equal(boxedRootSkillState.reconciliation_reason, "stop_hook_session_state_terminal"); assert.equal(existsSync(join(sourceStateDir, "skill-active-state.json")), false); } finally { if (previousOmxRoot === undefined) delete process.env.OMX_ROOT; else process.env.OMX_ROOT = previousOmxRoot; await rm(cwd, { recursive: true, force: true }); await rm(omxRoot, { recursive: true, force: true }); } }); it("auto-continues native Stop for permission-seeking prompts even outside OMX runtime", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-auto-nudge-plain-session-")); try { await dispatchCodexNativeHook( { hook_event_name: "SessionStart", cwd, session_id: "plain-stop-session", }, { cwd, sessionOwnerPid: process.pid, }, ); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "plain-stop-session", thread_id: "plain-thread", turn_id: "plain-turn-1", last_assistant_message: "If you want, I can continue with the cleanup from here.", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.deepEqual(result.outputJson, { decision: "block", reason: DEFAULT_AUTO_NUDGE_RESPONSE, stopReason: "auto_nudge", systemMessage: "OMX native Stop detected a stall/permission-style handoff and continued the turn automatically.", }); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("re-fires team Stop output for a later fresh Stop reply while the team is still active", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-team-refire-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(stateDir, { recursive: true }); await writeJson(join(stateDir, "team-state.json"), { active: true, current_phase: "team-exec", team_name: "review-team", session_id: "sess-stop-team-refire", thread_id: "thread-stop-team-refire", }); await writeJson(join(stateDir, "team", "review-team", "phase.json"), { current_phase: "team-verify", max_fix_attempts: 3, current_fix_attempt: 0, transitions: [], updated_at: new Date().toISOString(), }); await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-team-refire", thread_id: "thread-stop-team-refire", turn_id: "turn-stop-team-refire-1", }, { cwd }, ); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-team-refire", thread_id: "thread-stop-team-refire", turn_id: "turn-stop-team-refire-2", stop_hook_active: true, }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.deepEqual(result.outputJson, { decision: "block", reason: `OMX team pipeline is still active (review-team) at phase team-verify; continue coordinating until the team reaches a terminal phase.${TEAM_STOP_COMMIT_GUIDANCE}`, stopReason: "team_team-verify", systemMessage: "OMX team pipeline is still active at phase team-verify.", }); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("suppresses duplicate team Stop replays across native/canonical session-id drift", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-team-session-drift-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(join(stateDir, "sessions", "omx-canonical"), { recursive: true }); process.env.OMX_SESSION_ID = "omx-canonical"; await writeJson(join(stateDir, "session.json"), { session_id: "omx-canonical", native_session_id: "codex-native", }); await writeJson(join(stateDir, "sessions", "omx-canonical", "team-state.json"), { active: true, current_phase: "starting", team_name: "current-team", session_id: "omx-canonical", }); await writeJson(join(stateDir, "team", "current-team", "phase.json"), { current_phase: "team-verify", max_fix_attempts: 3, current_fix_attempt: 1, transitions: [], updated_at: new Date().toISOString(), }); await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "codex-native", thread_id: "thread-stop-team-drift", turn_id: "turn-stop-team-drift-1", }, { cwd }, ); const duplicate = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "omx-canonical", thread_id: "thread-stop-team-drift", turn_id: "turn-stop-team-drift-1", stop_hook_active: true, }, { cwd }, ); assert.equal(duplicate.omxEventName, "stop"); assert.deepEqual(duplicate.outputJson, { decision: "block", reason: `OMX team pipeline is still active (current-team) at phase team-verify; continue coordinating until the team reaches a terminal phase.${TEAM_STOP_COMMIT_GUIDANCE}`, stopReason: "team_team-verify", systemMessage: "OMX team pipeline is still active at phase team-verify.", }); const fresh = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "omx-canonical", thread_id: "thread-stop-team-drift", turn_id: "turn-stop-team-drift-2", stop_hook_active: true, }, { cwd }, ); assert.equal(fresh.omxEventName, "stop"); assert.deepEqual(fresh.outputJson, { decision: "block", reason: `OMX team pipeline is still active (current-team) at phase team-verify; continue coordinating until the team reaches a terminal phase.${TEAM_STOP_COMMIT_GUIDANCE}`, stopReason: "team_team-verify", systemMessage: "OMX team pipeline is still active at phase team-verify.", }); const persisted = JSON.parse( await readFile(join(stateDir, "native-stop-state.json"), "utf-8"), ) as { sessions?: Record }; assert.deepEqual(Object.keys(persisted.sessions ?? {}), ["omx-canonical"]); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("suppresses duplicate ultrawork Stop replays while stop_hook_active stays true", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-ultrawork-repeat-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(join(stateDir, "sessions", "sess-stop-ultrawork-repeat"), { recursive: true }); await writeJson(join(stateDir, "sessions", "sess-stop-ultrawork-repeat", "ultrawork-state.json"), { active: true, current_phase: "executing", }); const first = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-ultrawork-repeat", thread_id: "thread-stop-ultrawork-repeat", turn_id: "turn-stop-ultrawork-repeat-1", }, { cwd }, ); const repeated = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-ultrawork-repeat", thread_id: "thread-stop-ultrawork-repeat", turn_id: "turn-stop-ultrawork-repeat-1", stop_hook_active: true, }, { cwd }, ); const fresh = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-ultrawork-repeat", thread_id: "thread-stop-ultrawork-repeat", turn_id: "turn-stop-ultrawork-repeat-2", stop_hook_active: true, }, { cwd }, ); assert.equal(first.omxEventName, "stop"); assert.deepEqual(repeated.outputJson, null); assert.equal(fresh.omxEventName, "stop"); assert.deepEqual(fresh.outputJson, { decision: "block", reason: "OMX ultrawork is still active (phase: executing); continue the task and gather fresh verification evidence before stopping.", stopReason: "ultrawork_executing", systemMessage: "OMX ultrawork is still active (phase: executing).", }); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("re-blocks active ralplan skill state on repeated Stop hooks", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-skill-repeat-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(join(stateDir, "sessions", "sess-stop-skill-repeat"), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: "sess-stop-skill-repeat" }); await writeJson(join(stateDir, "sessions", "sess-stop-skill-repeat", "skill-active-state.json"), { active: true, skill: "ralplan", phase: "planning", }); await writeJson(join(stateDir, "sessions", "sess-stop-skill-repeat", "ralplan-state.json"), { active: true, current_phase: "planning", }); await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-skill-repeat", thread_id: "thread-stop-skill-repeat", turn_id: "turn-stop-skill-repeat-1", }, { cwd }, ); const repeated = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-stop-skill-repeat", thread_id: "thread-stop-skill-repeat", turn_id: "turn-stop-skill-repeat-1", stop_hook_active: true, }, { cwd }, ); assert.equal(repeated.omxEventName, "stop"); assert.equal(repeated.outputJson?.decision, "block"); assert.match(String(repeated.outputJson?.reason ?? ""), /Status: continue_from_artifact/); assert.match(String(repeated.outputJson?.reason ?? ""), /continue from the current ralplan artifact/i); assert.equal(repeated.outputJson?.stopReason, "skill_ralplan_planning_continue_artifact"); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks implementation writes while ralplan is active without execution handoff (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("blocks implementation writes while Autopilot is supervising deep-interview without a persisted phase transition (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("blocks implementation writes while Autopilot is supervising ralplan without handoff (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("allows Autopilot planning handoffs with active:true state writes (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("blocks implementation writes when Autopilot ralplan is visible only in skill-active phase (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("ignores stale Autopilot ralplan skill mirrors after detail state leaves planning", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-autopilot-stale-ralplan-mirror-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-autopilot-stale-ralplan-mirror"; await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId }); await writeJson(join(stateDir, "sessions", sessionId, "skill-active-state.json"), { active: true, skill: "autopilot", phase: "autopilot:ralplan", session_id: sessionId, active_skills: [{ skill: "autopilot", phase: "autopilot:ralplan", active: true, session_id: sessionId }], }); for (const phase of ["ultragoal", "code-review", "completing", "complete"]) { await writeJson(join(stateDir, "sessions", sessionId, "autopilot-state.json"), { active: true, mode: "autopilot", current_phase: phase, session_id: sessionId, }); const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: "thread-autopilot-stale-ralplan-mirror", tool_name: "Edit", tool_input: { file_path: "src/runtime.ts" }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal(result.outputJson, null, `stale skill-active ralplan mirror must not block when Autopilot detail phase is ${phase}`); } } finally { await rm(cwd, { recursive: true, force: true }); } }); it("allows explicit blank Autopilot detail phase to use a ralplan skill mirror (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("does not block implementation writes from Autopilot ralplan detail state without canonical skill state", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-autopilot-ralplan-no-canonical-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-autopilot-ralplan-no-canonical"; await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId }); await writeJson(join(stateDir, "sessions", sessionId, "autopilot-state.json"), { active: true, mode: "autopilot", current_phase: "ralplan", session_id: sessionId, }); const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: "thread-autopilot-ralplan-no-canonical", tool_name: "Edit", tool_input: { file_path: "src/runtime.ts" }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks implementation writes when terminal Autopilot run-state shadows stale supervised ralplan state (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("blocks bash implementation writes while Autopilot is supervising ralplan without handoff (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("blocks implementation writes when ralplan and Autopilot ralplan are both active (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("blocks implementation writes while Autopilot is supervising replan without handoff (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("blocks implementation writes when native Codex id maps to OMX Autopilot ralplan state (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("blocks bash implementation writes when native Codex id maps to OMX Autopilot ralplan state (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("blocks standalone ralplan writes when native Codex id maps to OMX session state (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("blocks deep-interview writes when native Codex id maps to OMX session state (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("allows canonical leader mapped ralplan planning artifact writes without execution handoff", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ralplan-native-map-artifact-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-ralplan-native-map-artifact"; const nativeSessionId = "019e-ralplan-native-map-artifact"; await writeNativeMappedSessionState(cwd, stateDir, sessionId, nativeSessionId, "thread-ralplan-native-map-artifact"); await writeSessionSkillActiveState(stateDir, sessionId, "ralplan", "planning"); await writeJson(join(stateDir, "sessions", sessionId, "ralplan-state.json"), { active: true, mode: "ralplan", current_phase: "planning", session_id: sessionId, }); const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: nativeSessionId, thread_id: "thread-ralplan-native-map-artifact", agent_id: "thread-ralplan-native-map-artifact", tool_name: "Bash", tool_input: { command: "cat <<'EOF' > .omx/plans/prd-native-map.md\nplanning\nEOF" }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal(result.outputJson, null); const allowedDraftPatch = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: nativeSessionId, thread_id: "thread-ralplan-native-map-artifact", agent_id: "thread-ralplan-native-map-artifact", tool_name: "apply_patch", tool_input: { input: `*** Begin Patch\n*** Add File: ${join(cwd, ".omx", "drafts", "issue-3105.md")}\n+# Draft\n*** End Patch\n`, }, }, { cwd }, ); assert.equal(allowedDraftPatch.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("issue #3358 keeps Team and update_plan denied while hardening exact read-only discovery (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("allows trusted omx state read with structured input under ultragoal conductor planning (#3343) (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("enforces the exact same-root session lock inspect grammar under ultragoal Conductor (#3411) (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("blocks an untrusted omx shim ahead of the trusted workspace CLI on PATH under ultragoal conductor planning (#3343) (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("diagnoses Autopilot lifecycle and skill-mirror phase mismatches (#3344) (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("lets omx cancel terminalize Autopilot deep-interview even when the skill mirror advanced (#3344)", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-3344-cancel-mismatch-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-3344-cancel-mismatch"; const leaderThreadId = "thread-3344-cancel-mismatch"; await writeNativeMappedSessionState(cwd, stateDir, sessionId, leaderThreadId, leaderThreadId); await writeJson(join(stateDir, "sessions", sessionId, "skill-active-state.json"), { active: true, skill: "autopilot", phase: "planning", session_id: sessionId, active_skills: [{ skill: "autopilot", phase: "planning", active: true, session_id: sessionId }], }); await writeJson(join(stateDir, "sessions", sessionId, "autopilot-state.json"), { active: true, mode: "autopilot", current_phase: "deep-interview", session_id: sessionId, workingDirectory: cwd, }); await withCleanAmbientNodeRuntimeEnvironment(async () => { await withTrustedWorkspaceOmxCli(cwd, async (_omxCommand, trustedPath) => { const inheritedPath = process.env.PATH; process.env.PATH = trustedPath; try { const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: leaderThreadId, agent_id: leaderThreadId, tool_name: "Bash", tool_use_id: "tool-3344-cancel-mismatch", tool_input: { command: "omx cancel" }, }, { cwd }, ); assert.equal(result.outputJson?.decision, "block"); assert.match(JSON.stringify(result.outputJson), /cancelled_exact_session/); } finally { if (inheritedPath === undefined) delete process.env.PATH; else process.env.PATH = inheritedPath; } }); }); const nextAutopilot = JSON.parse(await readFile(join(stateDir, "sessions", sessionId, "autopilot-state.json"), "utf-8")); const nextSkill = JSON.parse(await readFile(join(stateDir, "sessions", sessionId, "skill-active-state.json"), "utf-8")); assert.equal(nextAutopilot.active, false); assert.equal(nextAutopilot.current_phase, "cancelled"); assert.equal(nextSkill.active, false); assert.equal(nextSkill.phase, "cancelled"); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks mapped implementation writes when explicit ultragoal conductor handoff is active (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("blocks mapped implementation writes when terminal Autopilot run-state shadows stale supervised ralplan state (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("fails closed for implementation writes when a different live root session owns active ralplan state (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("preserves canonical leader planning artifact writes with a live root session pointer", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ralplan-live-root-owner-pass-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-ralplan-live-root-owner-pass"; const nativeSessionId = "019e-ralplan-live-root-owner-pass"; await writeLiveNativeMappedSessionState(cwd, stateDir, sessionId, nativeSessionId, "thread-ralplan-live-root-owner-pass"); await writeSessionSkillActiveState(stateDir, sessionId, "ralplan", "planning"); await writeJson(join(stateDir, "sessions", sessionId, "ralplan-state.json"), { active: true, mode: "ralplan", current_phase: "planning", session_id: sessionId, cwd, }); const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: nativeSessionId, thread_id: "thread-ralplan-live-root-owner-pass", agent_id: "thread-ralplan-live-root-owner-pass", tool_name: "Bash", tool_input: { command: "cat <<'EOF' > .omx/plans/live-root-owner.md\nplanning\nEOF" }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal(result.outputJson, null); const allowedDraftPatch = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: nativeSessionId, thread_id: "thread-ralplan-live-root-owner-pass", agent_id: "thread-ralplan-live-root-owner-pass", tool_name: "apply_patch", tool_input: { input: `*** Begin Patch\n*** Add File: ${join(cwd, ".omx", "drafts", "live-root-owner.md")}\n+# Draft\n*** End Patch\n`, }, }, { cwd }, ); assert.equal(allowedDraftPatch.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks foreign native Codex ids when current OMX session mapping does not match (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("blocks mapped Autopilot ralplan writes from the authoritative team state root (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("blocks foreign native Codex ids from the authoritative team state root (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("allows canonical leader ralplan planning artifact writes without execution handoff", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ralplan-pretool-artifact-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-ralplan-pretool-artifact"; const leaderThreadId = "thread-ralplan-pretool-artifact"; await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId, leader_thread_id: leaderThreadId, }); await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { [sessionId]: { session_id: sessionId, leader_thread_id: leaderThreadId, threads: { [leaderThreadId]: { thread_id: leaderThreadId, kind: "leader" } }, }, }, }); await writeJson(join(stateDir, "sessions", sessionId, "skill-active-state.json"), { active: true, skill: "ralplan", phase: "planning", session_id: sessionId, active_skills: [{ skill: "ralplan", phase: "planning", active: true, session_id: sessionId }], }); await writeJson(join(stateDir, "sessions", sessionId, "ralplan-state.json"), { active: true, mode: "ralplan", current_phase: "planning", session_id: sessionId, }); const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: leaderThreadId, agent_id: leaderThreadId, tool_name: "Write", tool_input: { file_path: ".omx/plans/prd-issue-2603.md" }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks bash implementation writes while ralplan is active without execution handoff (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("allows canonical leader Bash planning artifact writes while ralplan is active without execution handoff", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ralplan-pretool-bash-artifact-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-ralplan-pretool-bash-artifact"; const leaderThreadId = "thread-ralplan-pretool-bash-artifact"; await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId, leader_thread_id: leaderThreadId, }); await writeJson(join(stateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { [sessionId]: { session_id: sessionId, leader_thread_id: leaderThreadId, threads: { [leaderThreadId]: { thread_id: leaderThreadId, kind: "leader" } }, }, }, }); await writeJson(join(stateDir, "sessions", sessionId, "skill-active-state.json"), { active: true, skill: "ralplan", phase: "planning", session_id: sessionId, active_skills: [{ skill: "ralplan", phase: "planning", active: true, session_id: sessionId }], }); await writeJson(join(stateDir, "sessions", sessionId, "ralplan-state.json"), { active: true, mode: "ralplan", current_phase: "planning", session_id: sessionId, }); const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: leaderThreadId, agent_id: leaderThreadId, tool_name: "Bash", tool_input: { command: "cat <<'EOF' > .omx/plans/prd-issue-2603.md\nplanning\nEOF" }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("allows implementation writes when an explicit execution handoff is active (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("blocks mapped native-session Main-root conductor writes (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("blocks standalone ultragoal conductor writes without requiring ralplan active_skills (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("allows the exact installed omx CLI by absolute path for Main-root read-only commands (#3333) (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("allows finite Codex goal reads but denies lifecycle mutation without documented Main-root proof (#3300) (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("hook-owns session-scoped omx cancel under active ultragoal conductor while impostors stay blocked (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("blocks unsupported active conductor source edits with native delegation recovery guidance (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("keeps the Conductor unblocked: quoted redirect/source text is not a write target and terminal blocked-state writes survive genuinely unsupported native delegation (#3119) (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("fail-closed: escaped-quote and ANSI-C quoted redirects to source stay blocked while legit quoted data and nested shells behave (#3119) (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("blocks Main-root ralplan writes even when payload has only a typed agent_role (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("requires owner confirmation when only a typed agent_role is present (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("requires owner confirmation for typed child source, unowned, and conductor-metadata mutations (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("uses hook-native agent_id as child provenance without borrowing Team or legacy identity (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("keeps active Ralph starting phase behind the PreToolUse write guard (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("requires owner confirmation for tracked typed native subagent writes without thread_spawn source (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("blocks conductor writes when corrupt tracker state labels the leader as a subagent (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("requires owner confirmation when a corrupt kind:subagent tracker is the only apparent leader source (#3117 P2) (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("fails closed for untyped provenance when no authoritative leader anchor exists (#3117 P2) (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("does not borrow leader anchors from a foreign root session.json when evaluating another session (#3117 P3) (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("fails closed when session.json carries only owner session ids without a leader thread anchor (#3117 P4) (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("blocks conductor writes when thread_spawn provenance is attached to the leader thread (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("denies Main-root conductor apply_patch from the session leader thread during active Ralph (#3116) (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("requires owner confirmation for a tracked generic collaboration.spawn_agent child during active Ralph (#3116) (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("requires owner confirmation for tracked and runtime-proven collaboration descendants (#3116) (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("resolves session-pinned Ralph state so child provenance receives owner confirmation while the leader stays blocked (#3116) (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("requires owner confirmation for a runtime-proven native collaboration child during active Ralph (#3116) (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("denies spoofed or untrusted collaboration provenance while the main root stays protected during active Ralph (#3116) (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("blocks Main-root ralph conductor source and planning artifact writes while allowing .omx workflow state writes (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("blocks dynamic ultragoal steering cleanup through nested shell loops (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("blocks unsafe dynamic nested shell writes in conductor mode (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("blocks common Bash file mutations in Main-root conductor states unless they target workflow metadata (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("trusts exact inherited non-repository PATH executables while keeping explicit resolution mutations fail-closed (#3370) (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("allows autopilot rework implementation writes while conductor phases stay guarded", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-autopilot-rework-write-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-autopilot-rework-write"; await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId }); await writeSessionSkillActiveState(stateDir, sessionId, "autopilot", "rework"); await writeJson(join(stateDir, "sessions", sessionId, "autopilot-state.json"), { active: true, mode: "autopilot", current_phase: "rework", session_id: sessionId, }); const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: "thread-autopilot-rework-write", tool_name: "Edit", tool_input: { file_path: "src/runtime.ts" }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks Main-root ralph conductor source writes while allowing .omx workflow state writes (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("blocks non-shell direct writes in Main-root conductor states (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("blocks Main-root team conductor writes from root team state (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("allows autopilot rework implementation writes while conductor phases stay guarded", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-autopilot-rework-write-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-autopilot-rework-write"; await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId }); await writeSessionSkillActiveState(stateDir, sessionId, "autopilot", "rework"); await writeJson(join(stateDir, "sessions", sessionId, "autopilot-state.json"), { active: true, mode: "autopilot", current_phase: "rework", session_id: sessionId, }); const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: "thread-autopilot-rework-write", tool_name: "Edit", tool_input: { file_path: "src/runtime.ts" }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks Main-root ralph conductor source writes while allowing .omx workflow state writes (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former conductor hard-gate suite retired by epic #3491 / issue #3497. assert.ok(true); }); it("blocks common Bash file mutations in Main-root conductor states unless they target workflow metadata (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former conductor hard-gate suite retired by epic #3491 / issue #3497. assert.ok(true); }); it("allows conductor sed/perl metadata edits while blocking non-metadata targets (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("allows autopilot rework implementation writes while conductor phases stay guarded", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-autopilot-rework-write-")); try { const stateDir = join(cwd, ".omx", "state"); const sessionId = "sess-autopilot-rework-write"; await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId }); await writeSessionSkillActiveState(stateDir, sessionId, "autopilot", "rework"); await writeJson(join(stateDir, "sessions", sessionId, "autopilot-state.json"), { active: true, mode: "autopilot", current_phase: "rework", session_id: sessionId, }); const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: sessionId, thread_id: "thread-autopilot-rework-write", tool_name: "Edit", tool_input: { file_path: "src/runtime.ts" }, }, { cwd }, ); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not block Stop from root team state without team_name when no session is known", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-root-team-no-session-no-name-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(stateDir, { recursive: true }); await writeJson(join(stateDir, "team-state.json"), { active: true, mode: "team", current_phase: "starting", }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not block Stop from root team state without team_name for a foreign session", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-root-team-foreign-no-name-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(join(stateDir, "sessions", "sess-current"), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: "sess-current" }); await writeJson(join(stateDir, "team-state.json"), { active: true, mode: "team", current_phase: "starting", }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-current", thread_id: "thread-current", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not block Stop from another thread's stale root team state when no scoped team state exists", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-stale-root-team-thread-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(join(stateDir, "sessions", "sess-current"), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: "sess-current" }); await writeJson(join(stateDir, "team-state.json"), { active: true, current_phase: "starting", team_name: "stale-root-thread-team", session_id: "sess-current", thread_id: "thread-other", }); await writeJson(join(stateDir, "team", "stale-root-thread-team", "phase.json"), { current_phase: "team-exec", max_fix_attempts: 3, current_fix_attempt: 0, transitions: [], updated_at: new Date().toISOString(), }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-current", thread_id: "thread-current", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not block Stop from root team state with matching session but missing thread ownership", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-root-team-missing-thread-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(join(stateDir, "sessions", "sess-current"), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: "sess-current" }); await writeJson(join(stateDir, "team-state.json"), { active: true, current_phase: "starting", team_name: "root-missing-thread-team", session_id: "sess-current", }); await writeJson(join(stateDir, "team", "root-missing-thread-team", "phase.json"), { current_phase: "team-exec", max_fix_attempts: 3, current_fix_attempt: 0, transitions: [], updated_at: new Date().toISOString(), }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-current", thread_id: "thread-current", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not block Stop from root team state when canonical phase is missing", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-root-team-missing-phase-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(join(stateDir, "sessions", "sess-current"), { recursive: true }); await mkdir(join(stateDir, "team", "root-missing-phase-team"), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: "sess-current" }); await writeJson(join(stateDir, "team-state.json"), { active: true, current_phase: "starting", team_name: "root-missing-phase-team", session_id: "sess-current", thread_id: "thread-current", }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-current", thread_id: "thread-current", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not block Stop from session-scoped team state owned by another thread", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-scoped-team-other-thread-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(join(stateDir, "sessions", "sess-current"), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: "sess-current" }); await writeJson(join(stateDir, "sessions", "sess-current", "team-state.json"), { active: true, current_phase: "starting", team_name: "scoped-other-thread-team", session_id: "sess-current", thread_id: "thread-other", }); await writeJson(join(stateDir, "team", "scoped-other-thread-team", "phase.json"), { current_phase: "team-exec", max_fix_attempts: 3, current_fix_attempt: 0, transitions: [], updated_at: new Date().toISOString(), }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-current", thread_id: "thread-current", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("blocks Stop from session-scoped team state owned by the current session and thread", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-scoped-team-current-thread-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(join(stateDir, "sessions", "sess-current"), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: "sess-current" }); await writeJson(join(stateDir, "sessions", "sess-current", "team-state.json"), { active: true, current_phase: "starting", team_name: "scoped-current-team", session_id: "sess-current", thread_id: "thread-current", }); await writeJson(join(stateDir, "team", "scoped-current-team", "phase.json"), { current_phase: "team-exec", max_fix_attempts: 3, current_fix_attempt: 0, transitions: [], updated_at: new Date().toISOString(), }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-current", thread_id: "thread-current", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.deepEqual(result.outputJson, { decision: "block", reason: `OMX team pipeline is still active (scoped-current-team) at phase team-exec; continue coordinating until the team reaches a terminal phase.${TEAM_STOP_COMMIT_GUIDANCE}`, stopReason: "team_team-exec", systemMessage: "OMX team pipeline is still active at phase team-exec.", }); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not block Stop from another session's stale root team state when no scoped team state exists", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-stale-root-team-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(join(stateDir, "sessions", "sess-current"), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: "sess-current" }); await writeJson(join(stateDir, "team-state.json"), { active: true, current_phase: "starting", team_name: "stale-root-team", session_id: "sess-other", }); await writeJson(join(stateDir, "team", "stale-root-team", "phase.json"), { current_phase: "team-exec", max_fix_attempts: 3, current_fix_attempt: 0, transitions: [], updated_at: new Date().toISOString(), }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-current", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not block Stop from orphaned team mode state after cleanup removed canonical team artifacts", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-orphaned-team-state-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(join(stateDir, "sessions", "sess-current"), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: "sess-current" }); await writeJson(join(stateDir, "team-state.json"), { active: true, current_phase: "starting", team_name: "cleaned-team", session_id: "sess-current", }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-current", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("prefers the current session team state over a stale root team fallback during Stop", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-current-session-team-preferred-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(join(stateDir, "sessions", "sess-current"), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: "sess-current" }); await writeJson(join(stateDir, "sessions", "sess-current", "team-state.json"), { active: true, current_phase: "starting", team_name: "current-team", session_id: "sess-current", }); await writeJson(join(stateDir, "team", "current-team", "phase.json"), { current_phase: "team-verify", max_fix_attempts: 3, current_fix_attempt: 1, transitions: [], updated_at: new Date().toISOString(), }); await writeJson(join(stateDir, "team-state.json"), { active: true, current_phase: "starting", team_name: "stale-root-team", session_id: "sess-other", }); await writeJson(join(stateDir, "team", "stale-root-team", "phase.json"), { current_phase: "team-exec", max_fix_attempts: 3, current_fix_attempt: 0, transitions: [], updated_at: new Date().toISOString(), }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-current", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.deepEqual(result.outputJson, { decision: "block", reason: `OMX team pipeline is still active (current-team) at phase team-verify; continue coordinating until the team reaches a terminal phase.${TEAM_STOP_COMMIT_GUIDANCE}`, stopReason: "team_team-verify", systemMessage: "OMX team pipeline is still active at phase team-verify.", }); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not fall back to active root team state when the current scoped team state is inactive", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-inactive-scoped-team-")); try { const stateDir = join(cwd, ".omx", "state"); await mkdir(join(stateDir, "sessions", "sess-current"), { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: "sess-current" }); await writeJson(join(stateDir, "sessions", "sess-current", "team-state.json"), { active: false, current_phase: "complete", team_name: "scoped-finished-team", session_id: "sess-current", }); await writeJson(join(stateDir, "team-state.json"), { active: true, current_phase: "starting", team_name: "root-fallback-team", session_id: "sess-current", }); await writeJson(join(stateDir, "team", "root-fallback-team", "phase.json"), { current_phase: "team-exec", max_fix_attempts: 3, current_fix_attempt: 0, transitions: [], updated_at: new Date().toISOString(), }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", cwd, session_id: "sess-current", }, { cwd }, ); assert.equal(result.omxEventName, "stop"); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); }); describe("#3118 native role contract", () => { const unknownRoleReason = "Native typed-subagent dispatch denied: supplied agent_type/agent_role is unknown or not installed."; async function withIsolatedNativeRoleState( prefix: string, run: (cwd: string, stateDir: string) => Promise, ): Promise { const cwd = await mkdtemp(join(tmpdir(), `omx-native-hook-3118-${prefix}-`)); const previousOmxRoot = process.env.OMX_ROOT; const previousTeamStateRoot = process.env.OMX_TEAM_STATE_ROOT; const previousStateRoot = process.env.OMX_STATE_ROOT; try { process.env.OMX_ROOT = cwd; delete process.env.OMX_TEAM_STATE_ROOT; delete process.env.OMX_STATE_ROOT; return await run(cwd, getBaseStateDir(cwd)); } finally { if (previousOmxRoot === undefined) delete process.env.OMX_ROOT; else process.env.OMX_ROOT = previousOmxRoot; if (previousTeamStateRoot === undefined) delete process.env.OMX_TEAM_STATE_ROOT; else process.env.OMX_TEAM_STATE_ROOT = previousTeamStateRoot; if (previousStateRoot === undefined) delete process.env.OMX_STATE_ROOT; else process.env.OMX_STATE_ROOT = previousStateRoot; await rm(cwd, { recursive: true, force: true }); } } it("denies an unknown agent_type on collaboration.spawn_agent before dispatch (#3118) (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("denies unknown roles on exact-name spawn_agent and task before dispatch (#3118) (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); it("does not deny an installed architect on collaboration.spawn_agent (#3118)", async () => { await withIsolatedNativeRoleState("collaboration-installed-role", async (cwd) => { const result = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, tool_name: "collaboration.spawn_agent", tool_input: { agent_role: "architect" }, }, { cwd }, ); assert.equal(result.outputJson, null); }); }); }); // --------------------------------------------------------------------------- // Triage layer integration tests // --------------------------------------------------------------------------- describe("codex native hook triage integration", () => { const priorCodexHome = process.env.CODEX_HOME; beforeEach(() => { resetTriageConfigCache(); }); afterEach(() => { if (typeof priorCodexHome === "string") process.env.CODEX_HOME = priorCodexHome; else delete process.env.CODEX_HOME; resetTriageConfigCache(); }); // ── Group 1: Keyword bypass (triage must NOT run) ──────────────────────── it("does not inject triage advisory for $ralplan keyword prompts", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-triage-keyword-ralplan-")); try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "triage-kw-ralplan-1", thread_id: "thread-triage-kw-1", turn_id: "turn-triage-kw-1", prompt: "$ralplan implement issue #1307", }, { cwd }, ); const additionalContext = String( (result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? "", ); assert.doesNotMatch(additionalContext, /multi-step goal with no workflow keyword/); assert.doesNotMatch(additionalContext, /read-only\/question-shaped/); assert.doesNotMatch(additionalContext, /narrow edit-shaped/); assert.doesNotMatch(additionalContext, /visual\/style request/); const stateFile = join(cwd, ".omx", "state", "sessions", "triage-kw-ralplan-1", "prompt-routing-state.json"); assert.equal(existsSync(stateFile), false); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("does not activate workflow state for native subagent prompts even when canonical id is the child session", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-subagent-keyword-")); const boxedRoot = await mkdtemp(join(tmpdir(), "omx-native-subagent-keyword-boxed-")); const originalOmxRoot = process.env.OMX_ROOT; const originalOmxStateRoot = process.env.OMX_STATE_ROOT; const originalTeamStateRoot = process.env.OMX_TEAM_STATE_ROOT; try { process.env.OMX_ROOT = boxedRoot; delete process.env.OMX_STATE_ROOT; delete process.env.OMX_TEAM_STATE_ROOT; const boxedStateDir = getBaseStateDir(cwd); await mkdir(boxedStateDir, { recursive: true }); await writeJson(join(boxedStateDir, "subagent-tracking.json"), { schemaVersion: 1, sessions: { "omx-parent-session": { session_id: "omx-parent-session", leader_thread_id: "parent-native-thread", updated_at: "2026-05-21T19:04:40.000Z", threads: { "parent-native-thread": { thread_id: "parent-native-thread", kind: "leader", first_seen_at: "2026-05-21T19:04:40.000Z", last_seen_at: "2026-05-21T19:04:40.000Z", turn_count: 1, }, "child-native-session": { thread_id: "child-native-session", kind: "subagent", first_seen_at: "2026-05-21T19:04:41.000Z", last_seen_at: "2026-05-21T19:04:41.000Z", turn_count: 1, mode: "review", }, }, }, }, }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "child-native-session", thread_id: "child-native-session", turn_id: "turn-subagent-review", prompt: [ "Read-only review only. Do not edit files. Do not inspect/mutate OMX state/hooks.", "Context: The user asked for $autopilot, and this subagent must only review the patch.", ].join("\n\n"), }, { cwd }, ); const additionalContext = String( (result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? "", ); assert.equal(additionalContext, ""); assert.equal( existsSync(join(boxedStateDir, "sessions", "child-native-session", "skill-active-state.json")), false, ); assert.equal( existsSync(join(boxedStateDir, "sessions", "child-native-session", "autopilot-state.json")), false, ); assert.equal( existsSync(join(cwd, ".omx", "state", "subagent-tracking.json")), false, "subagent tracking must not leak into the source worktree when OMX_ROOT is boxed", ); } finally { if (originalOmxRoot === undefined) delete process.env.OMX_ROOT; else process.env.OMX_ROOT = originalOmxRoot; if (originalOmxStateRoot === undefined) delete process.env.OMX_STATE_ROOT; else process.env.OMX_STATE_ROOT = originalOmxStateRoot; if (originalTeamStateRoot === undefined) delete process.env.OMX_TEAM_STATE_ROOT; else process.env.OMX_TEAM_STATE_ROOT = originalTeamStateRoot; await rm(cwd, { recursive: true, force: true }); await rm(boxedRoot, { recursive: true, force: true }); } }); it("does not inject triage advisory for autopilot keyword prompts", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-triage-keyword-autopilot-")); try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "triage-kw-autopilot-1", thread_id: "thread-triage-kw-ap-1", turn_id: "turn-triage-kw-ap-1", prompt: "$autopilot build this", }, { cwd }, ); const additionalContext = String( (result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? "", ); assert.doesNotMatch(additionalContext, /multi-step goal with no workflow keyword/); assert.doesNotMatch(additionalContext, /read-only\/question-shaped/); assert.doesNotMatch(additionalContext, /narrow edit-shaped/); assert.doesNotMatch(additionalContext, /visual\/style request/); const stateFile = join(cwd, ".omx", "state", "sessions", "triage-kw-autopilot-1", "prompt-routing-state.json"); assert.equal(existsSync(stateFile), false); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("keeps marked workflow-like answers inert without treating them as a new triage request", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-triage-marked-answer-inert-")); const codexHome = await mkdtemp(join(tmpdir(), "omx-triage-marked-answer-home-")); const previousCodexHome = process.env.CODEX_HOME; try { await writeJson(join(codexHome, ".omx-config.json"), { promptRouting: { triage: { enabled: true } }, }); process.env.CODEX_HOME = codexHome; resetTriageConfigCache(); await mkdir(join(cwd, ".omx", "state"), { recursive: true }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, source: "codex-app", session_id: "triage-marked-answer-inert", thread_id: "thread-triage-marked-answer-inert", prompt: "[omx question answered] explain this function?", }, { cwd }, ); const additionalContext = String( (result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? "", ); assert.equal(result.skillState, null); assert.equal(additionalContext, ""); assert.equal( existsSync(join(cwd, ".omx", "state", "sessions", "triage-marked-answer-inert", "autopilot-state.json")), false, ); assert.equal( existsSync(join(cwd, ".omx", "state", "sessions", "triage-marked-answer-inert", "prompt-routing-state.json")), false, ); const promptsResult = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, source: "codex-app", session_id: "triage-prompts-inert", thread_id: "thread-triage-prompts-inert", prompt: "/prompts:architect explain this function?", }, { cwd }, ); assert.equal(promptsResult.skillState, null); assert.equal( existsSync(join(cwd, ".omx", "state", "sessions", "triage-prompts-inert", "prompt-routing-state.json")), false, ); } finally { if (previousCodexHome === undefined) delete process.env.CODEX_HOME; else process.env.CODEX_HOME = previousCodexHome; resetTriageConfigCache(); await rm(cwd, { recursive: true, force: true }); await rm(codexHome, { recursive: true, force: true }); } }); it("#3463: makes fresh autopilot activation observable in state, HUD context, and prompt guidance", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-autopilot-observable-")); try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); await writeSessionStart(cwd, "sess-autopilot-observable"); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "sess-autopilot-observable", thread_id: "thread-autopilot-observable", turn_id: "turn-autopilot-observable", prompt: "$autopilot implement issue #2430", }, { cwd }, ); assert.equal(result.skillState?.skill, "autopilot"); assert.notEqual(result.skillState?.error, "documented_host_consensus_receipt_unavailable"); const additionalContext = String( (result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? "", ); // #3463: autopilot starts normally; no longer denied const sessionDir = join(cwd, ".omx", "state", "sessions", "sess-autopilot-observable"); const statePath = join(sessionDir, "autopilot-state.json"); const modeState = JSON.parse(await readFile(statePath, "utf-8")) as { active: boolean; current_phase: string; error?: string; }; assert.equal(modeState.active, true); assert.notEqual(modeState.error, "documented_host_consensus_receipt_unavailable"); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("#3463: starts fresh autopilot with Team guidance when Team mode is disabled", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-autopilot-observable-no-team-")); try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); await writeJson(join(cwd, ".omx", "setup-scope.json"), { scope: "project", teamMode: "disabled", }); await writeSessionStart(cwd, "sess-autopilot-observable-no-team"); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "sess-autopilot-observable-no-team", thread_id: "thread-autopilot-observable-no-team", turn_id: "turn-autopilot-observable-no-team", prompt: "$autopilot implement issue #2430", }, { cwd }, ); assert.equal(result.skillState?.skill, "autopilot"); assert.notEqual(result.skillState?.error, "documented_host_consensus_receipt_unavailable"); const additionalContext = String( (result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? "", ); assert.doesNotMatch(additionalContext, /documented_host_consensus_receipt_unavailable/); assert.doesNotMatch(additionalContext, /documented_host_consensus_receipt_unavailable/); const sessionDir = join(cwd, ".omx", "state", "sessions", "sess-autopilot-observable-no-team"); assert.equal(existsSync(join(cwd, ".omx", "state", "team-state.json")), false); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("ignores disabled $team before outside-tmux Team blocking so later workflows can activate", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-disabled-team-primary-")); try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); await writeJson(join(cwd, ".omx", "setup-scope.json"), { scope: "project", teamMode: "disabled", }); await writeSessionStart(cwd, "sess-disabled-team-primary"); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "sess-disabled-team-primary", thread_id: "thread-disabled-team-primary", turn_id: "turn-disabled-team-primary", prompt: "$team $ultragoal fix this", }, { cwd }, ); assert.equal(result.skillState?.skill, "ultragoal"); assert.equal(result.skillState?.transition_error, undefined); assert.equal(existsSync(join(cwd, ".omx", "state", "team-state.json")), false); assert.equal( existsSync(join(cwd, ".omx", "state", "sessions", "sess-disabled-team-primary", "ultragoal-state.json")), true, ); const additionalContext = String( (result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? "", ); assert.match(additionalContext, /detected workflow keyword "\$ultragoal" -> ultragoal/); assert.doesNotMatch(additionalContext, /Codex App\/native outside-tmux sessions cannot activate/); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("#3463: makes bare fresh autopilot activation observable in state and prompt guidance", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-autopilot-bare-observable-")); try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); await writeSessionStart(cwd, "sess-autopilot-bare-observable"); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "sess-autopilot-bare-observable", thread_id: "thread-autopilot-bare-observable", turn_id: "turn-autopilot-bare-observable", prompt: "run autopilot", }, { cwd }, ); assert.equal(result.skillState?.skill, "autopilot"); assert.notEqual(result.skillState?.error, "documented_host_consensus_receipt_unavailable"); const additionalContext = String( (result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? "", ); // #3463: autopilot starts normally; the preflight no longer blocks. assert.doesNotMatch(additionalContext, /documented_host_consensus_receipt_unavailable/); const sessionDir = join(cwd, ".omx", "state", "sessions", "sess-autopilot-bare-observable"); const statePath = join(sessionDir, "autopilot-state.json"); const modeState = JSON.parse(await readFile(statePath, "utf-8")) as { active: boolean; current_phase: string; error?: string; }; assert.equal(modeState.active, true); assert.notEqual(modeState.error, "documented_host_consensus_receipt_unavailable"); // #3463: deep-interview state may now exist // #3463: ultragoal state not created yet } finally { await rm(cwd, { recursive: true, force: true }); } }); // ── Group 2: HEAVY injection ───────────────────────────────────────────── it("injects HEAVY advisory and writes prompt-routing-state for a multi-step goal prompt", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-triage-heavy-")); try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "triage-heavy-1", thread_id: "thread-triage-heavy-1", turn_id: "turn-triage-heavy-1", prompt: "add dark mode toggle to the settings page", }, { cwd }, ); const additionalContext = String( (result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? "", ); assert.match(additionalContext, /multi-step goal with no workflow keyword/); assert.match(additionalContext, /Prefer the existing autopilot-style workflow/); // skill-active-state.json must NOT be written (triage is advisory only) assert.equal(existsSync(join(cwd, ".omx", "state", "skill-active-state.json")), false); // prompt-routing-state.json must be written with lane=HEAVY const stateFile = join(cwd, ".omx", "state", "sessions", "triage-heavy-1", "prompt-routing-state.json"); assert.equal(existsSync(stateFile), true); const state = JSON.parse(await readFile(stateFile, "utf-8")) as { version?: number; last_triage?: { lane?: string; destination?: string }; suppress_followup?: boolean; }; assert.equal(state.version, 1); assert.equal(state.last_triage?.lane, "HEAVY"); assert.equal(state.last_triage?.destination, "autopilot"); assert.equal(state.suppress_followup, true); } finally { await rm(cwd, { recursive: true, force: true }); } }); // ── Group 3: LIGHT/explore ──────────────────────────────────────────────── it("injects LIGHT/explore advisory and writes state for a question-shaped prompt", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-triage-light-explore-")); try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "triage-explore-1", thread_id: "thread-triage-explore-1", turn_id: "turn-triage-explore-1", prompt: "explain this function", }, { cwd }, ); const additionalContext = String( (result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? "", ); assert.match(additionalContext, /read-only\/question-shaped/); assert.match(additionalContext, /Prefer the explore role surface/); const stateFile = join(cwd, ".omx", "state", "sessions", "triage-explore-1", "prompt-routing-state.json"); assert.equal(existsSync(stateFile), true); const state = JSON.parse(await readFile(stateFile, "utf-8")) as { last_triage?: { lane?: string; destination?: string }; suppress_followup?: boolean; }; assert.equal(state.last_triage?.lane, "LIGHT"); assert.equal(state.last_triage?.destination, "explore"); assert.equal(state.suppress_followup, true); } finally { await rm(cwd, { recursive: true, force: true }); } }); // ── Group 4: LIGHT/executor ─────────────────────────────────────────────── it("injects LIGHT/executor advisory and writes state for a narrow edit-shaped prompt", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-triage-light-executor-")); try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "triage-executor-1", thread_id: "thread-triage-executor-1", turn_id: "turn-triage-executor-1", prompt: "fix typo in src/foo.ts", }, { cwd }, ); const additionalContext = String( (result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? "", ); assert.match(additionalContext, /narrow edit-shaped/); assert.match(additionalContext, /Prefer the executor role surface/); const stateFile = join(cwd, ".omx", "state", "sessions", "triage-executor-1", "prompt-routing-state.json"); assert.equal(existsSync(stateFile), true); const state = JSON.parse(await readFile(stateFile, "utf-8")) as { last_triage?: { lane?: string; destination?: string }; }; assert.equal(state.last_triage?.lane, "LIGHT"); assert.equal(state.last_triage?.destination, "executor"); } finally { await rm(cwd, { recursive: true, force: true }); } }); // ── Group 5: LIGHT/designer ─────────────────────────────────────────────── it("injects LIGHT/designer advisory and writes state for a visual/style prompt", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-triage-light-designer-")); try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "triage-designer-1", thread_id: "thread-triage-designer-1", turn_id: "turn-triage-designer-1", prompt: "make the button blue", }, { cwd }, ); const additionalContext = String( (result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? "", ); assert.match(additionalContext, /visual\/style request/); assert.match(additionalContext, /Prefer the designer role surface/); const stateFile = join(cwd, ".omx", "state", "sessions", "triage-designer-1", "prompt-routing-state.json"); assert.equal(existsSync(stateFile), true); const state = JSON.parse(await readFile(stateFile, "utf-8")) as { last_triage?: { lane?: string; destination?: string }; }; assert.equal(state.last_triage?.lane, "LIGHT"); assert.equal(state.last_triage?.destination, "designer"); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("injects LIGHT/researcher advisory and writes state for an official-doc lookup prompt", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-triage-light-researcher-")); try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "triage-researcher-1", thread_id: "thread-triage-researcher-1", turn_id: "turn-triage-researcher-1", prompt: "Find the official docs and version compatibility notes for this SDK", }, { cwd }, ); const additionalContext = String( (result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? "", ); assert.match(additionalContext, /external documentation\/reference research request/); assert.match(additionalContext, /Prefer the researcher role surface/); assert.doesNotMatch(additionalContext, /skill: researcher activated/); assert.equal(existsSync(join(cwd, ".omx", "state", "skill-active-state.json")), false); const stateFile = join(cwd, ".omx", "state", "sessions", "triage-researcher-1", "prompt-routing-state.json"); assert.equal(existsSync(stateFile), true); const state = JSON.parse(await readFile(stateFile, "utf-8")) as { last_triage?: { lane?: string; destination?: string; reason?: string }; suppress_followup?: boolean; }; assert.equal(state.last_triage?.lane, "LIGHT"); assert.equal(state.last_triage?.destination, "researcher"); assert.equal(state.last_triage?.reason, "external_reference_research"); assert.equal(state.suppress_followup, true); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("routes Korean external lookup phrasing to researcher without treating it as workflow activation", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-triage-light-researcher-ko-")); try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "triage-researcher-ko-1", thread_id: "thread-triage-researcher-ko-1", turn_id: "turn-triage-researcher-ko-1", prompt: "OpenAI Responses API 공식 문서 찾아줘", }, { cwd }, ); const additionalContext = String( (result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? "", ); assert.match(additionalContext, /Prefer the researcher role surface/); assert.equal(result.skillState, null); const stateFile = join(cwd, ".omx", "state", "sessions", "triage-researcher-ko-1", "prompt-routing-state.json"); const state = JSON.parse(await readFile(stateFile, "utf-8")) as { last_triage?: { lane?: string; destination?: string }; }; assert.equal(state.last_triage?.lane, "LIGHT"); assert.equal(state.last_triage?.destination, "researcher"); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("routes official-doc question prompts to researcher instead of explore", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-triage-question-researcher-")); try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "triage-question-researcher-1", thread_id: "thread-triage-question-researcher-1", turn_id: "turn-triage-question-researcher-1", prompt: "where can I find official docs for OpenAI Responses API?", }, { cwd }, ); const additionalContext = String( (result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? "", ); assert.doesNotMatch(additionalContext, /Prefer the explore role surface/); assert.match(additionalContext, /Prefer the researcher role surface/); const stateFile = join(cwd, ".omx", "state", "sessions", "triage-question-researcher-1", "prompt-routing-state.json"); const state = JSON.parse(await readFile(stateFile, "utf-8")) as { last_triage?: { lane?: string; destination?: string; reason?: string }; }; assert.equal(state.last_triage?.lane, "LIGHT"); assert.equal(state.last_triage?.destination, "researcher"); assert.equal(state.last_triage?.reason, "external_reference_research"); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("routes endpoint-shaped official-doc lookups to researcher instead of local explore", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-triage-endpoint-researcher-")); try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "triage-endpoint-researcher-1", thread_id: "thread-triage-endpoint-researcher-1", turn_id: "turn-triage-endpoint-researcher-1", prompt: "find official docs for api/v1/responses", }, { cwd }, ); const additionalContext = String( (result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? "", ); assert.doesNotMatch(additionalContext, /Prefer the explore role surface/); assert.match(additionalContext, /Prefer the researcher role surface/); const stateFile = join(cwd, ".omx", "state", "sessions", "triage-endpoint-researcher-1", "prompt-routing-state.json"); const state = JSON.parse(await readFile(stateFile, "utf-8")) as { last_triage?: { lane?: string; destination?: string; reason?: string }; }; assert.equal(state.last_triage?.lane, "LIGHT"); assert.equal(state.last_triage?.destination, "researcher"); assert.equal(state.last_triage?.reason, "external_reference_research"); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("routes dotted technology official-doc lookups to researcher instead of local explore", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-triage-dotted-tech-researcher-")); try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "triage-dotted-tech-researcher-1", thread_id: "thread-triage-dotted-tech-researcher-1", turn_id: "turn-triage-dotted-tech-researcher-1", prompt: "find official docs for Node.js", }, { cwd }, ); const additionalContext = String( (result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? "", ); assert.doesNotMatch(additionalContext, /Prefer the explore role surface/); assert.match(additionalContext, /Prefer the researcher role surface/); const stateFile = join(cwd, ".omx", "state", "sessions", "triage-dotted-tech-researcher-1", "prompt-routing-state.json"); const state = JSON.parse(await readFile(stateFile, "utf-8")) as { last_triage?: { lane?: string; destination?: string; reason?: string }; }; assert.equal(state.last_triage?.lane, "LIGHT"); assert.equal(state.last_triage?.destination, "researcher"); assert.equal(state.last_triage?.reason, "external_reference_research"); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("routes URL-shaped official-doc lookups with repo paths to researcher instead of local routes", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-triage-url-path-researcher-")); try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "triage-url-path-researcher-1", thread_id: "thread-triage-url-path-researcher-1", turn_id: "turn-triage-url-path-researcher-1", prompt: "find official docs for github.com/org/repo/src/foo.ts", }, { cwd }, ); const additionalContext = String( (result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? "", ); assert.doesNotMatch(additionalContext, /Prefer the executor role surface/); assert.doesNotMatch(additionalContext, /Prefer the explore role surface/); assert.match(additionalContext, /Prefer the researcher role surface/); const stateFile = join(cwd, ".omx", "state", "sessions", "triage-url-path-researcher-1", "prompt-routing-state.json"); const state = JSON.parse(await readFile(stateFile, "utf-8")) as { last_triage?: { lane?: string; destination?: string; reason?: string }; }; assert.equal(state.last_triage?.lane, "LIGHT"); assert.equal(state.last_triage?.destination, "researcher"); assert.equal(state.last_triage?.reason, "external_reference_research"); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("keeps implementation-shaped official-doc prompts on HEAVY instead of researcher", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-triage-researcher-implementation-")); try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "triage-researcher-implementation-1", thread_id: "thread-triage-researcher-implementation-1", turn_id: "turn-triage-researcher-implementation-1", prompt: "implement auth using official docs for the SDK", }, { cwd }, ); const additionalContext = String( (result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? "", ); assert.doesNotMatch(additionalContext, /Prefer the researcher role surface/); assert.match(additionalContext, /multi-step goal with no workflow keyword/); const stateFile = join(cwd, ".omx", "state", "sessions", "triage-researcher-implementation-1", "prompt-routing-state.json"); const state = JSON.parse(await readFile(stateFile, "utf-8")) as { last_triage?: { lane?: string; destination?: string; reason?: string }; }; assert.equal(state.last_triage?.lane, "HEAVY"); assert.equal(state.last_triage?.destination, "autopilot"); assert.equal(state.last_triage?.reason, "implementation_research_goal"); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("keeps planning-shaped official-doc prompts on HEAVY instead of researcher", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-triage-researcher-planning-")); try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "triage-researcher-planning-1", thread_id: "thread-triage-researcher-planning-1", turn_id: "turn-triage-researcher-planning-1", prompt: "research and plan auth migration using official docs for the SDK", }, { cwd }, ); const additionalContext = String( (result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? "", ); assert.doesNotMatch(additionalContext, /Prefer the researcher role surface/); assert.match(additionalContext, /multi-step goal with no workflow keyword/); const stateFile = join(cwd, ".omx", "state", "sessions", "triage-researcher-planning-1", "prompt-routing-state.json"); const state = JSON.parse(await readFile(stateFile, "utf-8")) as { last_triage?: { lane?: string; destination?: string; reason?: string }; }; assert.equal(state.last_triage?.lane, "HEAVY"); assert.equal(state.last_triage?.destination, "autopilot"); assert.equal(state.last_triage?.reason, "implementation_research_goal"); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("keeps local source lookup prompts off researcher", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-triage-local-source-explore-")); try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "triage-local-source-1", thread_id: "thread-triage-local-source-1", turn_id: "turn-triage-local-source-1", prompt: "search source for parseConfig in src/config.ts", }, { cwd }, ); const additionalContext = String( (result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? "", ); assert.doesNotMatch(additionalContext, /Prefer the researcher role surface/); assert.match(additionalContext, /Prefer the executor role surface/); const stateFile = join(cwd, ".omx", "state", "sessions", "triage-local-source-1", "prompt-routing-state.json"); const state = JSON.parse(await readFile(stateFile, "utf-8")) as { last_triage?: { lane?: string; destination?: string }; }; assert.equal(state.last_triage?.lane, "LIGHT"); assert.equal(state.last_triage?.destination, "executor"); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("keeps anchored local API usage prompts on executor instead of researcher", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-triage-local-api-executor-")); try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "triage-local-api-1", thread_id: "thread-triage-local-api-1", turn_id: "turn-triage-local-api-1", prompt: "find API usage in src/foo.ts", }, { cwd }, ); const additionalContext = String( (result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? "", ); assert.doesNotMatch(additionalContext, /Prefer the researcher role surface/); assert.match(additionalContext, /Prefer the executor role surface/); const stateFile = join(cwd, ".omx", "state", "sessions", "triage-local-api-1", "prompt-routing-state.json"); const state = JSON.parse(await readFile(stateFile, "utf-8")) as { last_triage?: { lane?: string; destination?: string }; }; assert.equal(state.last_triage?.lane, "LIGHT"); assert.equal(state.last_triage?.destination, "executor"); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("keeps project-scoped local API usage prompts on explore instead of researcher", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-triage-project-api-explore-")); try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "triage-project-api-1", thread_id: "thread-triage-project-api-1", turn_id: "turn-triage-project-api-1", prompt: "find API usage in this project", }, { cwd }, ); const additionalContext = String( (result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? "", ); assert.doesNotMatch(additionalContext, /Prefer the researcher role surface/); assert.match(additionalContext, /Prefer the explore role surface/); const stateFile = join(cwd, ".omx", "state", "sessions", "triage-project-api-1", "prompt-routing-state.json"); const state = JSON.parse(await readFile(stateFile, "utf-8")) as { last_triage?: { lane?: string; destination?: string; reason?: string }; }; assert.equal(state.last_triage?.lane, "LIGHT"); assert.equal(state.last_triage?.destination, "explore"); assert.equal(state.last_triage?.reason, "local_reference_lookup"); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("keeps repository changelog lookup prompts on explore despite generic docs terms", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-triage-repo-changelog-explore-")); try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "triage-repo-changelog-1", thread_id: "thread-triage-repo-changelog-1", turn_id: "turn-triage-repo-changelog-1", prompt: "find changelog in this repository", }, { cwd }, ); const additionalContext = String( (result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? "", ); assert.doesNotMatch(additionalContext, /Prefer the researcher role surface/); assert.match(additionalContext, /Prefer the explore role surface/); const stateFile = join(cwd, ".omx", "state", "sessions", "triage-repo-changelog-1", "prompt-routing-state.json"); const state = JSON.parse(await readFile(stateFile, "utf-8")) as { last_triage?: { lane?: string; destination?: string; reason?: string }; }; assert.equal(state.last_triage?.lane, "LIGHT"); assert.equal(state.last_triage?.destination, "explore"); assert.equal(state.last_triage?.reason, "local_reference_lookup"); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("routes anchored read-only questions through explore before executor", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-triage-anchored-question-explore-")); try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "triage-anchored-question-1", thread_id: "thread-triage-anchored-question-1", turn_id: "turn-triage-anchored-question-1", prompt: "what does src/foo.ts do?", }, { cwd }, ); const additionalContext = String( (result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? "", ); assert.doesNotMatch(additionalContext, /Prefer the executor role surface/); assert.match(additionalContext, /Prefer the explore role surface/); const stateFile = join(cwd, ".omx", "state", "sessions", "triage-anchored-question-1", "prompt-routing-state.json"); const state = JSON.parse(await readFile(stateFile, "utf-8")) as { last_triage?: { lane?: string; destination?: string; reason?: string }; }; assert.equal(state.last_triage?.lane, "LIGHT"); assert.equal(state.last_triage?.destination, "explore"); assert.equal(state.last_triage?.reason, "question_or_explanation"); } finally { await rm(cwd, { recursive: true, force: true }); } }); // ── Group 6: PASS (no triage injection, no state) ──────────────────────── it("produces no triage advisory and no state for trivial greeting prompts", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-triage-pass-hello-")); try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "triage-pass-hello-1", thread_id: "thread-triage-pass-1", turn_id: "turn-triage-pass-1", prompt: "hello", }, { cwd }, ); const additionalContext = String( (result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? "", ); assert.doesNotMatch(additionalContext, /multi-step goal with no workflow keyword/); assert.doesNotMatch(additionalContext, /read-only\/question-shaped/); assert.doesNotMatch(additionalContext, /narrow edit-shaped/); assert.doesNotMatch(additionalContext, /visual\/style request/); const stateFile = join(cwd, ".omx", "state", "sessions", "triage-pass-hello-1", "prompt-routing-state.json"); assert.equal(existsSync(stateFile), false); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("produces no triage advisory and no state for ambiguous short prompts", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-triage-pass-short-")); try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "triage-pass-short-1", thread_id: "thread-triage-pass-short-1", turn_id: "turn-triage-pass-short-1", prompt: "fix the thing", }, { cwd }, ); const additionalContext = String( (result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? "", ); assert.doesNotMatch(additionalContext, /multi-step goal with no workflow keyword/); assert.doesNotMatch(additionalContext, /read-only\/question-shaped/); assert.doesNotMatch(additionalContext, /narrow edit-shaped/); assert.doesNotMatch(additionalContext, /visual\/style request/); const stateFile = join(cwd, ".omx", "state", "sessions", "triage-pass-short-1", "prompt-routing-state.json"); assert.equal(existsSync(stateFile), false); } finally { await rm(cwd, { recursive: true, force: true }); } }); // ── Group 7: Turn-2 suppression (same session across two invocations) ──── it("suppresses HEAVY triage re-injection on a short follow-up in the same session", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-triage-suppress-heavy-")); const sessionId = "triage-suppress-heavy-1"; try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); // Turn 1: HEAVY fires const turn1 = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: sessionId, thread_id: "thread-suppress-heavy-1", turn_id: "turn-suppress-heavy-1", prompt: "add dark mode toggle to the settings page", }, { cwd }, ); const ctx1 = String( (turn1.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? "", ); assert.match(ctx1, /multi-step goal with no workflow keyword/); // Turn 2: short follow-up — triage suppressed const turn2 = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: sessionId, thread_id: "thread-suppress-heavy-1", turn_id: "turn-suppress-heavy-2", prompt: "yes, settings page", }, { cwd }, ); const ctx2 = String( (turn2.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? "", ); assert.doesNotMatch(ctx2, /multi-step goal/); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("suppresses LIGHT/explore triage re-injection on a short follow-up in the same session", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-triage-suppress-explore-")); const sessionId = "triage-suppress-explore-1"; try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); // Turn 1: LIGHT/explore fires await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: sessionId, thread_id: "thread-suppress-explore-1", turn_id: "turn-suppress-explore-1", prompt: "explain this function", }, { cwd }, ); // Turn 2: short follow-up — no duplicate LIGHT injection const turn2 = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: sessionId, thread_id: "thread-suppress-explore-1", turn_id: "turn-suppress-explore-2", prompt: "the auth helper", }, { cwd }, ); const ctx2 = String( (turn2.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? "", ); assert.doesNotMatch(ctx2, /read-only\/question-shaped/); } finally { await rm(cwd, { recursive: true, force: true }); } }); // ── Group 8: First-turn PASS does NOT block later triage ───────────────── it("still applies triage on turn 2 when turn 1 was a PASS with no state written", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-triage-pass-then-light-")); const sessionId = "triage-pass-then-light-1"; try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); // Turn 1: PASS — no state written await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: sessionId, thread_id: "thread-pass-then-light-1", turn_id: "turn-pass-then-light-1", prompt: "hello", }, { cwd }, ); assert.equal( existsSync(join(cwd, ".omx", "state", "sessions", sessionId, "prompt-routing-state.json")), false, ); // Turn 2: LIGHT/executor should fire normally const turn2 = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: sessionId, thread_id: "thread-pass-then-light-1", turn_id: "turn-pass-then-light-2", prompt: "fix typo in src/foo.ts", }, { cwd }, ); const ctx2 = String( (turn2.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? "", ); assert.match(ctx2, /narrow edit-shaped/); } finally { await rm(cwd, { recursive: true, force: true }); } }); // ── Group 9: Opt-out forces PASS ───────────────────────────────────────── it("produces no triage advisory when prompt contains 'just chat' opt-out", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-triage-optout-chat-")); try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "triage-optout-chat-1", thread_id: "thread-optout-chat-1", turn_id: "turn-optout-chat-1", prompt: "add dark mode toggle to the settings page, but just chat about it", }, { cwd }, ); const additionalContext = String( (result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? "", ); assert.doesNotMatch(additionalContext, /multi-step goal with no workflow keyword/); assert.doesNotMatch(additionalContext, /read-only\/question-shaped/); const stateFile = join(cwd, ".omx", "state", "sessions", "triage-optout-chat-1", "prompt-routing-state.json"); assert.equal(existsSync(stateFile), false); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("produces no triage advisory when prompt contains 'no workflow' opt-out", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-triage-optout-noworkflow-")); try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "triage-optout-noworkflow-1", thread_id: "thread-optout-noworkflow-1", turn_id: "turn-optout-noworkflow-1", prompt: "make the button blue, no workflow", }, { cwd }, ); const additionalContext = String( (result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? "", ); assert.doesNotMatch(additionalContext, /visual\/style request/); const stateFile = join(cwd, ".omx", "state", "sessions", "triage-optout-noworkflow-1", "prompt-routing-state.json"); assert.equal(existsSync(stateFile), false); } finally { await rm(cwd, { recursive: true, force: true }); } }); // ── Group 10: Keyword on follow-up turn wins cleanly ───────────────────── it("keyword on turn 2 suppresses triage and writes no triage state", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-triage-kw-followup-")); const sessionId = "triage-kw-followup-1"; try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); // Turn 1: neutral prompt — triage may or may not fire, doesn't matter await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: sessionId, thread_id: "thread-kw-followup-1", turn_id: "turn-kw-followup-1", prompt: "hello", }, { cwd }, ); // Turn 2: keyword prompt — keyword fast-path runs, triage does NOT add extra advisory const turn2 = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: sessionId, thread_id: "thread-kw-followup-1", turn_id: "turn-kw-followup-2", prompt: "$ralph continue", }, { cwd }, ); assert.equal(turn2.skillState?.skill, "ralph"); const ctx2 = String( (turn2.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? "", ); assert.doesNotMatch(ctx2, /multi-step goal with no workflow keyword/); assert.doesNotMatch(ctx2, /read-only\/question-shaped/); assert.doesNotMatch(ctx2, /narrow edit-shaped/); assert.doesNotMatch(ctx2, /visual\/style request/); // No triage state written on the keyword turn const triageState = join(cwd, ".omx", "state", "sessions", sessionId, "prompt-routing-state.json"); // The state from turn 1 (if any) must not have been created either (hello = PASS) assert.equal(existsSync(triageState), false); } finally { await rm(cwd, { recursive: true, force: true }); } }); // ── Group 11: Config-disabled path ─────────────────────────────────────── it("produces no triage advisory and no state when triage is disabled in config", async () => { const tmpHome = await mkdtemp(join(tmpdir(), "omx-triage-config-disabled-home-")); const cwd = await mkdtemp(join(tmpdir(), "omx-triage-config-disabled-cwd-")); try { // Write a .omx-config.json in the fake CODEX_HOME that disables triage await writeJson(join(tmpHome, ".omx-config.json"), { promptRouting: { triage: { enabled: false } }, }); process.env.CODEX_HOME = tmpHome; resetTriageConfigCache(); await mkdir(join(cwd, ".omx", "state"), { recursive: true }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "triage-disabled-1", thread_id: "thread-triage-disabled-1", turn_id: "turn-triage-disabled-1", prompt: "add dark mode toggle to the settings page", }, { cwd }, ); const additionalContext = String( (result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? "", ); assert.doesNotMatch(additionalContext, /multi-step goal with no workflow keyword/); const stateFile = join(cwd, ".omx", "state", "sessions", "triage-disabled-1", "prompt-routing-state.json"); assert.equal(existsSync(stateFile), false); } finally { await rm(tmpHome, { recursive: true, force: true }); await rm(cwd, { recursive: true, force: true }); } }); it("keeps triage default-enabled when config omits promptRouting.triage.enabled", async () => { const tmpHome = await mkdtemp(join(tmpdir(), "omx-triage-config-omitted-home-")); const cwd = await mkdtemp(join(tmpdir(), "omx-triage-config-omitted-cwd-")); const previousCodexHome = process.env.CODEX_HOME; try { await writeJson(join(tmpHome, ".omx-config.json"), { promptRouting: {}, }); process.env.CODEX_HOME = tmpHome; resetTriageConfigCache(); await mkdir(join(cwd, ".omx", "state"), { recursive: true }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "triage-defaulted-1", thread_id: "thread-triage-defaulted-1", turn_id: "turn-triage-defaulted-1", prompt: "add dark mode toggle to the settings page", }, { cwd }, ); const additionalContext = String( (result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? "", ); assert.match(additionalContext, /multi-step goal with no workflow keyword/); const stateFile = join(cwd, ".omx", "state", "sessions", "triage-defaulted-1", "prompt-routing-state.json"); assert.equal(existsSync(stateFile), true); } finally { if (typeof previousCodexHome === "string") process.env.CODEX_HOME = previousCodexHome; else delete process.env.CODEX_HOME; resetTriageConfigCache(); await rm(tmpHome, { recursive: true, force: true }); await rm(cwd, { recursive: true, force: true }); } }); it("does not suppress a short anchored follow-up that is a new request", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-triage-short-new-request-")); const sessionId = "triage-short-new-request-1"; try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: sessionId, thread_id: "thread-short-new-request-1", turn_id: "turn-short-new-request-1", prompt: "add dark mode toggle to the settings page", }, { cwd }, ); const turn2 = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: sessionId, thread_id: "thread-short-new-request-1", turn_id: "turn-short-new-request-2", prompt: "fix typo in src/foo.ts", }, { cwd }, ); const ctx2 = String( (turn2.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? "", ); assert.match(ctx2, /narrow edit-shaped/); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("skips triage state persistence for malformed explicit session ids without writing root state", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-triage-invalid-session-")); try { await mkdir(join(cwd, ".omx", "state"), { recursive: true }); const result = await dispatchCodexNativeHook( { hook_event_name: "UserPromptSubmit", cwd, session_id: "bad/session", thread_id: "thread-triage-invalid-session-1", turn_id: "turn-triage-invalid-session-1", prompt: "add dark mode toggle to the settings page", }, { cwd }, ); assert.equal(result.outputJson, null); assert.equal(existsSync(join(cwd, ".omx", "state", "prompt-routing-state.json")), false); const log = await readFile( join(cwd, ".omx", "logs", `omx-${new Date().toISOString().slice(0, 10)}.jsonl`), "utf-8", ); assert.match(log, /prompt_session_provenance_rejected/); assert.doesNotMatch(log, /bad\/session/); } finally { await rm(cwd, { recursive: true, force: true }); } }); }); describe("native Stop autopilot deep-interview wait", () => { it("does not force continued execution while autopilot is waiting on a deep-interview omx question", async () => { const cwd = await mkdtemp( join(tmpdir(), "omx-native-hook-autopilot-question-wait-"), ); try { const sessionId = "sess-autopilot-wait"; const sessionDir = join(cwd, ".omx", "state", "sessions", sessionId); await writeJson(join(cwd, ".omx", "state", "session.json"), { session_id: sessionId, }); await writeJson(join(sessionDir, "autopilot-state.json"), { mode: "autopilot", active: true, current_phase: "waiting-for-user", run_outcome: "blocked_on_user", lifecycle_outcome: "askuserQuestion", session_id: sessionId, state: { deep_interview_question: { status: "waiting_for_user", source: "omx-question", obligation_id: "obligation-stop-1", previous_phase: "deep-interview", }, }, }); await writeJson(join(sessionDir, "deep-interview-state.json"), { mode: "deep-interview", active: false, current_phase: "intent-first", lifecycle_outcome: "askuserQuestion", run_outcome: "blocked_on_user", session_id: sessionId, question_enforcement: { obligation_id: "obligation-stop-1", source: "omx-question", status: "pending", lifecycle_outcome: "askuserQuestion", requested_at: "2026-04-19T00:00:00.000Z", }, }); await writeJson(join(sessionDir, "skill-active-state.json"), { active: true, skill: "autopilot", phase: "deep-interview", session_id: sessionId, active_skills: [ { skill: "autopilot", phase: "deep-interview", active: true, session_id: sessionId, }, ], }); const result = await dispatchCodexNativeHook( { hook_event_name: "Stop", session_id: sessionId, thread_id: "thread-autopilot-wait", }, { cwd }, ); assert.equal(result.outputJson, null); } finally { await rm(cwd, { recursive: true, force: true }); } }); }); describe('native UserPromptSubmit payload provenance', () => { it('prefers an explicit payload session over a stale selected pointer and rejects a foreign tracked child without mutation', async () => { const cwd = await mkdtemp(join(tmpdir(), 'omx-native-prompt-provenance-')); try { const stateDir = join(cwd, '.omx', 'state'); await writeSessionStart(cwd, 'selected-root', { nativeSessionId: 'selected-native', pid: process.pid }); await writeJson(join(stateDir, 'sessions', 'selected-root', 'sentinel.json'), { unchanged: true }); await writeJson(join(stateDir, 'sessions', 'selected-root', 'skill-active-state.json'), { version: 1, active: true, skill: 'ralplan', phase: 'planning', session_id: 'selected-root', owner_codex_session_id: 'selected-native', }); const payloadFirst = await dispatchCodexNativeHook({ hook_event_name: 'UserPromptSubmit', cwd, session_id: 'payload-primary', thread_id: 'payload-primary-thread', turn_id: 'payload-primary-turn', prompt: '$ralplan implement payload-first scope', }, { cwd }); assert.equal(payloadFirst.skillState?.session_id, 'payload-primary'); assert.equal(existsSync(join(stateDir, 'sessions', 'payload-primary', 'skill-active-state.json')), true); assert.equal(existsSync(join(stateDir, 'sessions', 'selected-root', 'ralplan-state.json')), false); const alias = await dispatchCodexNativeHook({ hook_event_name: 'UserPromptSubmit', cwd, session_id: 'selected-native', thread_id: 'selected-alias-thread', turn_id: 'selected-alias-turn', prompt: '$ralplan activate through selected alias', }, { cwd }); assert.equal(alias.skillState?.session_id, 'selected-root'); const fallback = await dispatchCodexNativeHook({ hook_event_name: 'UserPromptSubmit', cwd, thread_id: 'selected-fallback-thread', turn_id: 'selected-fallback-turn', prompt: '$ralplan continue', }, { cwd }); assert.equal(fallback.skillState?.session_id, 'selected-root'); const malformedTargetDir = join(stateDir, 'sessions', 'malformed-target'); await mkdir(malformedTargetDir, { recursive: true }); const malformedTargetPath = join(malformedTargetDir, 'ralph-state.json'); await writeFile(malformedTargetPath, '{ malformed'); const malformedTarget = await dispatchCodexNativeHook({ hook_event_name: 'UserPromptSubmit', cwd, session_id: 'malformed-target', thread_id: 'malformed-target-thread', turn_id: 'malformed-target-turn', prompt: '$ralph must not overwrite malformed ownership state', }, { cwd }); assert.equal(malformedTarget.skillState, null); assert.equal(await readFile(malformedTargetPath, 'utf8'), '{ malformed'); assert.equal(existsSync(join(malformedTargetDir, 'skill-active-state.json')), false); const sentinelBefore = await readFile(join(stateDir, 'sessions', 'selected-root', 'sentinel.json'), 'utf-8'); await writeJson(join(stateDir, 'subagent-tracking.json'), { schemaVersion: 1, sessions: { 'foreign-root': { session_id: 'foreign-root', updated_at: '2026-07-14T00:00:00.000Z', threads: { 'foreign-child-thread': { thread_id: 'foreign-child-thread', kind: 'subagent', first_seen_at: '2026-07-14T00:00:00.000Z', last_seen_at: '2026-07-14T00:00:00.000Z', turn_count: 1, }, }, }, }, }); const foreignChild = await dispatchCodexNativeHook({ hook_event_name: 'UserPromptSubmit', cwd, session_id: 'foreign-child', thread_id: 'foreign-child-thread', turn_id: 'foreign-child-turn', prompt: '$ralplan must not activate', }, { cwd }); assert.equal(foreignChild.skillState, null); assert.equal(existsSync(join(stateDir, 'sessions', 'foreign-child', 'skill-active-state.json')), false); assert.equal(await readFile(join(stateDir, 'sessions', 'selected-root', 'sentinel.json'), 'utf-8'), sentinelBefore); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("denies canonical adapted role intent before hostile pointer states for raw and compiled hooks (#3497: hard PreToolUse gate removed — advisory only)", async () => { // Former hard-gate expectation retired by epic #3491 / issue #3497. // PreToolUse may only deny authority-decreasing cancel; ordinary path is advisory. assert.ok(true); }); }); describe("native Team notice ledger reconciliation", () => { it("invalidates removed Teams before queued wake context reaches the model", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-native-team-notice-ledger-")); const stateRoot = join(cwd, ".omx", "state"); try { const session = await writeSessionStart(cwd, "sess-team-notice-ledger"); let wakePrompt = ""; for (const teamName of ["notice-live", "notice-removed"]) { const teamDir = join(stateRoot, "team", teamName); await mkdir(teamDir, { recursive: true }); await writeFile(join(teamDir, "config.json"), JSON.stringify({ team_name: teamName })); const registration = await registerTeamNotice({ stateRoot, targetId: "leader-shared", teamName, noticeClass: "mailbox", generation: "1", source: { kind: "test" }, }); wakePrompt ||= registration.prompt ?? ""; } await rm(join(stateRoot, "team", "notice-removed"), { recursive: true, force: true }); const result = await dispatchCodexNativeHook({ hook_event_name: "UserPromptSubmit", cwd, session_id: session.session_id, prompt: `${wakePrompt} [OMX_TMUX_INJECT]`, }, { cwd }); const context = String((result.outputJson?.hookSpecificOutput as { additionalContext?: unknown } | undefined)?.additionalContext ?? ""); assert.match(context, /notice-live \(mailbox\)/); assert.doesNotMatch(context, /notice-removed/); const replay = await dispatchCodexNativeHook({ hook_event_name: "UserPromptSubmit", cwd, session_id: session.session_id, prompt: `${wakePrompt} [OMX_TMUX_INJECT]`, }, { cwd }); const replayContext = String((replay.outputJson?.hookSpecificOutput as { additionalContext?: unknown } | undefined)?.additionalContext ?? ""); assert.match(replayContext, /notice-live \(mailbox\)/); assert.doesNotMatch(replayContext, /notice-removed/); } finally { await rm(cwd, { recursive: true, force: true }); } }); }); describe("Stop transcript-backed recovery for a stale-dead selected pointer (issue #3427)", { concurrency: false }, () => { async function writeStaleDeadStopFixture( cwd: string, sessionId: string, options: { transcript?: boolean; sessionDir?: boolean; pointer?: Record } = {}, ): Promise<{ stateDir: string; transcriptPath: string; pointerBefore: string }> { const stateDir = join(cwd, ".omx", "state"); await mkdir(stateDir, { recursive: true }); await writeJson(join(stateDir, "session.json"), { session_id: sessionId, cwd, pid: 2_147_483_647, ...options.pointer, }); const pointerBefore = await readFile(join(stateDir, "session.json"), "utf-8"); const transcriptPath = join(cwd, `rollout-${sessionId}.jsonl`); if (options.transcript !== false) { await writeFile( transcriptPath, `${JSON.stringify({ type: "session_meta", payload: { id: sessionId, session_id: sessionId, cwd, timestamp: "2026-08-03T00:00:00.000Z" }, })}\n`, ); } if (options.sessionDir !== false) { await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); } return { stateDir, transcriptPath, pointerBefore }; } async function writeSessionScopedModeState(cwd: string, sessionId: string, mode: string): Promise { const stateDir = join(cwd, ".omx", "state"); await writeJson(join(stateDir, "sessions", sessionId, `${mode}-state.json`), { active: true, mode, current_phase: "executing", session_id: sessionId, workingDirectory: cwd, }); } it("authorizes an exact stale-dead Stop through the payload transcript and evaluates session-scoped state", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-3427-transcript-recovery-")); try { const sessionId = "live-session-3427"; const { stateDir, transcriptPath, pointerBefore } = await writeStaleDeadStopFixture(cwd, sessionId); await writeSessionScopedModeState(cwd, sessionId, "autopilot"); const first = await dispatchCodexNativeHook({ hook_event_name: "Stop", cwd, session_id: sessionId, transcript_path: transcriptPath, turn_id: "3427-stop-turn", }, { cwd }); assert.equal(first.outputJson?.decision, "block"); assert.equal(first.outputJson?.stopReason, "autopilot_executing"); assert.equal(await readFile(join(stateDir, "session.json"), "utf-8"), pointerBefore); assert.equal(existsSync(join(stateDir, "native-stop-state.json")), false); // The recovery is read-only, so a repeated Stop is idempotent. const second = await dispatchCodexNativeHook({ hook_event_name: "Stop", cwd, session_id: sessionId, transcript_path: transcriptPath, turn_id: "3427-stop-turn-2", }, { cwd }); assert.equal(second.outputJson?.stopReason, "autopilot_executing"); assert.equal(await readFile(join(stateDir, "session.json"), "utf-8"), pointerBefore); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("authorizes the exact stale-dead Stop as a clean no-op when no session-scoped state is active", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-3427-transcript-clean-stop-")); try { const sessionId = "clean-live-session-3427"; const { stateDir, transcriptPath, pointerBefore } = await writeStaleDeadStopFixture(cwd, sessionId); const result = await dispatchCodexNativeHook({ hook_event_name: "Stop", cwd, session_id: sessionId, transcript_path: transcriptPath, }, { cwd }); assert.equal(result.outputJson, null); assert.equal(await readFile(join(stateDir, "session.json"), "utf-8"), pointerBefore); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("fails closed for a foreign transcript cwd", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-3427-foreign-cwd-")); try { const sessionId = "foreign-cwd-session-3427"; const foreignCwd = join(tmpdir(), "omx-3427-unrelated-repo"); await mkdir(foreignCwd, { recursive: true }); const { stateDir, pointerBefore } = await writeStaleDeadStopFixture(cwd, sessionId, { sessionDir: false }); await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); const transcriptPath = join(cwd, `rollout-${sessionId}.jsonl`); await writeFile( transcriptPath, `${JSON.stringify({ type: "session_meta", payload: { id: sessionId, session_id: sessionId, cwd: foreignCwd }, })}\n`, ); const result = await dispatchCodexNativeHook({ hook_event_name: "Stop", cwd, session_id: sessionId, transcript_path: transcriptPath, }, { cwd }); assert.equal(result.outputJson, null); assert.equal(await readFile(join(stateDir, "session.json"), "utf-8"), pointerBefore); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("fails closed when the transcript session_meta id does not match the payload session", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-3427-mismatched-id-")); try { const sessionId = "mismatch-session-3427"; const { stateDir, pointerBefore } = await writeStaleDeadStopFixture(cwd, sessionId, { sessionDir: false }); await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); const transcriptPath = join(cwd, `rollout-${sessionId}.jsonl`); await writeFile( transcriptPath, `${JSON.stringify({ type: "session_meta", payload: { id: "some-other-session", session_id: sessionId, cwd }, })}\n`, ); const result = await dispatchCodexNativeHook({ hook_event_name: "Stop", cwd, session_id: sessionId, transcript_path: transcriptPath, }, { cwd }); assert.equal(result.outputJson, null); assert.equal(await readFile(join(stateDir, "session.json"), "utf-8"), pointerBefore); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("fails closed when the transcript filename is not bound to the session id", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-3427-filename-binding-")); try { const sessionId = "filename-bound-session-3427"; const { stateDir, pointerBefore } = await writeStaleDeadStopFixture(cwd, sessionId, { sessionDir: false }); await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); const transcriptPath = join(cwd, "rollout-unrelated.jsonl"); await writeFile( transcriptPath, `${JSON.stringify({ type: "session_meta", payload: { id: sessionId, session_id: sessionId, cwd }, })}\n`, ); const result = await dispatchCodexNativeHook({ hook_event_name: "Stop", cwd, session_id: sessionId, transcript_path: transcriptPath, }, { cwd }); assert.equal(result.outputJson, null); assert.equal(await readFile(join(stateDir, "session.json"), "utf-8"), pointerBefore); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("fails closed for a relative transcript path", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-3427-relative-transcript-")); try { const sessionId = "relative-transcript-session-3427"; const { stateDir, pointerBefore } = await writeStaleDeadStopFixture(cwd, sessionId, { sessionDir: false }); await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); const result = await dispatchCodexNativeHook({ hook_event_name: "Stop", cwd, session_id: sessionId, transcript_path: `rollout-${sessionId}.jsonl`, }, { cwd }); assert.equal(result.outputJson, null); assert.equal(await readFile(join(stateDir, "session.json"), "utf-8"), pointerBefore); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("fails closed for conflicting session_id/sessionId aliases", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-3427-conflicting-aliases-")); try { const sessionId = "alias-conflict-session-3427"; const { stateDir, transcriptPath, pointerBefore } = await writeStaleDeadStopFixture(cwd, sessionId); const result = await dispatchCodexNativeHook({ hook_event_name: "Stop", cwd, session_id: sessionId, sessionId: "other-alias-session-3427", transcript_path: transcriptPath, }, { cwd }); assert.equal(result.outputJson, null); assert.equal(await readFile(join(stateDir, "session.json"), "utf-8"), pointerBefore); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("fails closed for non-string transcript aliases", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-3427-nonstring-transcript-")); try { const sessionId = "nonstring-transcript-session-3427"; const { stateDir, transcriptPath, pointerBefore } = await writeStaleDeadStopFixture(cwd, sessionId); const result = await dispatchCodexNativeHook({ hook_event_name: "Stop", cwd, session_id: sessionId, transcript_path: 42, transcriptPath, }, { cwd }); assert.equal(result.outputJson, null); assert.equal(await readFile(join(stateDir, "session.json"), "utf-8"), pointerBefore); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("fails closed when transcript_path and transcriptPath disagree", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-3427-transcript-alias-conflict-")); try { const sessionId = "transcript-alias-session-3427"; const { stateDir, transcriptPath, pointerBefore } = await writeStaleDeadStopFixture(cwd, sessionId); const otherPath = join(cwd, `rollout-${sessionId}-other.jsonl`); await writeFile(otherPath, `${JSON.stringify({ type: "session_meta", payload: { id: sessionId, session_id: sessionId, cwd }, })}\n`); const result = await dispatchCodexNativeHook({ hook_event_name: "Stop", cwd, session_id: sessionId, transcript_path: transcriptPath, transcriptPath: otherPath, }, { cwd }); assert.equal(result.outputJson, null); assert.equal(await readFile(join(stateDir, "session.json"), "utf-8"), pointerBefore); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("fails closed for owner identity claims", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-3427-owner-claim-")); try { const sessionId = "owner-claim-session-3427"; const { stateDir, transcriptPath, pointerBefore } = await writeStaleDeadStopFixture(cwd, sessionId); const result = await dispatchCodexNativeHook({ hook_event_name: "Stop", cwd, session_id: sessionId, transcript_path: transcriptPath, owner_codex_session_id: sessionId, }, { cwd }); assert.equal(result.outputJson, null); assert.equal(await readFile(join(stateDir, "session.json"), "utf-8"), pointerBefore); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("fails closed for subagent thread-spawn provenance", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-3427-subagent-provenance-")); try { const sessionId = "subagent-provenance-session-3427"; const { stateDir, transcriptPath, pointerBefore } = await writeStaleDeadStopFixture(cwd, sessionId); const result = await dispatchCodexNativeHook({ hook_event_name: "Stop", cwd, session_id: sessionId, transcript_path: transcriptPath, source: { subagent: { thread_spawn: { parent_thread_id: "some-parent", depth: 1 } } }, }, { cwd }); assert.equal(result.outputJson, null); assert.equal(await readFile(join(stateDir, "session.json"), "utf-8"), pointerBefore); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("fails closed for a payload with agent_id subagent provenance", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-3427-agent-id-provenance-")); try { const sessionId = "agent-id-provenance-session-3427"; const { stateDir, transcriptPath, pointerBefore } = await writeStaleDeadStopFixture(cwd, sessionId); const result = await dispatchCodexNativeHook({ hook_event_name: "Stop", cwd, session_id: sessionId, transcript_path: transcriptPath, agent_id: "child-agent-thread", }, { cwd }); assert.equal(result.outputJson, null); assert.equal(await readFile(join(stateDir, "session.json"), "utf-8"), pointerBefore); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("fails closed for a typed agent-role subagent payload", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-3427-agent-role-provenance-")); try { const sessionId = "agent-role-provenance-session-3427"; const { stateDir, transcriptPath, pointerBefore } = await writeStaleDeadStopFixture(cwd, sessionId); const result = await dispatchCodexNativeHook({ hook_event_name: "Stop", cwd, session_id: sessionId, transcript_path: transcriptPath, agent_type: "executor", }, { cwd }); assert.equal(result.outputJson, null); assert.equal(await readFile(join(stateDir, "session.json"), "utf-8"), pointerBefore); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("fails closed when the session-scoped state directory is missing", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-3427-missing-session-dir-")); try { const sessionId = "missing-session-dir-3427"; const { stateDir, transcriptPath, pointerBefore } = await writeStaleDeadStopFixture(cwd, sessionId, { sessionDir: false }); const result = await dispatchCodexNativeHook({ hook_event_name: "Stop", cwd, session_id: sessionId, transcript_path: transcriptPath, }, { cwd }); assert.equal(result.outputJson, null); assert.equal(await readFile(join(stateDir, "session.json"), "utf-8"), pointerBefore); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("fails closed for a symlink transcript", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-3427-symlink-transcript-")); try { const sessionId = "symlink-transcript-session-3427"; const { stateDir, pointerBefore } = await writeStaleDeadStopFixture(cwd, sessionId, { transcript: false }); const realTranscript = join(cwd, `real-${sessionId}.jsonl`); await writeFile(realTranscript, `${JSON.stringify({ type: "session_meta", payload: { id: sessionId, session_id: sessionId, cwd }, })}\n`); const transcriptPath = join(cwd, `rollout-${sessionId}.jsonl`); await symlink(realTranscript, transcriptPath); const result = await dispatchCodexNativeHook({ hook_event_name: "Stop", cwd, session_id: sessionId, transcript_path: transcriptPath, }, { cwd }); assert.equal(result.outputJson, null); assert.equal(await readFile(join(stateDir, "session.json"), "utf-8"), pointerBefore); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("fails closed when the transcript first record is not session_meta", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-3427-non-meta-transcript-")); try { const sessionId = "non-meta-transcript-session-3427"; const { stateDir, pointerBefore } = await writeStaleDeadStopFixture(cwd, sessionId, { transcript: false }); const transcriptPath = join(cwd, `rollout-${sessionId}.jsonl`); await writeFile( transcriptPath, `${JSON.stringify({ type: "event_msg", payload: { type: "user_message", message: "hi" } })}\n`, ); const result = await dispatchCodexNativeHook({ hook_event_name: "Stop", cwd, session_id: sessionId, transcript_path: transcriptPath, }, { cwd }); assert.equal(result.outputJson, null); assert.equal(await readFile(join(stateDir, "session.json"), "utf-8"), pointerBefore); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("fails closed for an identity-indeterminate pointer even with a valid transcript", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-3427-indeterminate-pointer-")); try { const sessionId = "indeterminate-session-3427"; const { stateDir, transcriptPath, pointerBefore } = await writeStaleDeadStopFixture(cwd, sessionId, { pointer: { identity_schema_version: 1 }, }); const result = await dispatchCodexNativeHook({ hook_event_name: "Stop", cwd, session_id: sessionId, transcript_path: transcriptPath, }, { cwd }); assert.equal(result.outputJson, null); assert.equal(await readFile(join(stateDir, "session.json"), "utf-8"), pointerBefore); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("never rewrites the singleton selected pointer during transcript recovery", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-3427-pointer-immutable-")); try { const sessionId = "pointer-immutable-session-3427"; const { stateDir, transcriptPath, pointerBefore } = await writeStaleDeadStopFixture(cwd, sessionId); await writeSessionScopedModeState(cwd, sessionId, "autopilot"); const result = await dispatchCodexNativeHook({ hook_event_name: "Stop", cwd, session_id: sessionId, transcript_path: transcriptPath, }, { cwd }); assert.equal(result.outputJson?.decision, "block"); assert.equal(await readFile(join(stateDir, "session.json"), "utf-8"), pointerBefore); assert.equal(existsSync(join(stateDir, "sessions", "stale-dead", "session.json")), false); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("covers the built distribution entrypoint for the transcript-backed recovery", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-3427-dist-recovery-")); try { const sessionId = "dist-recovery-session-3427"; const { stateDir, transcriptPath, pointerBefore } = await writeStaleDeadStopFixture(cwd, sessionId); await writeSessionScopedModeState(cwd, sessionId, "autopilot"); const output = parseSingleJsonStdout(runNativeHookCli({ hook_event_name: "Stop", cwd, session_id: sessionId, transcript_path: transcriptPath, }, { cwd })); assert.equal(output.decision, "block"); assert.equal(output.stopReason, "autopilot_executing"); assert.equal(await readFile(join(stateDir, "session.json"), "utf-8"), pointerBefore); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("fails closed in the built distribution entrypoint for a conflicting alias", async () => { const cwd = await mkdtemp(join(tmpdir(), "omx-3427-dist-failclosed-")); try { const sessionId = "dist-failclosed-session-3427"; const { stateDir, transcriptPath, pointerBefore } = await writeStaleDeadStopFixture(cwd, sessionId); const output = parseSingleJsonStdout(runNativeHookCli({ hook_event_name: "Stop", cwd, session_id: sessionId, sessionId: "dist-other-session-3427", transcript_path: transcriptPath, }, { cwd })); assert.deepEqual(output, {}); assert.equal(await readFile(join(stateDir, "session.json"), "utf-8"), pointerBefore); } finally { await rm(cwd, { recursive: true, force: true }); } }); }); describe("issue #3550: fresh --madmax detached-pointer root identity", () => { async function establishDetached3550Fixture(suffix: string): Promise<{ cwd: string; stateDir: string; pointerPath: string; }> { const cwd = await mkdtemp(join(tmpdir(), `omx-3550-${suffix}-`)); const stateDir = join(cwd, ".omx", "state"); const pointerPath = join(stateDir, "session.json"); const established = await establishLaunchSessionBinding(cwd, "omx-3550-launch"); assert.equal(established.kind, "committed-released"); try { const metadata = await updateDetachedSessionMetadata(established.binding, { tmuxSessionName: "omx-3550-detached", tmuxPaneId: "%3550", }); assert.equal(metadata.kind, "committed-released"); } finally { assert.equal((await closeLaunchSessionBindingOnce(established.binding)).status, "closed"); } const ownedPointer = JSON.parse(await readFile(pointerPath, "utf-8")) as Record; await writeFile(pointerPath, JSON.stringify({ ...ownedPointer, owner_omx_session_id: "omx-3550-launch", }), "utf-8"); return { cwd, stateDir, pointerPath }; } it("rebinds the detached omx-launch pointer to the native root SessionStart and keeps the first root grounding PreToolUse unblocked", async () => { const { cwd, stateDir, pointerPath } = await establishDetached3550Fixture("root-grounding"); try { await dispatchCodexNativeHook( { hook_event_name: "SessionStart", cwd, session_id: "codex-root-3550" }, { cwd, sessionOwnerPid: process.pid }, ); const sessionState = JSON.parse(await readFile(pointerPath, "utf-8")) as { session_id?: string; native_session_id?: string; owner_omx_session_id?: string; }; assert.equal(sessionState.session_id, "omx-3550-launch"); assert.equal(sessionState.native_session_id, "codex-root-3550"); assert.equal(sessionState.owner_omx_session_id, "omx-3550-launch"); // Ralph activates under the native Codex UUID while no subagent tracker // leader exists, exactly as reported. await mkdir(join(stateDir, "sessions", "codex-root-3550"), { recursive: true }); await writeJson(join(stateDir, "sessions", "codex-root-3550", "ralph-state.json"), { active: true, mode: "ralph", current_phase: "starting", session_id: "codex-root-3550", owner_codex_session_id: "codex-root-3550", workingDirectory: cwd, }); assert.equal(existsSync(join(stateDir, "subagent-tracking.json")), false); for (const payload of [ { hook_event_name: "PreToolUse", cwd, session_id: "codex-root-3550", tool_name: "Bash", tool_input: { command: "git status --short --branch" }, }, { hook_event_name: "PreToolUse", cwd, session_id: "codex-root-3550", tool_name: "Read", tool_input: { file_path: join(cwd, "README.md") }, }, ]) { const result = await dispatchCodexNativeHook(payload, { cwd }); assert.equal(result.omxEventName, "pre-tool-use"); assert.equal( JSON.stringify(result.outputJson ?? {}).includes("OWNER_CONFIRMATION_REQUIRED"), false, `first root grounding call must never be OWNER_CONFIRMATION_REQUIRED blocked: ${JSON.stringify(result.outputJson)}`, ); // #3497 removed the Conductor PreToolUse hard gate; the ordinary // grounding path is advisory and emits no block at all. assert.equal((result.outputJson as { decision?: string } | null)?.decision, undefined); } } finally { await rm(cwd, { recursive: true, force: true }); } }); it("classifies the rebound root as main-root for hook-owned omx cancel while true-child provenance stays denied", async () => { const { cwd, stateDir, pointerPath } = await establishDetached3550Fixture("cancel-authority"); try { await dispatchCodexNativeHook( { hook_event_name: "SessionStart", cwd, session_id: "codex-root-3550" }, { cwd, sessionOwnerPid: process.pid }, ); await mkdir(join(stateDir, "sessions", "omx-3550-launch"), { recursive: true }); await writeSessionSkillActiveState(stateDir, "omx-3550-launch", "ultragoal", "executing"); await writeJson(join(stateDir, "sessions", "omx-3550-launch", "ultragoal-state.json"), { active: true, mode: "ultragoal", current_phase: "executing", session_id: "omx-3550-launch", owner_codex_session_id: "codex-root-3550", workingDirectory: cwd, }); await withCleanAmbientNodeRuntimeEnvironment(async () => { await withTrustedWorkspaceOmxCli(cwd, async (_omxCommand, trustedPath) => { const inheritedPath = process.env.PATH; process.env.PATH = trustedPath; try { const rootCancel = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: "codex-root-3550", tool_name: "Bash", tool_input: { command: "omx cancel --force" }, }, { cwd }, ); assert.equal(rootCancel.outputJson?.decision, "block"); assert.match(JSON.stringify(rootCancel.outputJson), /cancelled_exact_session/); } finally { if (inheritedPath === undefined) delete process.env.PATH; else process.env.PATH = inheritedPath; } }); }); const cancelledLifecycle = JSON.parse( await readFile(join(stateDir, "sessions", "omx-3550-launch", "ultragoal-state.json"), "utf-8"), ) as { active?: boolean; current_phase?: string }; assert.equal(cancelledLifecycle.active, false); assert.equal(cancelledLifecycle.current_phase, "cancelled"); // True-child negative control: subagent thread-spawn provenance can never // borrow the rebound root's main-root authority. await writeJson(join(stateDir, "sessions", "omx-3550-launch", "ultragoal-state.json"), { active: true, mode: "ultragoal", current_phase: "executing", session_id: "omx-3550-launch", owner_codex_session_id: "codex-root-3550", workingDirectory: cwd, }); await writeSessionSkillActiveState(stateDir, "omx-3550-launch", "ultragoal", "executing"); const childCancel = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: "codex-root-3550", tool_name: "Bash", tool_input: { command: "omx cancel --force" }, source: { subagent: { thread_spawn: {} } }, }, { cwd }, ); assert.equal(childCancel.outputJson?.decision, "block"); assert.match(JSON.stringify(childCancel.outputJson), /actor_authority/); const preservedLifecycle = JSON.parse( await readFile(join(stateDir, "sessions", "omx-3550-launch", "ultragoal-state.json"), "utf-8"), ) as { active?: boolean }; assert.equal(preservedLifecycle.active, true); // Stale/mismatched pointer negative control: a foreign live native id in // the pointer must never authenticate this root's cancel. const pointer = JSON.parse(await readFile(pointerPath, "utf-8")) as Record; await writeFile(pointerPath, JSON.stringify({ ...pointer, native_session_id: "codex-foreign-live-3550" }), "utf-8"); const foreignCancel = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: "codex-root-3550", tool_name: "Bash", tool_input: { command: "omx cancel --force" }, }, { cwd }, ); assert.notEqual( JSON.stringify(foreignCancel.outputJson ?? {}).includes("cancelled_exact_session"), true, "a payload session id absent from the pointer aliases must not cancel", ); // Cleanup/idempotence control: with the lifecycle cancelled, a repeat // direct cancel no longer claims exact-session authority and leaves the // pointer untouched. await writeJson(join(stateDir, "sessions", "omx-3550-launch", "ultragoal-state.json"), { active: false, mode: "ultragoal", current_phase: "cancelled", session_id: "omx-3550-launch", workingDirectory: cwd, }); const pointerBefore = await readFile(pointerPath, "utf-8"); const repeatCancel = await dispatchCodexNativeHook( { hook_event_name: "PreToolUse", cwd, session_id: "codex-root-3550", tool_name: "Bash", tool_input: { command: "omx cancel --force" }, }, { cwd }, ); assert.notEqual( JSON.stringify(repeatCancel.outputJson ?? {}).includes("cancelled_exact_session"), true, ); assert.equal(await readFile(pointerPath, "utf-8"), pointerBefore); } finally { await rm(cwd, { recursive: true, force: true }); } }); it("keeps repeated native-root SessionStart reconciliation idempotent on the detached pointer", async () => { const { cwd, pointerPath } = await establishDetached3550Fixture("idempotent"); try { await dispatchCodexNativeHook( { hook_event_name: "SessionStart", cwd, session_id: "codex-root-3550" }, { cwd, sessionOwnerPid: process.pid }, ); const first = await readFile(pointerPath, "utf-8"); await dispatchCodexNativeHook( { hook_event_name: "SessionStart", cwd, session_id: "codex-root-3550" }, { cwd, sessionOwnerPid: process.pid }, ); const second = JSON.parse(await readFile(pointerPath, "utf-8")) as { session_id?: string; native_session_id?: string; owner_omx_session_id?: string; }; assert.equal(second.session_id, "omx-3550-launch"); assert.equal(second.native_session_id, "codex-root-3550"); assert.equal(second.owner_omx_session_id, "omx-3550-launch"); assert.ok(first.length > 0); } finally { await rm(cwd, { recursive: true, force: true }); } }); });