JIRO_V040_PHASE3_HANDOFF.md
Rama main · réplica de lectura del repositorio original.
# JIRO v0.4.0 — Phase 3 Handoff
- **Written:** 2026-10-12
- **Covers:** Phase 2 (cognitive memory), complete
- **State at handoff:** 265 memory tests passing, Phase 1 suite re-verified at 222
Phase 0 artifacts (`JIRO_V040_PHASE0_HANDOFF.md`, the v0.4.0 architecture and task
documents) are **not present in this repository**. Phase 1 and Phase 2 were both
derived from the code as it stands and recorded their decisions as ADRs. Phase 3
should not assume a Phase 0 document exists to consult.
## What is done
**Phase 1 — durable task runtime.** Lifecycle, persistence, recovery,
cancellation, ownership, at-least-once execution. `docs/runtime.md`, ADRs
0001–0007, `docs/adr/PHASE1_REPORT.md`.
**Phase 2 — cognitive memory.** Working, episodic, semantic and procedural
memory with owner-scoped storage, provenance, contradiction handling, retention,
verified deletion, explainable retrieval, an optional embedding path, and a
durable outbox wired to task outcomes. `docs/memory.md`, ADRs 0008–0010,
`docs/adr/PHASE2_REPORT.md`.
## The three things Phase 3 must not undo
1. **Ownership comes from the durable record, never the payload.**
`TaskContext.owner_key_id` is populated by the worker from `TaskRecord`. The
memory task schemas deliberately declare no `owner_key_id` field, so a
spoofing attempt fails validation rather than being honoured. Any new memory
transport must take the owner from the auth context and must not reintroduce a
caller-supplied owner field.
2. **Stored procedures do not execute.** Steps name registered actions and carry
no callable. See ADR 0009 before writing anything that runs a
`MemoryProcedure`.
3. **`Database.execute()` and `Database.transaction()` roll back on failure.**
This is not a style preference. Returning a connection to the pool inside an
open write transaction keeps the file's write lock and deadlocks every later
writer until its busy timeout expires. It cost a long debugging detour during
Phase 2; `docs/adr/PHASE2_REPORT.md` records the symptom.
## Where Phase 3 could go
Ordered by what the code most obviously wants next, not by priority.
**A public memory surface.** Memory currently has a service facade and three
runtime task types, but no REST endpoint, MCP tool or CLI command. The service
is the interface; a transport is mostly plumbing. Note that a new transport must
derive owner from the auth context (point 1 above).
**An embedding backfill schedule.** `MemoryService.embed_pending()` is
implemented and bounded, but nothing calls it periodically. This is only inert
while `memory.embedding.enabled` is false — turning it on without a scheduler
leaves every record permanently `pending`. `docs/memory.md` documents the
configuration.
**A procedure executor.** Deferred to Phase 3+ by ADR 0009, and the most
dangerous item on this list. It must resolve `action` names through a registry of
registered callables, check `required_capabilities`, and never reach stored data
as code.
**Crash-injection tests.** Phase 2 covers a crash between "task committed" and
"episode written" via the outbox, but not a real `kill -9` mid-transaction. The
rollback fix in point 3 is currently tested through a failing statement rather
than a killed process.
## Working with this code
```powershell
# Memory suite
python -m pytest tests/test_memory_domain.py tests/test_memory_persistence.py `
tests/test_memory_retrieval.py tests/test_memory_types.py `
tests/test_memory_security.py tests/test_memory_integration.py
# Phase 1 regression, after any runtime or db.py change
python -m pytest tests/test_runtime_lifecycle.py tests/test_runtime_persistence.py `
tests/test_runtime_integration.py tests/test_runtime_recovery.py `
tests/test_runtime_api.py
# Lint
python -m ruff check jiro/memory jiro/db.py jiro/runtime tests/conftest_memory.py tests/test_memory_*.py
```
Ten pre-existing lint errors remain in `tests/test_self_healing.py` and
`tests/test_social_normalize.py`. They are unrelated to Phases 1 and 2 and were
deliberately left untouched.
There is an uncommitted working tree containing both phase's work and
unrelated user changes. `tests/test_mcp.py` in particular carries a formatting-
only diff from an over-broad `ruff --fix` during Phase 1; its logic is
unchanged. Do not revert user changes when tidying up.
## Two things worth knowing before you design
**`owner_key_id` is an API key, not a person.** A user holding three keys has
three separate memory namespaces and cannot recall across them. This is
inherited from ADR 0002 and is a real limitation, not a documentation
convention. If Phase 3 introduces a user concept, `owner_key_id` becomes a
foreign key and the existing scoping does not change — the queries already carry
it.
**Anonymous callers share one memory namespace.** With `owner_key_id = NULL`
there is nothing to separate them by. Jiro does not pretend otherwise.