M10: ProcessingRunner skeleton

- Add modules/runtime/runner.py with ProcessingRunner and ProcessingRequest
- Wire process_images to delegate through runner (internal only)
- Add test/quality/test_processing_runner.py contract test

Behavior-preserving. Zero blast radius. All callers unchanged.

Made-with: Cursor
This commit is contained in:
Michael Cahill 2026-03-11 21:50:25 -07:00
parent 11b9e0f16e
commit 59e46fa069
6 changed files with 192 additions and 1 deletions

View file

@ -0,0 +1,125 @@
# M10 — ProcessingRunner Skeleton
Phase: **Phase III — Runner & Service Boundary**
Status: Planned
---
# 1. Intent / Target
Introduce the **ProcessingRunner** abstraction that will become the unified execution surface for Serena.
Currently the pipeline is invoked directly through internal orchestration code.
This milestone introduces a **runner boundary** that:
- encapsulates pipeline execution
- standardizes runtime entrypoints
- prepares Serena for CLI / API / service mode
The runner initially acts as a **thin adapter** around existing behavior.
No behavior changes are permitted.
---
# 2. Scope Boundaries
### In scope
- Create `ProcessingRunner` skeleton at `modules/runtime/runner.py`
- Define `ProcessingRequest` wrapping `StableDiffusionProcessing`
- Wire `process_images` to delegate through runner (internal only)
- Maintain existing CLI/UI/API/scripts behavior
- Add minimal contract test at `test/quality/test_processing_runner.py`
### Out of scope
- No runtime behavior changes
- No async processing yet
- No service layer
- No multiprocessing
- No new configuration surfaces
- No performance changes
- No RuntimeContext in runner (M10)
---
# 3. Clarifications (Authoritative)
| Decision | Choice |
|----------|--------|
| Module path | `modules/runtime/runner.py` |
| ProcessingRequest | Wraps `p`: `ProcessingRequest(processing=p)` |
| RuntimeContext | Not passed to runner; omit from constructor |
| Wiring | Inside `process_images` only; all callers unchanged |
| Test location | `test/quality/test_processing_runner.py` |
---
# 4. Invariants
| Surface | Invariant | Verification |
|---------|-----------|--------------|
| CLI behavior | Identical outputs and execution path | smoke tests |
| API responses | No schema changes | tests |
| Processing results | Byte-identical outputs | golden comparison |
| Runtime state | No side effects introduced | test suite |
| CI coverage | ≥ 40% | CI gate |
---
# 5. Implementation Steps
## Step 1 — Create runner module
Create `modules/runtime/runner.py`:
```python
class ProcessingRequest:
def __init__(self, processing):
self.processing = processing
class ProcessingRunner:
def run(self, request):
from modules.processing import process_images_inner
return process_images_inner(request.processing)
```
## Step 2 — Wire process_images
Inside `process_images`, replace:
```python
res = process_images_inner(p)
```
With:
```python
from modules.runtime.runner import ProcessingRunner, ProcessingRequest
runner = ProcessingRunner()
request = ProcessingRequest(p)
res = runner.run(request)
```
## Step 3 — Add contract test
Add `test/quality/test_processing_runner.py` with delegation test.
---
# 6. Risk & Rollback Plan
Risk level: **Low**
Mechanical refactor. Rollback: revert wiring commit.
---
# 7. Deliverables
Code: `modules/runtime/runner.py`, wiring in `process_images`
Tests: `test/quality/test_processing_runner.py`
Docs: M10_toolcalls.md, M10_run1.md, M10_summary.md, M10_audit.md
Ledger: Update `docs/serena.md`
Tag: `v0.0.10-m10`

View file

@ -0,0 +1,11 @@
# M10 Toolcalls — ProcessingRunner Skeleton
Implementation toolcalls for Cursor execution.
| Timestamp | Tool | Purpose | Files/Target | Status |
|-----------|------|---------|--------------|--------|
| 2026-03-11 | write | Create M10_plan.md, M10_toolcalls.md | docs/milestones/M10/ | done |
| 2026-03-11 | write | Create modules/runtime/runner.py | modules/runtime/ | done |
| 2026-03-11 | search_replace | Wire process_images to delegate through runner | modules/processing.py | done |
| 2026-03-11 | write | Add contract test for ProcessingRunner | test/quality/test_processing_runner.py | done |
| 2026-03-11 | run | Create branch m10-processing-runner | git | in_progress |

View file

@ -844,7 +844,10 @@ def process_images(p: StableDiffusionProcessing) -> Processed:
sd_samplers.fix_p_invalid_sampler_and_scheduler(p)
with profiling.Profiler():
res = process_images_inner(p)
from modules.runtime.runner import ProcessingRunner, ProcessingRequest
runner = ProcessingRunner()
request = ProcessingRequest(p)
res = runner.run(request)
finally:
sd_models.apply_token_merging(p.sd_model, 0)

View file

@ -0,0 +1,4 @@
"""Runtime execution boundary for Serena.
M10: ProcessingRunner skeleton. Thin adapter around process_images_inner.
"""

22
modules/runtime/runner.py Normal file
View file

@ -0,0 +1,22 @@
"""ProcessingRunner — unified execution entrypoint for Serena pipeline.
M10: Thin adapter around process_images_inner. No behavior changes.
"""
class ProcessingRequest:
"""Wraps StableDiffusionProcessing for runner boundary."""
def __init__(self, processing):
self.processing = processing
class ProcessingRunner:
"""
Unified execution entrypoint for Serena processing pipeline.
"""
def run(self, request):
"""Execute processing pipeline."""
from modules.processing import process_images_inner
return process_images_inner(request.processing)

View file

@ -0,0 +1,26 @@
"""Contract tests for ProcessingRunner (M10 runner skeleton)."""
import modules.processing
from modules.runtime.runner import ProcessingRunner, ProcessingRequest
def test_processing_runner_delegates(monkeypatch):
"""ProcessingRunner.run delegates to process_images_inner."""
called = {}
def fake_process_images_inner(p):
called["ok"] = True
return "result"
monkeypatch.setattr(
modules.processing,
"process_images_inner",
fake_process_images_inner,
)
runner = ProcessingRunner()
request = ProcessingRequest(processing="dummy")
result = runner.run(request)
assert called["ok"]
assert result == "result"