feat(memory): person-memory writer — deterministic, evidence-minimal, firewalled - #297
Merged
Merged
Conversation
… firewalled
Adds src/pxh/people.py: a regex-only writer that extracts three narrow fact
kinds (preference, relationship, commitment) from what Obi literally says,
into state/people-{persona}.jsonl. No LLM anywhere in the write path;
provenance kind is the hardcoded literal "report".
Privacy shape:
- Separate file mind.py never opens — person facts structurally cannot
reach reflection, /api/v1/public/thoughts, the feed, blog or Bluesky.
Pinned by source-scan tests in tests/test_people_invariants.py.
- Evidence is the exact matched clause plus the source message id, never
the whole utterance: "I'm sad about school today, but I really like
dinosaurs." stores only the dinosaur clause. Pinned by test.
- Real event-id threading on obi-chat (obi_chat:<id>); content-hash
fallback only on the id-less voice buffer.
- Persona gate inside record_person_facts: GREMLIN/VIXEN refused at the
writer, not by filename.
- Rejection-biased matcher: questions, hedges, conditionals, reported
speech, hyperbole, deictic objects, negated intents all refused; 30-case
false-positive corpus in tests/test_people.py is the authority.
- Commitments expire (3d default / 10d cap, Hobart days), filtered at read
time; corrections supersede, never delete.
Call sites: voice_loop.record_conversation_turn (user text, spark persona)
and api._append_obi_chat_api (obi role, after durable store). people.py and
its invariant tests are blacklisted from px-evolve; conftest gains an
autouse fixture isolating the store from the live robot.
Design: docs/specs/2026-08-25-person-memory-minimal-design.md.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ThqC6Gq4mZyXWnnvGC2a57
Deploying spark with
|
| Latest commit: |
f6a1ff9
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://d8a1055c.spark-e11.pages.dev |
| Branch Preview URL: | https://feat-person-memory-writer.spark-e11.pages.dev |
This was referenced Aug 25, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stage 1 of the minimal person-memory plan: land the writer only. No retrieval, no seeding yet — nothing reads
state/people-*.jsonlin this PR, and a structural test enforces that until the retrieval stage deliberately relaxes it.What this adds
src/pxh/people.py— a deterministic, regex-only writer that records three narrow fact kinds (preference, relationship, commitment) from what Obi literally says, intostate/people-{persona}.jsonl. No LLM anywhere in the write path; provenance kind is the hardcoded literalreport(ceiling 0.9). Design doc:docs/specs/2026-08-25-person-memory-minimal-design.md.Two call sites, both after the source message is durably stored:
api._append_obi_chat_api(roleobionly) — threads the realobi_chatevent idvoice_loop.record_conversation_turn(user text, SPARK persona only) — id-less channel, so aturn:sha1content hash is the honest fallbackPrivacy shape (the point of the review)
mind.pynever opens, so they structurally cannot reach reflection →thoughts-spark.jsonl→/api/v1/public/thoughts→ feed/blog/Bluesky. Pinned by source scans intests/test_people_invariants.py, same pattern astest_policy_invariants.py.likes dinosaurswith evidence"I really like dinosaurs"; "sad"/"school" appear nowhere in the store (test_evidence_is_the_matched_clause_never_the_whole_utterance).record_person_facts, not by filename.tests/test_people.pyis the authority, and patterns may only be widened against it.people.pyandtest_people_invariants.pyadded toclaude_session.BLACKLIST_FILES(mind.py/voice_loop.py are evolvable, so the module deciding whether a bridge exists must not be).Testing
84 tests in
test_people.py+test_people_invariants.py, plustest_conversation_buffer.py(14),test_api.py -k obi(6),test_resident_only_invariant.py+test_policy_invariants.py(102) — all green locally; CI is the gate.🤖 Generated with Claude Code
https://claude.ai/code/session_01ThqC6Gq4mZyXWnnvGC2a57