v0.9 Sub-issue #7 — Built-in echo Provider + end-to-end conformance harness + docs
Part of v0.9 epic. Final sub-issue.
Pulls Stages 1–5 together: ships a built-in text_chat echo Provider so
the runtime can actually respond, adds the end-to-end conformance test
harness exercised in CI, and ships the v0.9 quickstart docs.
Spec ref
docs/LIFE_RUNTIME_STANDARD.md Part B §B.2.1 (LifeCapabilityProvider)
- All sub-issues 1–6 above (this PR consumes their public surfaces).
Built-in echo Provider
runtime/providers/echo.py:
- Implements
LifeCapabilityProvider for capability_name() == "text_chat".
provider_name() == "echo-builtin", provider_version() == "1.0.0".
sandbox_class() == "built_in".
initialize(asset_paths, params, hard_constraints):
- Reads
params.system_prompt (if present) — used to prefix invoke().
- Reads
hard_constraints.max_response_length — clips output.
- Stores both for
invoke().
invoke({user_input, hosted_api_allowed, ...}):
- Returns
{text: f"You said: {user_input}"} (no LLM).
- Truncates to
max_response_length if set.
- Ignores
hosted_api_allowed (the echo Provider has no hosted mode).
teardown(): no-op.
This Provider is purely a reference implementation. It exists so a
fresh DLRS user can run lifectl run minimal-life-package.life and get
a meaningful response without installing any user-installed Provider.
Real Providers (TTS, video synthesis, GraphRAG-backed chat, ...) ship as
separate v0.10+ work via the DLRS Extension Architecture plugin system.
The echo Provider is auto-registered at runtime/providers/__init__.py
import time, ensuring ProviderRegistry finds it without explicit
configuration.
Update minimal-life-package fixture
examples/minimal-life-package/ already exists from v0.7 (#83) but
predates the v0.8 binding spec. Update the fixture so it has a
binding/runtime_binding.json declaring:
{
"capabilities": ["text_chat"],
"capability_binding": {
"text_chat": {
"asset_paths": [],
"params": {
"system_prompt": "You are a digital life instance of the test author."
},
"hard_constraints": {
"max_response_length": 200
},
"engine_compatibility": [
{ "engine": { "name": "echo-builtin", "version_range": ">=1.0.0,<2.0.0", "strict": false } }
],
"tier_floor": null
}
},
"surface": {
"ui_hints": {
"disclosure_label": "(AI digital life instance of the test author)"
}
},
"hosted_api_preference": {
"allowed": false
}
}
Mirror this update in the existing tools/build_life_package.py test
fixture path. Bump examples/minimal-life-package/manifest.json if
needed for v0.8 conformance.
End-to-end conformance harness
tools/test_runtime_conformance.py:
def test_e2e_minimal_life_package():
# 1. Build the .life via existing builder
pkg_path = build_minimal_life_package(tmpdir)
# 2. Spawn lifectl run --once with a canned prompt
proc = subprocess.run(
["lifectl", "run", "--once", "--no-tty",
"--poll-interval-override", "1",
pkg_path],
input="Hello!\n",
capture_output=True,
timeout=30,
)
# 3. Assert stdout has the disclosure label + echo response
assert proc.returncode == 0
assert "(AI digital life instance of the test author)" in proc.stdout
assert "You said: Hello!" in proc.stdout
# 4. Assert audit log has all required events in the right order
log = read_runtime_audit_log(pkg_path)
assert event_order(log) == [
"mount_attempted",
"withdrawal_poll", # pre-flight
"capability_bound", # text_chat → echo-builtin
"mount_succeeded",
"turn_started",
"turn_completed",
"withdrawal_poll", # at least one watcher poll within 1s
"unmount",
]
# 5. Assert hash chain integrity
assert validate_hash_chain(log)
# 6. Assert no zombie subprocess
assert no_zombie_children()
def test_e2e_withdrawal_revocation():
pkg_path = build_minimal_life_package(tmpdir, withdrawal_url=mock_endpoint("/revoked"))
# mock_endpoint returns {revoked: true} after 2 polls
proc = subprocess.run(
["lifectl", "run", "--no-tty",
"--poll-interval-override", "1",
pkg_path],
input="hi\nhi\nhi\nhi\nhi\n",
capture_output=True,
timeout=30,
)
assert proc.returncode == 0
log = read_runtime_audit_log(pkg_path)
assert any(e["event_type"] == "unmount" and e["reason"] == "withdrawal" for e in log)
5 conformance test cases total:
- End-to-end happy path (above).
- Withdrawal revocation triggers unmount within 2 polls.
- Expiry triggers unmount.
- Forbidden_use rejection round-trips.
- Audit chain integrity across full session.
CI integration
.github/workflows/validate.yml: replace the per-stage runtime-*
jobs (added by sub-issues 1–6) with a single consolidated
runtime-conformance job that runs python tools/test_runtime_conformance.py
- all per-stage tests. Or keep both layers — final structure is up to
the reviewer; either is acceptable.
Documentation
docs/LIFE_RUNTIME_QUICKSTART.md (new)
Walk-through:
- Install the runtime (
pip install -e . from repo root for now;
pip install dlrs-runtime from PyPI is post-v0.9).
- Build the example
.life:
python tools/build_life_package.py --record examples/minimal-life-package/.
- Run it:
lifectl run --once examples/minimal-life-package/out/*.life.
- Inspect the runtime audit log:
cat ~/.local/share/dlrs/mounts/<package_id>/events.jsonl.
- What each Stage does (link to
docs/LIFE_RUNTIME_STANDARD.md Part B).
README.md + README.en.md updates
Add a "Quickstart" section pointing at the runtime quickstart doc;
update the implementation-status badge from "spec only" to "reference
runtime ✓".
docs/IMPLEMENTATION_STATUS.md + docs/GAP_ANALYSIS.md
Mark v0.9 deliverables complete + flip the runtime-completeness from 0%
to ~30% (single .life only; multi-life / .world / plugins still pending).
CHANGELOG.md
Add v0.9.0 entry summarizing the 7 sub-issues + naming the milestone
v0.9-runtime-impl.
ROADMAP.md
Update the .life Runtime Standard track row for life-runtime v0.1.1
to mark "reference impl shipped (v0.9 epic)". Add a v0.10 placeholder row
referencing the embodiment + multi-life + DLRS-EA design.
Acceptance
After merge, the v0.9 epic closes; v0.10 epic kicks off.
v0.9 Sub-issue #7 — Built-in echo Provider + end-to-end conformance harness + docs
Part of v0.9 epic. Final sub-issue.
Pulls Stages 1–5 together: ships a built-in
text_chatecho Provider sothe runtime can actually respond, adds the end-to-end conformance test
harness exercised in CI, and ships the v0.9 quickstart docs.
Spec ref
docs/LIFE_RUNTIME_STANDARD.mdPart B §B.2.1 (LifeCapabilityProvider)Built-in echo Provider
runtime/providers/echo.py:LifeCapabilityProviderforcapability_name() == "text_chat".provider_name() == "echo-builtin",provider_version() == "1.0.0".sandbox_class() == "built_in".initialize(asset_paths, params, hard_constraints):params.system_prompt(if present) — used to prefixinvoke().hard_constraints.max_response_length— clips output.invoke().invoke({user_input, hosted_api_allowed, ...}):{text: f"You said: {user_input}"}(no LLM).max_response_lengthif set.hosted_api_allowed(the echo Provider has no hosted mode).teardown(): no-op.This Provider is purely a reference implementation. It exists so a
fresh DLRS user can run
lifectl run minimal-life-package.lifeand geta meaningful response without installing any user-installed Provider.
Real Providers (TTS, video synthesis, GraphRAG-backed chat, ...) ship as
separate v0.10+ work via the DLRS Extension Architecture plugin system.
The echo Provider is auto-registered at
runtime/providers/__init__.pyimport time, ensuring
ProviderRegistryfinds it without explicitconfiguration.
Update minimal-life-package fixture
examples/minimal-life-package/already exists from v0.7 (#83) butpredates the v0.8 binding spec. Update the fixture so it has a
binding/runtime_binding.jsondeclaring:{ "capabilities": ["text_chat"], "capability_binding": { "text_chat": { "asset_paths": [], "params": { "system_prompt": "You are a digital life instance of the test author." }, "hard_constraints": { "max_response_length": 200 }, "engine_compatibility": [ { "engine": { "name": "echo-builtin", "version_range": ">=1.0.0,<2.0.0", "strict": false } } ], "tier_floor": null } }, "surface": { "ui_hints": { "disclosure_label": "(AI digital life instance of the test author)" } }, "hosted_api_preference": { "allowed": false } }Mirror this update in the existing
tools/build_life_package.pytestfixture path. Bump
examples/minimal-life-package/manifest.jsonifneeded for v0.8 conformance.
End-to-end conformance harness
tools/test_runtime_conformance.py:5 conformance test cases total:
CI integration
.github/workflows/validate.yml: replace the per-stageruntime-*jobs (added by sub-issues 1–6) with a single consolidated
runtime-conformancejob that runspython tools/test_runtime_conformance.pythe reviewer; either is acceptable.
Documentation
docs/LIFE_RUNTIME_QUICKSTART.md(new)Walk-through:
pip install -e .from repo root for now;pip install dlrs-runtimefrom PyPI is post-v0.9)..life:python tools/build_life_package.py --record examples/minimal-life-package/.lifectl run --once examples/minimal-life-package/out/*.life.cat ~/.local/share/dlrs/mounts/<package_id>/events.jsonl.docs/LIFE_RUNTIME_STANDARD.mdPart B).README.md+README.en.mdupdatesAdd a "Quickstart" section pointing at the runtime quickstart doc;
update the implementation-status badge from "spec only" to "reference
runtime ✓".
docs/IMPLEMENTATION_STATUS.md+docs/GAP_ANALYSIS.mdMark v0.9 deliverables complete + flip the runtime-completeness from 0%
to ~30% (single .life only; multi-life / .world / plugins still pending).
CHANGELOG.mdAdd v0.9.0 entry summarizing the 7 sub-issues + naming the milestone
v0.9-runtime-impl.ROADMAP.mdUpdate the
.lifeRuntime Standard track row forlife-runtime v0.1.1to mark "reference impl shipped (v0.9 epic)". Add a v0.10 placeholder row
referencing the embodiment + multi-life + DLRS-EA design.
Acceptance
LifeCapabilityProvidercorrectlyruntime-conformancejob greenv0.9-runtime-implready for releaseAfter merge, the v0.9 epic closes; v0.10 epic kicks off.