fix(issue-34): merge review - a sub-job cancelled before injection keeps its reason in the review table; unattributed open jobs come with a verdict rule

- registry.get_feedback only returned the detail of the last completed or
  error event, so a sub-job the wrapper cancelled before injection
  (target_not_found, session_busy) reached the review table as a bare
  `cancelled` and its reason was lost (merge review, grok N1). get_feedback
  takes the event kinds to read (default unchanged); the table asks for
  cancelled too and shows NOT-DISPATCHED with the reason. Test T13, both
  reasons, fails without the fix.
- the [귀속되지 않은 열린 잡] line listed jobs registered without
  --parent-job but gave the reviewer no rule for them - the one case the
  table cannot show (claude K1). A rule sentence follows the line whenever
  such a job exists; without one the prompt is unchanged. T6 pins it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VCgu9Fa9iT7TbJUSzdq4Ff
This commit is contained in:
GodopuandClaude Fable 5.1 committed 2026-09-25 16:19:12 +09:00
1 parent ed293dcef4
commit 8a9101ed30
3 files changed
+49 -9

No files matched your search

+11 -8
View File
@@ -19,7 +19,7 @@ import logging
import sys
import uuid
from pathlib import Path
from typing import Any, Dict, List, Optional
from typing import Any, Dict, List, Optional, Tuple
from lib_py.delegation import mqtt_common
from lib_py.delegation.mqtt_common import (
@@ -313,17 +313,20 @@ def _iter_records(registry_dir: str):
logger.warning("skipping unreadable record %s: %s", path, exc)
def get_feedback(job_id: str, registry_dir: str = DEFAULT_REGISTRY_DIR) -> str:
"""Read the job's audit log or events log and return the detail of the last completed/error event."""
def get_feedback(job_id: str, registry_dir: str = DEFAULT_REGISTRY_DIR,
events: Tuple[str, ...] = ("completed", "error")) -> str:
"""Read the job's audit log or events log and return the detail of the last
event whose kind is in ``events`` (completed/error by default; Issue #34's
sub-job table adds ``cancelled`` to see why a job was never dispatched)."""
# 1) Try the unified audit log first (ndjson) since it's written synchronously by the subscriber
try:
from lib_py.delegation import mqtt_common
logs_dir = mqtt_common.get_logs_dir()
events = list(mqtt_common.iter_logged_events(job_id, logs_dir))
for e in reversed(events):
if e.get("source_event") in ("completed", "error"):
logged = list(mqtt_common.iter_logged_events(job_id, logs_dir))
for e in reversed(logged):
if e.get("source_event") in events:
return e.get("detail", "")
if e.get("event") in ("completed", "error"):
if e.get("event") in events:
return e.get("detail", "")
except Exception:
pass
@@ -339,7 +342,7 @@ def get_feedback(job_id: str, registry_dir: str = DEFAULT_REGISTRY_DIR) -> str:
continue
try:
payload = json.loads(line)
if payload.get("event") in ("completed", "error"):
if payload.get("event") in events:
feedback = payload.get("detail", "")
except json.JSONDecodeError:
continue