|
| 1 | +# Slice 13 Plan: Local Engine Execution |
| 2 | + |
| 3 | +Status: planned. |
| 4 | +Date: 2026-07-02. |
| 5 | + |
| 6 | +## Intent |
| 7 | + |
| 8 | +Complete the middle of the local launch pipeline: |
| 9 | + |
| 10 | +```python |
| 11 | +prepared = app.workflows.local_runs.prepare_next() |
| 12 | +engine = app.workflows.local_engine.execute(run_id, harness) |
| 13 | +delivery = app.workflows.local_delivery.deliver(run_id) |
| 14 | +``` |
| 15 | + |
| 16 | +Slice 13 does not expose a public CLI command. It adds the backend workflow that |
| 17 | +cloud and local orchestration can call without going through human CLI strings. |
| 18 | + |
| 19 | +## Product Contract |
| 20 | + |
| 21 | +- The worker receives sources by reference through `sources_path`. |
| 22 | +- The model runs inside the prepared worker repo, not the user's active checkout. |
| 23 | +- The model may edit only the configured Almanac root. |
| 24 | +- The engine writes `~/.codealmanac/runs/<run-id>/result.json`. |
| 25 | +- The engine records normalized `run_events` in `~/.codealmanac/control.sqlite`. |
| 26 | +- The engine does not commit, merge, open PRs, or deliver changes. |
| 27 | +- Delivery remains deterministic and separate. |
| 28 | + |
| 29 | +## Code Shape |
| 30 | + |
| 31 | +```python |
| 32 | +result = app.workflows.local_engine.execute( |
| 33 | + ExecuteLocalEngineRunRequest(run_id=run.id, harness=HarnessKind.CODEX) |
| 34 | +) |
| 35 | +``` |
| 36 | + |
| 37 | +Ownership: |
| 38 | + |
| 39 | +- `workflows/local_engine/` owns orchestration for executing a prepared engine |
| 40 | + request. |
| 41 | +- `services/engine_runs/` continues to own request/result artifacts. |
| 42 | +- `services/harnesses/` continues to own provider-neutral model execution. |
| 43 | +- `workflows/local_delivery/` remains the only local writer to the real |
| 44 | + checkout. |
| 45 | + |
| 46 | +## Implementation Scope |
| 47 | + |
| 48 | +Add: |
| 49 | + |
| 50 | +- `src/codealmanac/workflows/local_engine/` |
| 51 | + - `requests.py` |
| 52 | + - `models.py` |
| 53 | + - `prompt.py` |
| 54 | + - `service.py` |
| 55 | + - `__init__.py` |
| 56 | +- `src/codealmanac/prompts/operations/update.md` |
| 57 | +- `PromptName.OPERATION_UPDATE` |
| 58 | +- `app.workflows.local_engine` |
| 59 | +- tests for success, failure, and event/result recording |
| 60 | + |
| 61 | +The workflow should: |
| 62 | + |
| 63 | +1. Read the control run and prepared `EngineRunRequest`. |
| 64 | +2. Mark the control run `running`. |
| 65 | +3. Render an update prompt with the request JSON as runtime context. |
| 66 | +4. Run the requested harness with `cwd=request.repo_path`. |
| 67 | +5. Append normalized harness events to `run_events`. |
| 68 | +6. Write an `EngineRunResult`. |
| 69 | +7. Store `result_ref` on the control run. |
| 70 | +8. On harness failure, write a failed engine result, mark the run `failed`, and |
| 71 | + append an error event. |
| 72 | + |
| 73 | +## Out Of Scope |
| 74 | + |
| 75 | +- Public CLI command surface. |
| 76 | +- Cloud worker execution. |
| 77 | +- WorkOS/API/auth changes. |
| 78 | +- Delivery changes. |
| 79 | +- Parsing a commit subject out of model prose. |
| 80 | + |
| 81 | +## Verification |
| 82 | + |
| 83 | +Focused: |
| 84 | + |
| 85 | +```bash |
| 86 | +uv run pytest tests/test_local_engine_workflow.py tests/test_engine_runs_service.py tests/test_architecture.py |
| 87 | +``` |
| 88 | + |
| 89 | +Full gate: |
| 90 | + |
| 91 | +```bash |
| 92 | +uv run pytest |
| 93 | +uv run ruff check . |
| 94 | +git diff --check |
| 95 | +``` |
| 96 | + |
| 97 | +## Architecture Notes |
| 98 | + |
| 99 | +The workflow can deterministically derive a commit subject from the harness |
| 100 | +summary: |
| 101 | + |
| 102 | +```text |
| 103 | +docs almanac: <summary> |
| 104 | +``` |
| 105 | + |
| 106 | +If no usable summary exists, use: |
| 107 | + |
| 108 | +```text |
| 109 | +docs almanac: update wiki |
| 110 | +``` |
| 111 | + |
| 112 | +The prompt should still instruct the model to produce a concise summary, but |
| 113 | +the Python code should not scrape durable fields out of unstructured final |
| 114 | +assistant text. |
0 commit comments