fix(claude_session): surface real CLI error + sanitize nested env (#85) #90

Merged
chaim merged 1 commits from worktree-task85-claude-session-nested into main 2026-06-06 19:30:23 +00:00
Owner

מה ולמה

תיקון TaskMaster legal-ai #85write_interim_draft נכשל לכל הבלוקים מתוך instance ה-CEO עם Claude CLI failed (exit 1): unknown error.

שני שינויים ב-mcp-server/src/legal_mcp/services/claude_session.py:

  1. חשיפת השגיאה האמיתית (הזכייה הוודאית): ב-exit≠0 לוכדים ומתעדים גם stderr וגם stdout (ה-CLI לעיתים כותב את האבחון ל-stdout או לשום מקום), במקום לקרוס ל-"unknown error". זו בדיוק הסיבה ש-#85 נשאר לא-פתור — השגיאה האמיתית נבלעה.

  2. הקשחה הגנתית: סינון סמני-סשן של Claude Code (CLAUDECODE, CLAUDE_CODE_*, CLAUDE_AGENT_*, AI_AGENT, CLAUDE_EFFORT) מה-env של קריאת claude -p מקוננת + הרצה מ-$HOME, כדי לנתק את הקריאה המקוננת ממצב-הסשן/פרויקט של ההורה. מיישר את query() עם query_streaming() שכבר מגדיר cwd=HOME. משתני auth/config נשמרים.

כנות מחקרית: הכשל המקורי (בהקשר adapter claude_local של ה-CEO) לא שוחזר בסשן אינטראקטיבי רגיל — claude -p מקונן מצליח שם גם בקוד הישן וגם בחדש. לכן סינון ה-env הוא חשוד, לא סיבה מוכחת. הערך הוודאי הוא הדיאגנוסטיקה: בפעם הבאה שזה ייכשל, נראה את השגיאה האמיתית.

Invariants — הצהרה

  • נוגע / מקיים: G1 (נרמול-במקור — תיקון ב-spawn ולא בקריאה), G2 (אין מסלול מקביל — אותה query()), כלל-הנדסה §6 (אין בליעה שקטה של שגיאות). INV: feedback_claude_session_local_only נשמר — כל הקריאות נשארות מקומיות.

אימות

  • pytest tests/test_claude_session.py — 2 passed (סינון env: מסיר סמנים, שומר auth/PATH).
  • E2E: query('...PONG') מקונן מתוך סשן CLAUDECODE=1 מחזיר PONG.
  • import של המודול תקין.

🤖 Generated with Claude Code

## מה ולמה תיקון TaskMaster `legal-ai` #85 — `write_interim_draft` נכשל לכל הבלוקים מתוך instance ה-CEO עם `Claude CLI failed (exit 1): unknown error`. שני שינויים ב-`mcp-server/src/legal_mcp/services/claude_session.py`: 1. **חשיפת השגיאה האמיתית (הזכייה הוודאית):** ב-exit≠0 לוכדים ומתעדים גם stderr **וגם** stdout (ה-CLI לעיתים כותב את האבחון ל-stdout או לשום מקום), במקום לקרוס ל-"unknown error". זו בדיוק הסיבה ש-#85 נשאר לא-פתור — השגיאה האמיתית נבלעה. 2. **הקשחה הגנתית:** סינון סמני-סשן של Claude Code (`CLAUDECODE`, `CLAUDE_CODE_*`, `CLAUDE_AGENT_*`, `AI_AGENT`, `CLAUDE_EFFORT`) מה-env של קריאת `claude -p` מקוננת + הרצה מ-`$HOME`, כדי לנתק את הקריאה המקוננת ממצב-הסשן/פרויקט של ההורה. מיישר את `query()` עם `query_streaming()` שכבר מגדיר `cwd=HOME`. משתני auth/config נשמרים. > **כנות מחקרית:** הכשל המקורי (בהקשר adapter `claude_local` של ה-CEO) **לא שוחזר** בסשן אינטראקטיבי רגיל — `claude -p` מקונן מצליח שם גם בקוד הישן וגם בחדש. לכן סינון ה-env הוא **חשוד, לא סיבה מוכחת**. הערך הוודאי הוא הדיאגנוסטיקה: בפעם הבאה שזה ייכשל, נראה את השגיאה האמיתית. ## Invariants — הצהרה - **נוגע / מקיים:** G1 (נרמול-במקור — תיקון ב-spawn ולא בקריאה), G2 (אין מסלול מקביל — אותה `query()`), כלל-הנדסה §6 (אין בליעה שקטה של שגיאות). INV: `feedback_claude_session_local_only` נשמר — כל הקריאות נשארות מקומיות. ## אימות - `pytest tests/test_claude_session.py` — 2 passed (סינון env: מסיר סמנים, שומר auth/PATH). - E2E: `query('...PONG')` מקונן מתוך סשן `CLAUDECODE=1` מחזיר `PONG`. - import של המודול תקין. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
chaim added 1 commit 2026-06-06 19:30:18 +00:00
write_interim_draft failed for all blocks from the CEO MCP instance with
"Claude CLI failed (exit 1): unknown error". Two fixes:

1. Error surfacing (the certain win): on non-zero exit, capture and log
   both stderr AND stdout (the CLI sometimes writes its diagnostic to
   stdout or nowhere), so the next occurrence is diagnosable instead of
   collapsing to "unknown error". This is why #85 was unsolved — the real
   error was swallowed (engineering rule §6: no silent swallow).

2. Defensive hardening: strip Claude Code session markers (CLAUDECODE,
   CLAUDE_CODE_*, CLAUDE_AGENT_*, AI_AGENT, CLAUDE_EFFORT) from the env of
   nested `claude -p` calls and run them from $HOME, decoupling them from
   the parent agent's session/project state. Aligns query() with the
   existing query_streaming() path (which already sets cwd=HOME). Auth/
   config vars are preserved.

Note: the original adapter-context failure could not be reproduced in a
plain interactive session (nested claude -p succeeds there in both old and
new code), so the env markers are a suspect, not a proven cause. The real
value is the diagnostics. Verified: nested query() returns PONG from
inside a CLAUDECODE=1 session; unit tests cover env sanitization.

Invariants: G1 (normalize at source — fix the spawn, not readers),
G2 (no parallel path — same query()), §6 (no silent error swallow).
INV: feedback_claude_session_local_only preserved (all calls stay local).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
chaim merged commit 12bdec10fa into main 2026-06-06 19:30:23 +00:00
chaim deleted branch worktree-task85-claude-session-nested 2026-06-06 19:30:23 +00:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: ezer-mishpati/legal-ai#90