2026-09-13 · builder model: claude-fable-5 · independent reviewer: gpt-5.6-sol (read-only)
This report covers three things: (1) how the memoryfield prototype (a directory of
Markdown pages with a SQLite vector index) is now wired into every Sorcar agent run
behind a config flag, (2) the new memory_refresh maintenance tool, and
(3) what the agent actually wrote to memory when twelve real tasks from the task
history were replayed back-to-back as independent sessions sharing one memory.
_memory_root_for_run() in SorcarAgent.run().
Sub-agents spawned by run_parallel go through the same path, so a whole task tree
shares one store consistently. Tools and protocol are always enabled together — never one
without the other.use_memory (default off) and
memory_dir (default ~/.kiss/memories) in ~/.kiss/config.json
(vscode_config.DEFAULTS). The KISS_USE_MEMORY environment variable
overrides the stored flag for one process (0/false/no/off disable, anything else enables).MEMORY_PROTOCOL block
(search before work, write durable lessons, no secrets, refresh when stale) is appended to the
composed system prompt — after SYSTEM.md/SYSTEM_LITE.md and any caller suffix.append_basic_tools=False (the run has no built-in tools, so promising
memory_* tools would be a lie), when docker_image is set (the memory
tools execute on host paths; registering them would hand a containerized task read/write access
to host memory outside the container boundary), and for run-to-completion CLI models
(cc/*, codex/* never see KISS-registered tools).self._memory_tools is built per run and reset in the
finally block, so a reused agent instance cannot leak one run's memory into a run
that should not have it.memory_refreshRequested mid-task: a way to keep the store fresh. memory_refresh(stale_days=30,
duplicate_threshold=0.9) does three things in one call:
git pull) are re-embedded and deleted pages drop out of the index.VectorIndex.near_duplicates() scans all
stored embedding pairs and lists pages at or above the similarity threshold, so the agent can merge
them (memory_write the merged page, memory_delete the loser).updated frontmatter is older than
stale_days are listed for re-verification or deletion. Pages with missing or
unparseable timestamps are deliberately skipped — staleness cannot be established for them.Run against the store the agent built during the day's replay:
Index refreshed: 1 added, 0 updated, 0 removed, 5 unchanged. Near-duplicate pages (merge with memory_write, then memory_delete the loser): nonstick-brand-comparison-2026 ~ sensarte-cookware-brand-facts (similarity 0.747) sorcar-agent-failed-abruptly-sentinel ~ sorcar-task-classifier (similarity 0.630)
(That output used a demonstration threshold of 0.55; at the default 0.9 neither pair is flagged, which is correct — these pages are related, not redundant.)
Twelve real past tasks from ~/.kiss/sorcar.db were replayed sequentially, each as a
fresh SorcarAgent session on claude-fable-5 (no shared conversation), all sharing
tmp/memory-day/memories. Every tool call was logged. Total replay cost: $14.74.
| # | Task (real, from history) | Time | Cost | Memory activity |
|---|---|---|---|---|
| 0 | hi | 12s | $0.11 | searched, wrote nothing (right call) |
| 1 | what tools are available to you? | 17s | $0.14 | none (listed memory tools in its answer) |
| 2 | is the KISSAgent agentic? | 39s | $0.51 | searched; wrote kissagent-agentic-mode |
| 3 | …the KISSAgent that checks if a task is simple/development | 62s | $0.61 | recalled task 2's page; wrote sorcar-task-classifier |
| 4 | is SENSARTE an American brand? …safe? | 247s | $3.75 | searched; wrote sensarte-cookware-brand-facts |
| 5 | Compare SENSARTE with Carote, GreenPan, Caraway… | 290s | $5.74 | recalled task 4's page (skipped re-research); wrote comparison page that cross-references it |
| 6 | BMW 330i drive cycle | 40s | $0.22 | searched, wrote nothing |
| 7 | how does automerge work in non-worktree mode? | 94s | $0.70 | searched; wrote sorcar-worktree-automerge-vs-nonwt-guard |
| 8 | change the "Another tab is running…" message | 61s | $0.48 | none (small edit, nothing durable) |
| 9 | add a classify-tasks parameter to server run() | 113s | $0.96 | recalled task 3's classifier page, then verified the parameter already exists |
| 10 | uv run check --full and fix | 132s | $0.21 | none (checks passed, nothing to record) |
| 11 | why is the last task shown as failed? | 152s | $1.32 | searched; wrote sorcar-agent-failed-abruptly-sentinel |
thinking.type.disabled; it now requires
thinking.type.adaptive). Every replayed run fell back to the full SYSTEM.md prompt,
which is the designed no-op behavior. Not caused by this change; worth a separate fix in
task_classifier.py.| Severity | Finding | Status |
|---|---|---|
| High | CLI run-to-completion models (cc/*, codex/*) would get MEMORY_PROTOCOL but can never call KISS-registered tools | Fixed — gated in _memory_root_for_run |
| High | Docker runs would expose host-path memory tools inside the container boundary | Fixed — Docker runs get no memory |
| High | Pre-existing: a caller-supplied model_config["system_instruction"] silently replaces the whole composed system prompt (not just the memory block) via setdefault | Reported; pre-existing, affects all prompts, out of this change's scope |
| Medium | Semantic search always uses text-embedding-3-small, so recall silently needs an OpenAI key; the offline hashing embedder is never auto-selected | Known prototype limit (search degrades to a tool error; writes still work) |
| Medium | Embedding API cost is not folded into the task's budget accounting | Known limit (~$0.01 per 300 pages) |
| Medium | Concurrent sub-agents share the page directory without cross-process file locking (lost-update window on simultaneous writes to the same page) | Known limit; SQLite index itself uses per-op connections with 30s busy timeout |
| Medium | No settings-panel UI for the two new keys (config file / env only) | Deliberate for a flag that defaults off |
| Low | memory_* names not in the MCP tool-name reservation set; unvalidated memory_dir paths surface as repeated tool errors | Reported |
The reviewer confirmed the paths that matter are correct: ChatSorcarAgent, WorktreeSorcarAgent,
server/daemon runs, run_agent, and run_parallel children all converge on
the same flag logic; the SYSTEM_LITE (simple-task) path still gets the protocol; entry and
finally cleanup prevent reuse leaks; and the new DEFAULTS keys round-trip correctly
through sanitize/load/save and old clients.
src/kiss/tests/core/memoryfield/ (including the new
test_refresh.py) and the new src/kiss/tests/agents/sorcar/test_memory_wiring.py
— 18 offline tests for settings resolution and the run gates, plus 3 live agent tests on
claude-haiku-4-5: a store-then-recall across two separate sessions, a default-off run that
behaviorally proves no memory tool exists, and an append_basic_tools=False run.memoryfield/tools.py and index.py; all new
lines and branches in sorcar_agent.py covered.uv run check --full (ruff, mypy, pyright) passes.# persistent (all future runs) python -c "from kiss.core.vscode_config import load_config, save_config; \ c = load_config(); c['use_memory'] = True; save_config(c)" # or one process only KISS_USE_MEMORY=1 <run sorcar>
Pages land in ~/.kiss/memories/*.md — plain Markdown you can read, edit, or
delete in any editor; the index rebuilds itself on the next search.
run_parallel use.