Skip to content

The Writer — the cold path

Path: cold · Status: mem recorder on; enrichment off (batch only); consolidator unbuilt · Code: src/alto/read/mem_bridge.py, src/mem/

The Writer turns the now into the past. It takes what was just said and persists it — episodes, the entities mentioned in them, the relations and events those mentions imply — so a later moment can be shaped by an earlier one. Everything Alto can ever remember arrives through here. If the Writer records nothing, the read side has nothing to read, and several organs that look independently broken are in fact starved by this one.

Where it sits

It consumes a heard utterance and the finished reply: perception hands the writer the raw text and its speaker, and the turn hands it Alto's reply. The target shape starts from a grounded percept from Intake, an organ deleted in code on 2026-10-04 (intake.md). Today it emits segments and derived lines into mem, the only store.

It is a cold-path sibling of the Subconscious — not nested under it, not attached to the designed Intake (architecture revisions §2). Three tenses in the design: intake extracts the incoming, the subconscious feels the now, the Writer turns the now into the past. (In code there is no intake, and the Writer records what perception heard.)

The firewall is current-mood-blindness, not affect-neutrality (revisions §3). The Writer must read graph-affect — prior valences, standing affect — because computing an episode's valence is its job. What it must never do is subscribe to the live subconscious mood as an input to what it records: Alto irritated now does not get to bend the facts. The subconscious legitimately gates whether and how much is worth writing; the Writer alone controls what. Downstream, what it writes is read by the Surfacer — see retrieval.md.

The intended design

Two timescales (revisions §4), because memory has two:

  • Live recorder — concurrent with experience but off the hot path. Records that an episode is happening, stamps its date, captures what was perceived, computes that episode's immediate valence. Live so recent memory does not lag experience.
  • Consolidator — batched, sleep-time. Aggregates standing affect across many episodes, promotes and demotes beliefs, detects patterns, prunes, restructures. Deliberately not live: expensive, and it needs an accumulated backlog — sleep pressure is roughly that backlog's size. Draft contract: SPEC-consolidation-loop.md.

This split is the Writer-side expression of the standing rule that the hot loop never writes durable structure — only the cold consolidator changes who Alto is becoming.

The graph writer is gone and its replacement is designed, not built. That is the most important thing to know before reading the rest. PLAN-writer-v2.md decides the target: v2 replaces the resolver's identity adjudication with a deterministic canonical → alias → create-new cascade (the LLM stops adjudicating sameness), makes merges soft so every later mistake is recoverable, and collapses the three extractors into one organ. Separately, the config records a 2026-08-15 decision to retire the per-turn LLM cold path altogether in favour of an episode-close picture pass (config.yaml, writer: block), shaped by SPEC-episode-account.md after DECISION-picture-shape-and-felt-state.md found the claim/triple form wrong — an exclusion became an entity, a claim got built on the wrong nucleus, a comparative stance had no slot at all.

The AGE-graph writer (episode Nodes, succession edges, the affect assessor, the episode reader) is archived at tag archive/age-brain-2026-10-11. The design above is what remains authoritative.

What exists today

The recorder is mem, and it is on. src/mem/__init__.py maps its modules against SPEC-writer-build_14; the seam is mem_bridge.py, the only alto/ module that imports mem. On every turn it appends a segment before the reply (observe_user_utterance, src/alto/read/mem_bridge.py:219, axis speaker) and one after it (observe_turn_complete, src/alto/read/mem_bridge.py:236, axis alto_reply). It takes no actions: the action axis has no producer. mem.enabled: true (src/config.yaml:257) — one INSERT per segment, no model, no GPU.

  • Failure. mem.append spools to a JSONL file when the store call raises and returns success, so the turn never fails because the database is down (src/mem/api.py:212); drain replays the spool later.
  • Episodes. A segment that arrives more than idle_gap_seconds (600, src/config.yaml:385) after the last one, on any axis, closes the open episode and opens the next (src/mem/api.py:268). The write side has no turn-shaped episode of its own.
  • Closing. close_episodes: true (src/config.yaml:300) runs the EpisodeCloser (src/mem/worker.py:807), which certifies a closed episode. It refuses any episode whose segments the enricher has not reached, so with enrichment off it finds nothing until a batch run advances the segment mark.
  • Enrichment is off. enrich: false (src/config.yaml:272) for a measured reason: step ⑤'s 14B needs 9.57 GiB beside a 9.69 GiB conversational model on a card with ~14.5 GiB free, so loading it evicts all three production models and costs 8.8s on the next turn against ~1s of work. The Enricher (src/mem/worker.py:389) therefore does not run in the process. Derived lines are written only when someone runs the batch (src/scripts/mem/enrich_batch.py); until then a stated fact is raw testimony only. When a rebuild replaces a derived row, the old one is archived to <table>_history in the same transaction.

corefer_model is false (src/config.yaml:310). The Surfacer reads what mem holds — see retrieval.md.

The four name gates (entity_shape, name_gates, phrase_gates, value_gates) and canon live in src/mem/. The write side records no entity, relation or self-schema facts anywhere but as mem lines; Alto's name comes from mem's testimony (selfname.py).

The consolidator has no code. Aggregate standing affect, belief promotion, pattern detection and pruning are designed and unbuilt.

The gap

The gap is not "the organs do not work." The per-turn LLM write organs were switched off, then deleted, and the graph writer went with the AGE graph; the thing that replaces them is designed but not built. The plan doc is PLAN-writer-v2.md.

Closing it is gated on that build order: phase 0 extracts the guard chain the three extractors each copy, phase 1 lands the deterministic identity cascade, phase 2 builds a merge whose loser is soft-deactivated rather than DETACH DELETEd — which is what makes every later mistake recoverable and is therefore pillar 9's real unblock — and phase 3 collapses the extractors. The 15 named type gates (now in src/mem/ as name_gates, phrase_gates and value_gates) and their tests are v2's acceptance suite, turning "rebuild" into "port" (§2), and the five identity mechanisms — PRs #197–#201, closed 2026-09-29 with their tips tagged archive/resolver-identity-stack/* — are the only rollback. §6b records why they were closed before v2 beat v1's edge precision on an unseen slice, which was the original condition. One metric is already retired: pair-closure scored 10 and 13 on the same commit, same config, same slice (§5). Do not reach for it.

Pillars this serves

  • Pillar 9 (repairability over precision) — the governing principle here. Bias toward capture; a precision gate that DROPS rather than records is the anti-pattern (the deleted resolver and relator were exactly that gate). Measured trigger: 730 simulated episodes → 953 mentions, 0 relations; a blind gold standard → 0 of ~30 durable facts.
  • Pillar 3 (honesty by construction) — the line pillar 9 does not cross, worked out at _assertion_polarity (now mem/polarity.py, written for the deleted relator): a span with both a clause boundary and a negator has a sign no regex can decide, so the relator dropped the proposal rather than guessing it. An inverted sign "says the opposite of the utterance, so there is nothing to revise later" — a confident falsehood that looks exactly like a fact.
  • Pillar 2 (affect as lived state) — the Writer is designed to compute an episode's evidential affect from stored evidence, never from the present mood. Nothing computes it today.
  • Pillar 1 (continuous being) — episode succession is the record of continuity; mem rotates episodes on an idle gap, which is the nearest thing to a continuity record built so far.
  • Pillar 7 (composed proof) — every flag above was flipped, or held, on a measured gate.

Sources