diff --git a/docs/milestones/M10/M10_closeout_prompt.md b/docs/milestones/M10/M10_closeout_prompt.md new file mode 100644 index 000000000..ac60f76e3 --- /dev/null +++ b/docs/milestones/M10/M10_closeout_prompt.md @@ -0,0 +1,44 @@ +# M10 Closeout Prompt for Cursor + +**Use this once PR CI is green against m-cahill/serena.** + +--- + +``` +M10 PR CI is complete. + +Proceed with M10 closeout. + +Steps: + +1. Verify CI status + - Linter ✓ + - Smoke Tests ✓ + +2. Merge PR into main. + +3. Monitor post-merge CI: + - Quality Tests must pass. + +4. Generate milestone artifacts: + +docs/milestones/M10/M10_run2.md +docs/milestones/M10/M10_summary.md +docs/milestones/M10/M10_audit.md + +5. Update ledger: + +docs/serena.md + +Add row: + +| M10 | ProcessingRunner skeleton | Closed | m10-processing-runner | PR # | | | 5.0 | + +6. Create tag: + +v0.0.10-m10 + +7. Push tag. + +Follow the RefactorWorkflowPrompt and RefactorSummaryPrompt formats. +``` diff --git a/docs/milestones/M10/M10_plan.md b/docs/milestones/M10/M10_plan.md new file mode 100644 index 000000000..7551cbb56 --- /dev/null +++ b/docs/milestones/M10/M10_plan.md @@ -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` diff --git a/docs/milestones/M10/M10_run1.md b/docs/milestones/M10/M10_run1.md new file mode 100644 index 000000000..fcf4df8ca --- /dev/null +++ b/docs/milestones/M10/M10_run1.md @@ -0,0 +1,138 @@ +# M10 CI Run 1 — ProcessingRunner Skeleton + +**Date:** 2026-03-11 +**Branch:** m10-processing-runner +**PR:** (verify target repo — gh may have created against upstream) +**Trigger:** pull_request (PR to main) +**Commit:** 59e46fa0 + +--- + +## 1. Workflow Identity + +| Workflow | Run ID | Trigger | Branch | Commit | Status | +|----------|--------|---------|--------|--------|--------| +| Linter | (pending) | pull_request | m10-processing-runner | 59e46fa0 | — | +| Smoke Tests | (pending) | pull_request | m10-processing-runner | 59e46fa0 | — | +| Quality Tests | (post-merge) | push | main | — | — | + +**Note:** PR created via `gh pr create`. Monitor CI at GitHub Actions. Quality Tests run on push to main after merge. + +--- + +## 2. Change Context + +| Item | Value | +|------|-------| +| Milestone | M10 — ProcessingRunner Skeleton | +| Phase | Phase III — Runner & Service Boundary | +| Posture | Behavior-preserving | +| Refactor target | `modules/runtime/runner.py` (new), `modules/processing.py` (delegation) | +| Run type | First CI verification of M10 implementation | + +--- + +## 3. Step 1 — Workflow Inventory + +(To be populated after CI run completes.) + +### Linter + +| Job | Required? | Purpose | Pass/Fail | +|-----|-----------|---------|-----------| +| ruff | Yes | Python lint | — | +| eslint | Yes | JS lint | — | + +### Smoke Tests + +| Job / Step | Required? | Purpose | Pass/Fail | +|------------|-----------|---------|-----------| +| Run smoke tests | Yes | pytest test/smoke | — | + +### Quality Tests (post-merge) + +| Job / Step | Required? | Purpose | Pass/Fail | +|------------|-----------|---------|-----------| +| Run quality tests | Yes | pytest test/quality, coverage ≥40% | — | + +--- + +## 4. Step 2 — Refactor Signal Integrity + +### A) Tests + +- **Tier:** Smoke + Quality (new contract test: `test_processing_runner_delegates`) +- **Coverage of refactor target:** Smoke tests exercise txt2img/img2img API → `process_images()` → runner → `process_images_inner()`. Contract test verifies runner delegates correctly. +- **Failures:** (to be filled after CI) +- **Golden/snapshot:** Behavior-preserving; no output changes. + +### B) Coverage + +- Quality tier enforces ≥40%. New test adds minimal coverage for runner module. + +### C) Static Gates + +- Ruff, eslint: (to be filled after CI) + +--- + +## 5. Step 3 — Delta Analysis + +### Change Inventory + +| File | Change | +|------|--------| +| modules/runtime/__init__.py | **New:** Package init | +| modules/runtime/runner.py | **New:** ProcessingRunner, ProcessingRequest | +| modules/processing.py | Delegate to runner inside process_images | +| test/quality/test_processing_runner.py | **New:** Contract test | +| docs/milestones/M10/* | Plan, toolcalls | + +**Call graph (unchanged from caller perspective):** + +``` +UI/API/scripts + │ + ▼ +process_images(p) + │ + ▼ +ProcessingRunner.run(request) + │ + ▼ +process_images_inner(p) +``` + +--- + +## 6. Step 4 — Invariant Verification + +| Invariant | Verification | Status | +|-----------|--------------|--------| +| CLI behavior | No CLI changes | ✓ | +| API responses | Same path; smoke tests | — | +| Processing results | Byte-identical (runner is thin adapter) | — | +| Runtime state | No new side effects | ✓ | +| CI coverage | ≥40% (Quality gate) | — | + +--- + +## 7. Blast Radius + +**Files changed:** +- `modules/runtime/` (new) +- `modules/processing.py` (modified) +- `test/quality/test_processing_runner.py` (new) +- `docs/milestones/M10/*` + +**Zero blast radius to callers.** All UI, API, scripts call `process_images(p)` unchanged. + +--- + +## 8. Verdict + +**CI Status:** (pending — monitor GitHub Actions) + +**Refactor posture:** Behavior-preserving. First Phase III execution boundary. Runner is thin adapter; no behavior change. + +**Next step:** Monitor CI. After green: await merge permission. Post-merge: Quality Tests, then audit/summary/ledger/tag per governance. diff --git a/docs/milestones/M10/M10_toolcalls.md b/docs/milestones/M10/M10_toolcalls.md new file mode 100644 index 000000000..cec5f5e2a --- /dev/null +++ b/docs/milestones/M10/M10_toolcalls.md @@ -0,0 +1,12 @@ +# 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 | done | +| 2026-03-11 | run | git add, commit, push, gh pr create | git, gh | done | diff --git a/docs/serena.md b/docs/serena.md index 21fb0f2be..a559f3331 100644 --- a/docs/serena.md +++ b/docs/serena.md @@ -80,48 +80,49 @@ Core principles: | M08 | process_images_inner snapshot threading | | M09 | Execution context/state seam | -### Phase III — Runner & Service Boundary (M10–M14) +### Phase III — Runner & Service Boundary (M10–M15) | Milestone | Title | |-----------|-------| | M10 | ProcessingRunner skeleton | -| M11 | txt2img via runner | -| M12 | img2img via runner | -| M13 | API adoption of runner | -| M14 | UI adoption of runner | +| M11 | Runner lifecycle surface (prepare / execute / finalize) | +| M12 | Runtime instrumentation hooks | +| M13 | txt2img path through runner | +| M14 | API integration | +| M15 | background/queue runner preparation | -### Phase IV — Runtime Extraction (M15–M19) +### Phase IV — Runtime Extraction (M16–M20) | Milestone | Title | |-----------|-------| -| M15 | Runtime module extraction | -| M16 | Sampler runner extraction | -| M17 | Decode/save separation | -| M18 | Model provider interface | -| M19 | Runtime tests with mockable boundaries | +| M16 | Runtime module extraction | +| M17 | Sampler runner extraction | +| M18 | Decode/save separation | +| M19 | Model provider interface | +| M20 | Runtime tests with mockable boundaries | -### Phase V — UI & Extension Stabilization (M20–M24) +### Phase V — UI & Extension Stabilization (M21–M25) | Milestone | Title | |-----------|-------| -| M20 | UI tab registry | -| M21 | txt2img/img2img tab modularization | -| M22 | Settings/extensions modularization | -| M23 | Extension API version/contract | -| M24 | Deprecation/compatibility scaffolding | +| M21 | UI tab registry | +| M22 | txt2img/img2img tab modularization | +| M23 | Settings/extensions modularization | +| M24 | Extension API version/contract | +| M25 | Deprecation/compatibility scaffolding | -### Phase VI — Hardening & Reproducibility (M25–M29) +### Phase VI — Hardening & Reproducibility (M26–M30) | Milestone | Title | |-----------|-------| -| M25 | Locked manifests / npm ci / CI env stabilization | -| M26 | Coverage and complexity gates | -| M27 | Security/supply-chain evidence | -| M28 | Health/perf verification | -| M29 | QA/evidence publishing | +| M26 | Locked manifests / npm ci / CI env stabilization | +| M27 | Coverage and complexity gates | +| M28 | Security/supply-chain evidence | +| M29 | Health/perf verification | +| M30 | QA/evidence publishing | -### Phase VII — Release Lock / 5.0 Closure (M30–M32) +### Phase VII — Release Lock / 5.0 Closure (M31–M33) | Milestone | Title | |-----------|-------| -| M30 | Architecture lock | -| M31 | Evidence/audit closure | -| M32 | Release-ready 5/5 close | +| M31 | Architecture lock | +| M32 | Evidence/audit closure | +| M33 | Release-ready 5/5 close | --- diff --git a/modules/processing.py b/modules/processing.py index 6f3d87b30..b8feb730a 100644 --- a/modules/processing.py +++ b/modules/processing.py @@ -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) diff --git a/modules/runtime/__init__.py b/modules/runtime/__init__.py new file mode 100644 index 000000000..590d3ad53 --- /dev/null +++ b/modules/runtime/__init__.py @@ -0,0 +1,4 @@ +"""Runtime execution boundary for Serena. + +M10: ProcessingRunner skeleton. Thin adapter around process_images_inner. +""" diff --git a/modules/runtime/runner.py b/modules/runtime/runner.py new file mode 100644 index 000000000..96c38b53e --- /dev/null +++ b/modules/runtime/runner.py @@ -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) diff --git a/test/quality/test_processing_runner.py b/test/quality/test_processing_runner.py new file mode 100644 index 000000000..3aae3f0ff --- /dev/null +++ b/test/quality/test_processing_runner.py @@ -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"