fix: prompt delivery через stdin и захват session ID

- Зачем:
  - длинные XML-промпты (60+ строк) ломали shell quoting при inline-передаче в codex exec.
  - session ID терялся из-за 2>/dev/null на stderr, делая resume невозможным.
- Что:
  - промпт записывается в temp-файл, передаётся через stdin: `codex exec ... - < file`.
  - stderr перенаправлен в temp-файл, session ID извлекается через grep.
  - resume унифицирован: тот же stdin-механизм вместо inline-аргумента.
  - fallback fresh exec явно обновляет CODEX_SESSION_ID.
  - cleanup дополнен новыми temp-файлами (prompt, stderr).
- Проверка:
  - smoke test: plan review → 2 раунда с resume через session ID — OK.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-06 21:16:06 +03:00
co-authored by Claude Opus 4.6
parent 649256517e
commit 0b8891e19e
2 changed files with 34 additions and 13 deletions
+1
View File
@@ -0,0 +1 @@
.codex/
+33 -13
View File
@@ -293,13 +293,16 @@ Flags:
- `-s read-only` — reviewer only reads, does not write - `-s read-only` — reviewer only reads, does not write
- `-o /tmp/codex-review-${REVIEW_ID}.md` — file for capturing output - `-o /tmp/codex-review-${REVIEW_ID}.md` — file for capturing output
**Prompt delivery:** write the prompt to `/tmp/codex-prompt-${REVIEW_ID}.md` via **Write tool**, then pass via stdin redirection (`- < file`). This avoids shell quoting issues with long XML prompts.
```bash ```bash
timeout 600 codex exec \ timeout 600 codex exec \
-m gpt-5.4 \ -m gpt-5.4 \
-c model_reasoning_effort=high \ -c model_reasoning_effort=high \
-s read-only \ -s read-only \
-o /tmp/codex-review-${REVIEW_ID}.md \ -o /tmp/codex-review-${REVIEW_ID}.md \
"PROMPT" - < /tmp/codex-prompt-${REVIEW_ID}.md \
2>/tmp/codex-stderr-${REVIEW_ID}.txt
``` ```
**Important:** **Important:**
@@ -307,13 +310,20 @@ timeout 600 codex exec \
- Use `timeout: 620000` parameter in Bash tool for headroom. - Use `timeout: 620000` parameter in Bash tool for headroom.
- The command is **synchronous**: when it returns, the `-o` file is ready. Do **NOT** use a poll-loop (`while/sleep`). - The command is **synchronous**: when it returns, the `-o` file is ready. Do **NOT** use a poll-loop (`while/sleep`).
- If exit code = 124 (timeout) — inform the user and offer to retry. - If exit code = 124 (timeout) — inform the user and offer to retry.
- stderr is redirected to a temp file for session ID capture and error diagnostics.
**After launch:** find the `session id: <uuid>` line in the output and save the value as `CODEX_SESSION_ID` — needed for `resume` in subsequent rounds. **After launch:** extract the session ID from the stderr file:
```bash
grep -o 'session id: [a-f0-9-]*' /tmp/codex-stderr-${REVIEW_ID}.txt | head -1 | sed 's/session id: //'
```
Save the result as `CODEX_SESSION_ID` — needed for `resume` in subsequent rounds. If grep returns empty — session ID not available, resume will not work (fallback to fresh exec).
**Notes:** **Notes:**
- Default model: `gpt-5.4` with `model_reasoning_effort=high`. User can override via arguments. - Default model: `gpt-5.4` with `model_reasoning_effort=high`. User can override via arguments.
- Always `-s read-only` — reviewer must not write files. - Always `-s read-only` — reviewer must not write files.
- `-o` captures output to file. Do **NOT** run in background — the command returns control on its own. - `-o` captures model output to file. Session ID goes to stderr (captured separately). Do **NOT** run in background.
### Step 5: Read the review and check the verdict ### Step 5: Read the review and check the verdict
@@ -356,11 +366,10 @@ Based on the reviewer's findings:
**Resume is the primary path.** Saves tokens and preserves session context. A fresh `codex exec` without resume is an **emergency fallback** that costs significantly more tokens. Use only if resume fails. **Resume is the primary path.** Saves tokens and preserves session context. A fresh `codex exec` without resume is an **emergency fallback** that costs significantly more tokens. Use only if resume fails.
1. Run resume with stderr suppressed (`2>/dev/null`): 1. Write the resume prompt to `/tmp/codex-prompt-${REVIEW_ID}.md` via **Write tool** (overwrite previous prompt):
```bash ```
timeout 600 codex exec resume ${CODEX_SESSION_ID} \ I've revised based on your feedback.
"I've revised based on your feedback.
Here's what I changed: Here's what I changed:
[List of fixes] [List of fixes]
@@ -369,22 +378,32 @@ Re-review with the same adversarial stance. Focus on:
1. Whether my fixes actually resolve the reported issues 1. Whether my fixes actually resolve the reported issues
2. Any NEW issues introduced by the fixes 2. Any NEW issues introduced by the fixes
End with VERDICT: APPROVED or VERDICT: REVISE" 2>/dev/null End with VERDICT: APPROVED or VERDICT: REVISE
```
2. Run resume via stdin redirection:
```bash
timeout 600 codex exec resume ${CODEX_SESSION_ID} \
- < /tmp/codex-prompt-${REVIEW_ID}.md \
2>/tmp/codex-stderr-${REVIEW_ID}.txt
``` ```
Use `timeout: 620000` in Bash tool parameters. Use `timeout: 620000` in Bash tool parameters.
**Why `2>/dev/null`:** `codex exec` by design separates streams — progress/metadata → stderr, final model response → stdout. Suppressing stderr gives clean output without CLI noise. The Bash tool result = review only. stderr is redirected to temp file for diagnostics. On resume failure, check `/tmp/codex-stderr-${REVIEW_ID}.txt` for details.
2. Check the result by exit code: 3. Check the result by exit code:
- **exit 0** — success. stdout contains clean review. Show to user directly (Write to file and Read are **not needed**). Check VERDICT: the last non-empty line of stdout = `VERDICT: APPROVED` or `VERDICT: REVISE`. If verdict is missing → output may have been truncated, proceed to Fallback. Then apply verdict handling from Step 5 (APPROVED → Step 8, REVISE → Step 6). - **exit 0** — success. stdout contains clean review. Show to user directly (Write to file and Read are **not needed**). Check VERDICT: the last non-empty line of stdout = `VERDICT: APPROVED` or `VERDICT: REVISE`. If verdict is missing → output may have been truncated, proceed to Fallback. Then apply verdict handling from Step 5 (APPROVED → Step 8, REVISE → Step 6).
- **exit 124** — timeout. Tell the user: "Reviewer did not respond within 10 minutes" and offer to retry. - **exit 124** — timeout. Tell the user: "Reviewer did not respond within 10 minutes" and offer to retry.
- **other exit code** — tell the user: "Resume failed (exit code N)". Proceed to Fallback. Diagnostics without stderr are unavailable — do not try to parse stdout as an error. - **other exit code** — tell the user: "Resume failed (exit code N)". Proceed to Fallback. Check `/tmp/codex-stderr-${REVIEW_ID}.txt` for diagnostics.
**Fallback** — if `resume` did not work (session expired, session ID not captured, error): **Fallback** — if `resume` did not work (session expired, session ID not captured, error):
1. Collect the list of changed files (same as Step 3). 1. Collect the list of changed files (same as Step 3).
2. Launch a fresh `codex exec -o` with a description of previous rounds in the prompt. 2. Write the fallback prompt (with description of previous rounds) to `/tmp/codex-prompt-${REVIEW_ID}.md`.
3. Launch a fresh `codex exec` using the **same command template as Step 4** (stdin via `- < file`, stderr to temp file, `-o` for output).
4. **Extract the new session ID** from `/tmp/codex-stderr-${REVIEW_ID}.txt` and **refresh `CODEX_SESSION_ID`** — otherwise subsequent resume attempts will use the stale session ID.
Return to **Step 5**. Return to **Step 5**.
@@ -422,7 +441,8 @@ Return to **Step 5**.
**Outside Plan Mode:** **Outside Plan Mode:**
```bash ```bash
rm -f /tmp/claude-plan-${REVIEW_ID}.md /tmp/codex-review-${REVIEW_ID}.md rm -f /tmp/claude-plan-${REVIEW_ID}.md /tmp/codex-review-${REVIEW_ID}.md \
/tmp/codex-prompt-${REVIEW_ID}.md /tmp/codex-stderr-${REVIEW_ID}.txt
``` ```
If the user declined rm — continue without error. If the user declined rm — continue without error.