Two root causes addressed:
1. events reducer (state.py): replaced operator.add with _merge_events, a
dedup reducer keyed on (type, timestamp). operator.add concatenated the
full returned list on top of the existing state list, doubling events on
every serial node. _merge_events only appends events not already present.
2. demo_runner.py: replaced partial-node-update event processing with
app.get_state() canonical events. The old approach used total_seen as a
cursor into each node's partial update, but parallel nodes (generate_spec_tests
+ generate_solution) each returned 2-item lists — the second one's events
were always at events[2:] = [] and got deferred to the next serial node,
causing misalignment that led to duplicate renders and the AttributeError.
Using canonical state events after each astream() yield fixes the alignment.
3. framework/streaming.py: added isinstance(payload, dict) guard in
format_event_for_timeline to prevent AttributeError if pay…
Fix 5 — Dual-oracle testing: generate_spec_tests node generates tests from
task description only (no code). execute_solution now runs both spec tests
and adversarial tests; both must pass. Failure summaries label which suite
caught the failure. New generate_from_spec template in qa_adversarial.yaml.
SPEC_TESTS_GENERATED + REPAIR_REVIEW event types added to events.py and
streaming.py.
Fix 7 — Graceful degradation: call_with_fallback() + _get_fallback() added
to LLMRouter. Returns role-specific safe defaults instead of crashing on
schema validation exhaustion. diagnose_failure now uses call_with_fallback
and tracks itself in degraded_nodes. Confidence-aware routing added to
graph: confidence < 0.3 routes to blind retry (generate_solution) instead
of targeted repair.
Fix 9 — Sandbox improvements: solution code written to a separate temp file
and loaded as an isolated module via importlib, preventing test namespace
coupling to implementation internals. Resource limits applied via preexec…
Fix two remaining Python 3.13 / Gradio 5 incompatibilities
1. app.py: remove theme="soft" from demo.launch() — theme parameter was
removed from Blocks.launch() in Gradio 5 (TypeError: unexpected keyword)
2. framework/event_bus.py: replace deprecated asyncio.get_event_loop()
with asyncio.get_running_loop() — get_event_loop() raises DeprecationWarning
and can error in Python 3.13 when no current loop exists. New logic:
if a loop is running, schedule via create_task(); otherwise asyncio.run().
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Full implementation of the autonomous self-healing code agent:
- LangGraph state machine with Generator → QA → Executor → Debugger → Memory → Repair loop
- Unified LLM Router with Ollama (local), HuggingFace Transformers (GPU), and Mock providers
- Async Python execution sandbox with subprocess isolation and timeout enforcement
- Rolling memory summarizer capping lessons at 5 bullets to prevent context overflow
- JSON schema validation with automatic retry on malformed LLM output
- YAML-based prompt templates for all agent roles (generator, qa_adversarial, debugger, memory_summarizer)
- Event-driven streaming architecture for live Gradio UI updates
- Gradio demo with two tabs: Run Live Agent (streaming) and Performance (precomputed charts)
- 8-task benchmark suite covering interval merging, data normalization, log processing, and boundary conditions
- 30 tests passing covering sandbox, schema validation, prompt loading, event bus, and full graph integration
Co-Authored-By: Claude Opu…