Skip to content

the memory index's reader and writer open a path of any kind: a FIFO beside the memories wedges MemoryIndex #2078

Closed

Description

@argszero

The memory index's reader and writer open a path of any kind, so a FIFO beside the memories wedges them too

What is wrong

#2062 taught the three file tools to ask what they are opening, because an open is not a read.
#2073 found the same condition in the daemon's own five path readers, and #2076 (merged as
123d0abc) in the memory store's reader. Both #2076's issue (#2075) and this class of defect name
the layer below: emrg/memory.py's MemoryIndex pair.

MemoryIndex.from_file   (emrg/memory.py)  ->  path.exists()  then  path.read_text(...)
MemoryIndex.save        (emrg/memory.py)  ->  path.stat()    then  path.write_text(...)

Neither asks the subject's kind. On a FIFO both opens block with no bound and raise nothing: a read
waits for a writer, a write waits for a reader. The callers' except (OSError, ValueError) cannot
bound that, because nothing is raised — the call simply never returns.

Measurement

Checkout /Users/argszero/.emrg/evolution/emrg, detached worktree at #2076's head b831a745,
emrg.__file__ printed inside every child and resolving in that worktree. One fixture, each call in its
own process under the parent's 25 s cap:

the subject is a FIFO (<memory dir>/MEMORY.md):
    MemoryIndex.from_file(<fifo>)  -> DID NOT RETURN within 25s
    MemoryIndex().save(<fifo>)     -> DID NOT RETURN within 25s

control, the same calls with the FIFO replaced by a regular file:
    MemoryIndex.from_file(<file>)  -> RETURNED  (0.0s)
    MemoryIndex().save(<file>)     -> RETURNED  (0.0s)

control, the same calls with the path absent:
    MemoryIndex.from_file(<absent>) -> RETURNED  (0.0s)
    MemoryIndex().save(<absent>)    -> RETURNED  (0.0s)

Both arms hang, which is why #2075 left the pair: guarding the reader alone only moves the hang to
the writer.

Reachability — weaker than #2075's, and stated as such

#2075's two frames (list_memories, read_memory) walk *.md and skip MEMORY.md, so no daemon
frame reads the index today
. What reaches the pair is the store's own public API:

caller path
MemoryStore.create(), update(), merge(), rebuild_index(), delete() _load_index() → MemoryIndex.from_file
the same, plus promote_to_project() _save_index() → MemoryIndex.save

so this is an in-process API boundary, not a client-reachable one: anything that runs in the daemon's
process or imports the module (a script, a skill, a future frame) can reach it, and nothing today does.
Filed rather than fixed-with-#2076 so the boundary #2075's "Not taken" section named is tracked
somewhere a merged PR cannot hide it — the note it lived in closes with the issue.

What fixes it

  • The reader: non_regular_kind (added by #2076, on master) before read_text, exactly the shape
    MemoryFile.from_file now has.
  • The writer: the same predicate before write_text, but the answer has to be decided rather than
    copied — _save_index's callers return normally today, so a refusal raised there would surface as a
    new exception in create/update/merge/promote_to_project, where the honest behaviour is more
    likely to skip the index write and report it (the shape _unreadable_index_notice already gives the
    daemon's carriers of the same file).

A control for both arms is in the measurement above; the tests belong beside #2076's, in
tests/test_memory.py.

Activity

  1. argszero commented on Oct 10, 2026

    @argszero
    OwnerAuthor

    Handled by #2080 — branch fix/the-index-reader-and-writer-ask-the-kind, commit 7c2f57b9. Both halves of the pair ask the kind now; the writer's answer is a refusal rather than a skipped write, with the reason in the PR body.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions