Merge pull request #30 from m-cahill/m11-runner-lifecycle

M11: Runner lifecycle surface
This commit is contained in:
m-cahill 2026-03-11 23:45:13 -07:00 committed by GitHub
commit 08ac1c0e24
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 245 additions and 32 deletions

View file

@ -1,45 +1,48 @@
# M11 — Runner Lifecycle Surface
Phase: **Phase III — Runner & Service Boundary**
Status: Planned
Status: In Progress
---
# 1. Intent / Target
Introduce lifecycle structure on ProcessingRunner:
Introduce a **lifecycle structure** for the ProcessingRunner.
The runner currently exposes a single method: `ProcessingRunner.run(request)`.
This milestone refactors the internal implementation into three lifecycle stages:
```
runner.prepare()
runner.execute()
runner.finalize()
prepare → execute → finalize
```
This enables:
* cancellation
* instrumentation
* progress reporting
* distributed execution (later milestones)
This creates a **stable execution surface** that later milestones can instrument
(progress tracking, cancellation, API runners, queue workers).
Behavior-preserving. No runtime changes yet.
Behavior must remain identical.
---
# 2. Scope Boundaries
### In scope
## In scope
* Add prepare / execute / finalize structure to ProcessingRunner
* Route existing run() logic through execute()
* Maintain identical behavior
* Add minimal contract tests
* Add lifecycle methods to ProcessingRunner
* Refactor run() to delegate to lifecycle stages
* Add minimal lifecycle contract test
* Preserve all current execution behavior
### Out of scope
## Out of scope
* No cancellation implementation yet
* No async
* No instrumentation hooks (M12)
* No txt2img path through runner (M13)
* No API changes
* No CLI changes
* No runtime behavior changes
* No async / threading
* No cancellation yet
* No instrumentation yet
Those come in later Phase III milestones.
---
@ -47,17 +50,125 @@ Behavior-preserving. No runtime changes yet.
| Surface | Invariant | Verification |
|---------|-----------|--------------|
| CLI behavior | Identical | smoke tests |
| API responses | Unchanged | tests |
| Processing results | Byte-identical | quality tests |
| CI coverage | ≥ 40% | CI gate |
| CLI behavior | identical outputs | smoke tests |
| API responses | unchanged schemas | smoke tests |
| Processing results | identical images / metadata | golden outputs |
| Runtime context | still created inside process_images_inner | code review |
| Coverage | ≥ 40% | CI gate |
---
# 4. Deliverables
# 4. Verification Plan
CI must remain green.
Expected CI checks:
| Check | Expected |
|-------|----------|
| Linter | pass |
| Smoke Tests | pass |
| Quality Tests | pass (post-merge) |
| Coverage | ≥ 40% |
Manual verification: `pytest`
Runner contract tests must pass.
---
# 5. Implementation Steps
## Step 1 — Extend ProcessingRunner
File: `modules/runtime/runner.py`
Refactor runner:
```python
class ProcessingRunner:
def run(self, request):
state = self.prepare(request)
result = self.execute(state)
return self.finalize(state, result)
def prepare(self, request):
return request
def execute(self, state):
from modules.processing import process_images_inner
return process_images_inner(state.processing)
def finalize(self, state, result):
return result
```
Important: prepare/execute/finalize must remain **pass-through behavior**.
## Step 2 — Preserve delegation
Call graph must remain:
```
process_images(p)
ProcessingRunner.run(request)
prepare
execute
finalize
process_images_inner(p)
```
## Step 3 — Update contract tests
Add lifecycle verification.
File: `test/quality/test_processing_runner.py`
Add test verifying lifecycle order. Keep existing `test_processing_runner_delegates`.
---
# 6. Risk & Rollback Plan
Risk level: **Low**
Changes are mechanical and internal.
Rollback: revert runner lifecycle commit, restore single run(), re-run CI.
No runtime data or external API surfaces change.
---
# 7. Deliverables
Code: `modules/runtime/runner.py` lifecycle implementation
Tests: updated `test_processing_runner.py`
Code: `modules/runtime/runner.py` (lifecycle methods)
Tests: `test/quality/test_processing_runner.py` (lifecycle contract)
Docs: M11_plan.md, M11_toolcalls.md, M11_run1.md, M11_summary.md, M11_audit.md
Ledger: Update `docs/serena.md`
Ledger update: `docs/serena.md`
Tag: `v0.0.11-m11`
---
# 8. Exit Criteria
M11 closes when:
* PR CI passes
* post-merge Quality Tests pass
* lifecycle runner merged
* ledger updated
* tag created

View file

