Tasks: Support embedded Temporal mode
The last spec artifact (requirements → design → testing plan → tasks). A DAG of implementation tasks derived from the approved design and testing plan. It has no approval gate of its own (issue-281).
Nine tasks, delivered on the work item's one pull request, #18, in this repository. No other repository is touched. Every task names the requirement it satisfies, the testing-plan row that proves it, and the test that goes red first.
Conventions for every task:
- pyright strict, zero errors, no
Any. - Integration tests carry the Gherkin docstring with
Requirement: docs/specs/issue-17/requirements.md#R<n>(the repository's convention). - Embedded tests run with
TEMPORAL_API_KEYunset. - The commit message records the test command and its red→green transition.
Task list
[x] 1. Config:
mode,[temporal.embedded]and per-mode validation- Add
EmbeddedTemporalConfigand the newTemporalConfigfields (mode, optionaladdress/namespace/api_key,embedded), themodel_validatorenforcing the per-mode table (usingmodel_fields_setfortls),effective_namespaceanddatabase_path(store), and thebinary_pathexecutable check. - In
load_settings: validate the rawtemporal.modefirst, then requireTEMPORAL_API_KEYin remote mode, refuse it in embedded mode, and skip it otherwise. runtime.secret_valuesskips aNonekey.connect()asserts remote mode, so it never runs withapi_key=None.- Depends on: none
- Requirements: R1.1–R1.7, R3.1, R3.2, R3.4, R4.1, R4.3
- Test: T1, T8 (abuse cases 2, 3, 4), T10 —
uv run pytest tests/unit/test_config.py(new cases per row of design § the per-mode table; the existing demo config still loads as remote and still needs the key) (red→green)
- Add
[x] 2.
EmbeddedTemporalandtemporal_client(service/durable/temporal.py)- Implement the state lock (
fcntl.flockon<db>.lock; skipped with a WARNING wherefcntlis absent),0600database and lock creation and tightening, and the private0700download dir ($XDG_CACHE_HOME/~/.cachefallback). - Call
start_localwith the design's arguments, using the same converter and interceptor asconnect(), withip="127.0.0.1"as a literal. - Log the INFO and WARNING lines and the download notice. Wrap failures in
EmbeddedTemporalErrorand release the lock.__aexit__shuts down and releases. temporal_client(settings)dispatches on the mode.- Depends on: 1
- Requirements: R2.1–R2.5, R3.1, R3.4, R4.2, R4.4
- Test: T1, T8 (abuse cases 5, 7, 8; temp-dir plant) —
uv run pytest tests/unit/test_embedded_temporal.pywithstart_localstubbed (red→green)
- Implement the state lock (
[x] 3. Integration: the real dev server
- Add
tests/integration/embedded/with a shared fixture (atmp_pathstate dir anddownload_dir= the suite's~/.cache/temporalio). - Scenarios: Embedded mode starts without Temporal credentials, Embedded server binds loopback only, Embedded server stops when the harness exits (normal, exception, cancellation; checks the child pid is gone), Embedded state survives a restart (start a task workflow, stop, restart on the same database, the workflow is still running), A second process cannot open the same embedded state.
- The
startup_timemeasurement (5 cached starts, timings printed). - Depends on: 2
- Requirements: R1.4, R2.1–R2.3, R3.3; design § lock
- Test: T2, T7, T8 (abuse cases 1, 6; two-process lock) —
env -u TEMPORAL_API_KEY uv run pytest tests/integration/embedded(red→green)
- Add
[x] 4.
running_harness/serve(service/process.py) and the public API- Move the body of
commands.serveintorunning_harness. It openstemporal_client, builds the runtime, runs the worker (forced on in embedded mode, with the INFO line), and runs uvicorn as a task with a readiness wait onserver.started(re-raising if the task dies). Teardown runs in reverse. serveis built on it.commands.serveandcommands.workerroute throughtemporal_client. Re-exportrunning_harness,serve,RunningHarness,temporal_clientfromtiny_harness.service.- Search attributes and the heartbeat schedule go through the existing
ensure_search_attributesandensure_heartbeat_schedule. - Depends on: 2
- Requirements: R2.6, R2.7, R5.1, R6.1–R6.3
- Test: T2 —
Scenario: Programmatic harness runs a task in embedded mode(stub model; an A2A message reaches a terminal state throughrunning_harness) andScenario: Embedded server registers search attributes and the heartbeat schedule; T10 — the existingtests/integration/a2asuite unchanged (red→green)
- Move the body of
[x] 5. CLI: per-command embedded behaviour and signal routing
- In
cli.dispatch:workerin embedded mode → exit 2;schedules delete/tasks purgewithpersist = false→ exit 2;EmbeddedTemporalError→ exit 1 withembedded Temporal failed to start: <cause>. - In
cli.main: installSIGTERM→KeyboardInterrupt. commands.tuiwith no--urlin embedded mode wrapsrun_tuiinrunning_harness; with--urlit is unchanged.schedules_deleteandtasks_purgeusetemporal_client.- Depends on: 4
- Requirements: R2.3, R2.4, R5.2–R5.5
- Test: T1 —
uv run pytest tests/unit/test_cli.py(exit codes, messages); T2 —Scenario: TUI hosts its own harness in embedded mode(Textual pilot against the in-process harness),Scenario: TUI with a URL starts no embedded server,Scenario: Admin commands act on persisted embedded state; T8 — SIGTERM totiny-harness servein embedded mode leaves no dev-server child (abuse case 6) (red→green)
- In
[x] 6. Demo:
config.embedded.tomland an optional config path- Add
examples/demo/config.embedded.toml.examples.demo.__main__takes an optional path argument and callsservice.serve. - Add the embedded e2e test beside the existing ones. The e2e conftest's
REQUIREDbecomes per-test: the embedded test needs onlyOPENAI_API_KEY. - Depends on: 5
- Requirements: R6.4
- Test: T4 —
Scenario: Demo completes a task in embedded mode without Temporal credentials,env -u TEMPORAL_API_KEY uv run pytest tests/e2e -k embedded(red→green: fails before the config exists)
- Add
[x] 7. Documentation and capability docs
- README: a "Run without Temporal" block beside the Cloud instructions (R7.1).
- Configuration reference (
docs/guide/anddocs/capabilities/configuration.md): every new key, its default, and the per-mode table (R7.2).docs/capabilities/durable-execution.mdgets the embedded-mode behaviour and a history row. docs/guide/deployment.md: "embedded mode is not a production deployment" and why (R7.3).docs/guide/getting-started.md: embedded as the zero-setup path.docs/architecture/architecture.md: thedurable/temporal.pyline.- Depends on: 5, 6
- Requirements: R7.1–R7.3
- Test: T12 —
bun run --cwd docs docs:build; markdownlint viauv run pre-commit run --all-files
[x] 8. Regression sweep
- Run CI's exact commands on the branch. Fix any break in remote mode (expected: none). Confirm
examples/demo/config.tomlis untouched. - Depends on: 1–7
- Requirements: R1.2, R1.3
- Test: T10 —
uv run pre-commit run --all-filesanduv run pytest tests/integration tests/contract tests/security tests/ui
- Run CI's exact commands on the branch. Fix any break in remote mode (expected: none). Confirm
[x] 9. Record the PR in
evidence/pull-requests.md- Update #18's row: it now carries the implementation as well as the spec chain.
- Depends on: 8
- Requirements: —
- Test: n/a (paper trail)
Dependency graph (DAG)
Tasks 3 and 4 are independent once 2 is done; everything else is sequential.
Checkpoints
- After every task:
uv run pytest tests/unitplus that task's own test command, with the red→green recorded in the commit. The task is ticked here in the same commit. - After task 3 and task 5:
env -u TEMPORAL_API_KEY uv run pytest tests/integration, the whole suite, because both change how integration tests start servers. - After task 8: the CI commands in full. Then the verification node executes
testing-plan.md(T1–T12) and commits the evidence. Then come the self and critic review rounds and the security review gate (evidence/security-review.md).
Review comments
Appended by the-loop's
record-feedbackhook when a human gate approves with comments (issue-109). Append-only and attributed: an approval never silently discards a reviewer's suggestions, and the feedback travels with the document it concerns rather than living in a side-channel tracker.