M13: txt2img path through runner (#31)

* M13: txt2img path through runner — contract test and verification

- Add test_txt2img_runner_contract.py verifying txt2img invokes ProcessingRunner
- No routing changes: process_images already delegates to runner (M10)
- Update M13_plan, M13_toolcalls, M13_run1
- Scope: UI txt2img only; API/img2img in M14/M15

Made-with: Cursor

* docs(M13): add CI run1 results — smoke tests pass

Made-with: Cursor
This commit is contained in:
m-cahill 2026-03-13 15:17:06 -07:00 committed by GitHub
parent 40b4aebb07
commit 4dd0499987
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 321 additions and 0 deletions

View file

@ -0,0 +1,150 @@
# M13 — txt2img Execution via Runner
Phase: Phase III — Runner & Service Boundary
Status: Planned
---
# 1. Intent / Target
Route the **txt2img execution path** through `ProcessingRunner` explicitly.
Right now the runner exists but is still only used by the internal `process_images` wrapper. M13 makes the runner the **true execution surface** for txt2img while preserving behavior.
### Architectural impact
Before:
```
UI/API
process_images
runner
```
After:
```
UI/API
Runner
Pipeline
```
This begins the **UI/runtime separation** that Serena is aiming for.
---
# 2. Scope Boundaries
## In scope
• Route txt2img path via runner
• Preserve existing request objects
• No UI or API behavior changes
## Out of scope
• API runner
• queue runner
• cancellation
• progress reporting
---
# 3. Invariants
| Surface | Requirement | Verification |
|---------------|---------------|--------------|
| CLI behavior | unchanged | smoke tests |
| API responses | unchanged | smoke tests |
| Output images | identical | quality tests|
| Extensions | unaffected | extension tests |
---
# 4. Verification Plan
CI must remain green.
Expected checks:
| Check | Expected |
|-------|----------|
| Linter | pass |
| Smoke Tests | pass |
| Quality Tests | pass (post-merge) |
| Coverage | ≥ 40% |
---
# 5. Implementation Steps
1. **Verify routing** — txt2img path calls `process_images` only; no direct `process_images_inner` in `modules/txt2img.py`. ✓
2. **Confirm delegation**`process_images` delegates to `ProcessingRunner().run(ProcessingRequest(p))` inside profiler block. ✓
3. **Add contract test**`test/quality/test_txt2img_runner_contract.py` verifies txt2img path invokes runner. ✓
4. **No routing changes required** — Runner already sits behind `process_images` (M10M12). Verification milestone.
Key principle: txt2img call path must flow through `ProcessingRunner.run()` while preserving identical behavior.
---
# 6. Risk & Rollback Plan
Risk level: **Low** (routing verification milestone; runner already used by process_images)
Rollback: revert txt2img routing commit; restore direct process_images path.
---
# 7. Deliverables
Code:
```
modules/runtime/runner.py
modules/processing.py
```
Tests:
```
test/quality/test_processing_runner.py
test/quality/test_txt2img_runner_contract.py # M13 contract test
```
Docs:
```
docs/milestones/M13/M13_plan.md
docs/milestones/M13/M13_toolcalls.md
docs/milestones/M13/M13_run1.md
docs/milestones/M13/M13_summary.md
docs/milestones/M13/M13_audit.md
```
Ledger:
```
docs/serena.md
```
Tag:
```
v0.0.13-m13
```
---
# 8. Exit Criteria
M13 closes when:
• PR CI passes
• post-merge Quality Tests pass
• txt2img routed through runner merged
• ledger updated
• tag created

View file

@ -0,0 +1,104 @@
# M13 Run 1 — CI Analysis
**Milestone:** M13 — txt2img execution via runner
**Branch:** m13-txt2img-runner
**PR:** [#31](https://github.com/m-cahill/serena/pull/31)
**Baseline:** v0.0.12-m12 (46cf6d1c)
---
## 0. Workflow Run — Actual Results
| Item | Value |
|------|-------|
| **Workflow** | Smoke Tests |
| **Run ID** | [23038170275](https://github.com/m-cahill/serena/actions/runs/23038170275) |
| **Trigger** | pull_request (#31) |
| **Branch** | m13-txt2img-runner |
| **Commit** | 142f0bbe |
| **Status** | ✓ completed |
| **Conclusion** | ✓ success |
| **Duration** | 2m 45s |
### Job: smoke tests
| Step | Result |
|------|--------|
| Verify repository | ✓ |
| Verify base branch | ✓ |
| Checkout Code | ✓ |
| Set up Python 3.10 | ✓ |
| Cache models | ✓ |
| Install test dependencies | ✓ |
| Install runtime dependencies | ✓ |
| Create stub repositories | ✓ |
| Setup environment | ✓ |
| Smoke startup | ✓ |
| Start test server | ✓ |
| **Run smoke tests** | ✓ |
| Kill test server | ✓ |
| Upload main app output | ✓ |
**Annotation:** Node.js 20 actions deprecation warning (informational; not merge-blocking).
---
## 1. Implementation Summary
M13 was a **verification milestone**. No routing changes were required because:
- `process_images` already delegates to `ProcessingRunner().run(ProcessingRequest(p))` (M10)
- `modules/txt2img.py` calls `process_images(p)` only (lines 83, 109) — no direct `process_images_inner` calls
- txt2img → process_images → runner → process_images_inner flow was already in place
### Changes Made
| File | Change |
|------|--------|
| `test/quality/test_txt2img_runner_contract.py` | New contract test verifying txt2img path invokes runner |
| `docs/milestones/M13/M13_plan.md` | Updated implementation steps, risk level, deliverables |
| `docs/milestones/M13/M13_toolcalls.md` | Tool call log |
### Code Diff Size
~50 lines (new test file + doc updates). No changes to `modules/txt2img.py`, `modules/processing.py`, or `modules/runtime/runner.py`.
---
## 2. CI Results
| Workflow | Trigger | Expected | Actual |
|----------|---------|----------|--------|
| Smoke Tests | PR | ✓ pass | ✓ pass (2m 45s) |
| Quality Tests | Push to main | ✓ pass | Pending (post-merge) |
| Coverage | Push to main | ≥ 40% | Pending (post-merge) |
---
## 3. Refactor Target
- **Surface:** txt2img UI path → process_images → ProcessingRunner
- **Posture:** Behavior-preserving
- **Verification:** Contract test locks in runner invocation
---
## 4. Architectural Sign
That routing required no changes is a **positive signal**: the runner seam introduced in M10M12 is correctly positioned. All consumers (txt2img, img2img, API) that call `process_images` already flow through the runner.
---
## 5. Conclusion
**Run 1 status: ✓ GREEN.** Smoke tests passed. PR #31 is ready for merge (with permission).
---
## 6. Next Steps
1. ~~Await PR smoke CI pass~~ ✓ Done
2. Merge PR (with permission)
3. Verify quality tests pass on main (post-merge)
4. Update ledger, create tag v0.0.13-m13
5. Proceed to M14 (API integration)

View file

@ -0,0 +1,12 @@
# M13 Toolcalls — txt2img Execution via Runner
Implementation toolcalls for Cursor execution.
| Timestamp | Tool | Purpose | Files/Target | Status |
|-----------|------|---------|--------------|--------|
| 2026-03-12 | read | Verify baseline tag, branch creation | git, conftest | Done |
| 2026-03-12 | read | Verify txt2img routing, process_images delegation | txt2img.py, processing.py | Done |
| 2026-03-12 | write | Add contract test | test/quality/test_txt2img_runner_contract.py | Done |
| 2026-03-12 | search_replace | Update M13_plan implementation steps, risk, deliverables | docs/milestones/M13/M13_plan.md | Done |
| 2026-03-12 | write | M13_run1 CI analysis | docs/milestones/M13/M13_run1.md | Done |
| 2026-03-12 | run | Push branch, create PR | git push, gh pr | Done (PR link below) |

View file

@ -0,0 +1,55 @@
"""M13 contract test: txt2img path uses ProcessingRunner.
Verifies that the txt2img execution path flows through process_images runner,
not direct process_images_inner calls.
"""
from modules.runtime.runner import ProcessingRunner
def test_txt2img_path_uses_runner(monkeypatch, initialize):
"""txt2img path invokes ProcessingRunner when process_images is called."""
from modules.processing import (
StableDiffusionProcessingTxt2Img,
process_images,
Processed,
)
calls = []
class TestRunner(ProcessingRunner):
def execute(self, state):
calls.append("runner_execute")
return super().execute(state)
monkeypatch.setattr(
"modules.runtime.runner.ProcessingRunner",
TestRunner,
)
# Minimal processing object matching txt2img path
p = StableDiffusionProcessingTxt2Img(
sd_model=None,
prompt="test",
override_settings={},
steps=1,
width=64,
height=64,
extra_generation_params={},
)
p.scripts = None
p.comments = []
# Mock process_images_inner to avoid full pipeline (model, device, etc.)
def fake_inner(proc):
return Processed(proc, [], seed=-1, info="", comments="")
import modules.processing as proc_mod
monkeypatch.setattr(proc_mod, "process_images_inner", fake_inner)
# Mock model reload to avoid loading weights
import modules.sd_models as sd_models_mod
monkeypatch.setattr(sd_models_mod, "reload_model_weights", lambda: None)
process_images(p)
assert "runner_execute" in calls