@ -0,0 +1,56 @@
# M11 CI Run 1 — Runner Lifecycle Surface
**Date:** 2026-03-12
**Branch:** m11-runner-lifecycle
**PR:** [#30](https://github.com/m-cahill/serena/pull/30)
**Trigger:** push, pull_request
**Commit:** fb705fe6 (M11 impl), ce2659f1 (trigger CI)
---
## 1. Workflow Identity
| Workflow | Run ID | Trigger | Branch | Status |
|----------|--------|---------|--------|--------|
| Linter | 22989791480, 22989833120 | push | m11-runner-lifecycle | ✓ success |
| Smoke Tests | — | pull_request | m11-runner-lifecycle | not triggered |
**Quality Tests:** Post-merge only (runs on push to main).
**Note:** Smoke Tests did not run for this PR (may be branch-protection or workflow config). Linter (ruff + eslint) passed.
---
## 2. Workflow Inventory
### Linter (22989833120)
| Job | Required? | Purpose | Pass/Fail | Notes |
|-----|-----------|---------|-----------|-------|
| ruff | Yes | Python lint | ✓ | 7s |
| eslint | Yes | JS lint | ✓ | 13s |
---
## 3. Refactor Signal Integrity
### A) Tests
- **Tier:** Smoke (not run for this PR), Quality (post-merge)
- **Coverage of refactor target:** `test_runner_lifecycle_order` verifies prepare → execute → finalize order. `test_processing_runner_delegates` verifies run still delegates to process_images_inner.
- **Expected:** Both pass. No behavior change.
### B) Change Inventory
| File | Change |
|------|--------|
| modules/runtime/runner.py | Lifecycle: prepare, execute, finalize; run() delegates |
| test/quality/test_processing_runner.py | Add test_runner_lifecycle_order |
---
## 4. Verdict
**Verdict:** Linter passed. Smoke Tests did not run for this PR (may require manual trigger or merge). Implementation is mechanical and behavior-preserving; Quality Tests will run post-merge.
**Recommended:** Merge PR; verify Quality Tests pass post-merge. If Smoke Tests are required for merge, consider re-running workflows or checking branch protection.

View file

@ -4,3 +4,10 @@ Implementation toolcalls for Cursor execution.
| Timestamp | Tool | Purpose | Files/Target | Status |
|-----------|------|---------|--------------|--------|
| 2026-03-11 | run | Create branch m11-runner-lifecycle | git | done |
| 2026-03-11 | search_replace | Implement lifecycle in runner.py | modules/runtime/runner.py | done |
| 2026-03-11 | search_replace | Add test_runner_lifecycle_order | test/quality/test_processing_runner.py | done |
| 2026-03-11 | search_replace | Update M11_plan.md with full details | docs/milestones/M11/M11_plan.md | done |
| 2026-03-11 | run | Commit and push M11 implementation | git | done |
| 2026-03-11 | run | Create PR #30 | gh pr create | done |
| 2026-03-12 | search_replace | Update M11_run1.md with CI results | docs/milestones/M11/M11_run1.md | done |

View file

@ -1,6 +1,7 @@
"""ProcessingRunner — unified execution entrypoint for Serena pipeline.
M10: Thin adapter around process_images_inner. No behavior changes.
M11: Lifecycle surface (prepare execute finalize). Pass-through behavior.
"""
@ -14,9 +15,24 @@ class ProcessingRequest:
class ProcessingRunner:
"""
Unified execution entrypoint for Serena processing pipeline.
M11: Exposes lifecycle stages for future instrumentation.
"""
def run(self, request):
"""Execute processing pipeline."""
"""Execute processing pipeline via lifecycle stages."""
state = self.prepare(request)
result = self.execute(state)
return self.finalize(state, result)
def prepare(self, request):
"""Lifecycle stage 1: prepare request. Pass-through in M11."""
return request
def execute(self, state):
"""Lifecycle stage 2: run processing. Delegates to process_images_inner."""
from modules.processing import process_images_inner
return process_images_inner(request.processing)
return process_images_inner(state.processing)
def finalize(self, state, result):
"""Lifecycle stage 3: finalize. Pass-through in M11."""
return result

View file

@ -1,7 +1,30 @@
"""Contract tests for ProcessingRunner (M10 runner skeleton)."""
"""Contract tests for ProcessingRunner (M10 runner skeleton, M11 lifecycle)."""
from modules.runtime.runner import ProcessingRunner, ProcessingRequest
def test_runner_lifecycle_order(monkeypatch, initialize):
"""ProcessingRunner invokes prepare → execute → finalize in order."""
calls = []
class TestRunner(ProcessingRunner):
def prepare(self, request):
calls.append("prepare")
return request
def execute(self, state):
calls.append("execute")
return "result"
def finalize(self, state, result):
calls.append("finalize")
return result
runner = TestRunner()
runner.run(ProcessingRequest(processing="dummy"))
assert calls == ["prepare", "execute", "finalize"]
def test_processing_runner_delegates(monkeypatch, initialize):
"""ProcessingRunner.run delegates to process_images_inner."""
import modules.processing