Phase 1 persistence per PHASE1.md §6.
history.open(path, meta?) -> session | (nil, err)
parent dir auto-created; meta line written iff file is new/empty so
reopening a session doesn't duplicate the header
session:append(turn)
JSON-encoded line, fh:flush after every write (no fsync — Q16
tracks the policy if it ever bites)
session:close()
history.load(path) -> meta, turns | (nil, err)
skips unparseable lines (e.g. partial trailing write from a crash);
distinguishes the meta-header line from role/content turn lines
history.list_sessions(dir) -> [basename, ...]
sorted (ISO 8601 names lex-sort chronologically); no mtime / turn
counts in Phase 1 — that's a Phase 4 :sessions UI concern
Smoke:
- open, append 3 turns, close, list_sessions sees 1 file
- load returns meta (model="fast") and 3 turns in order
- corrupt tail (partial JSON line appended) is silently skipped on load
- reopen with different meta does NOT duplicate the header line
Repl wiring (`:save`, `:resume`, `:sessions`, auto-write on quit) lands
in the next commit.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- README, .gitignore, CLAUDE.md (project conventions)
- docs/PHASE0.md — full Phase 0 manifest (locked substrate)
- 10 root .lua modules + 4 ffi/ bindings, all stubs raising NotImplemented
with module-scoped responsibilities matching the manifest
- config.lua wired to current dirac/hossenfelder endpoints (qwen-coder-7b
snappy/32k + cloud via OpenRouter through hossenfelder)
File names match docs/PHASE0.md §4 exactly. Module bodies fill in across
later phases; the tree shape is locked.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>