Skip to content

feat(memory): add curated cross-session memory NodeTypes#1207

Merged
silversurfer562 merged 1 commit into
mainfrom
feat/memory-curated-nodetypes
Jul 1, 2026
Merged

feat(memory): add curated cross-session memory NodeTypes#1207
silversurfer562 merged 1 commit into
mainfrom
feat/memory-curated-nodetypes

Conversation

@silversurfer562

Copy link
Copy Markdown
Member

Summary

  • Extends MemoryGraph's NodeType enum with USER_CONTEXT/FEEDBACK/PROJECT_CONTEXT/REFERENCE, mapped 1:1 onto the harness auto-memory's own proven taxonomy (user/feedback/project/reference).
  • These node types represent hand-curated cross-session memory (no workflow origin, no open/resolved lifecycle) — severity stays unset, status is reinterpreted as active/superseded/stale. No schema change.
  • Adds regression guards for the exact pre-fix failure: reloading a saved graph containing one of these types raised ValueError: '<value>' is not a valid NodeType in MemoryGraph._load() -> Node.from_dict() -> NodeType(data["type"]).
  • Recovered from uncommitted work left in a stale worktree — this PR is the landing of that WIP, rebased onto current main and verified.

This is prerequisite infrastructure for a follow-up structured one-shot evaluating whether MemoryGraph can hold curated cross-session memory as well as the harness's file-based auto-memory does.

Test plan

  • pytest tests/unit/memory/test_graph.py tests/unit/memory/test_nodes_coverage_boost.py tests/memory/test_graph.py — 181 passed
  • Pre-commit hooks (black, ruff, bandit, help-template regen) all passed

Extend MemoryGraph's NodeType enum with USER_CONTEXT/FEEDBACK/
PROJECT_CONTEXT/REFERENCE, mapped 1:1 onto the harness auto-memory's
own proven taxonomy (user/feedback/project/reference). These nodes
represent hand-curated memory with no workflow origin and no
open/resolved lifecycle - severity stays unset, status is
reinterpreted as active/superseded/stale.

Regression guards cover the exact pre-fix failure: reloading a saved
graph containing one of these types raised
`ValueError: '<value>' is not a valid NodeType` in
MemoryGraph._load() -> Node.from_dict() -> NodeType(data["type"]).

No schema change - existing fields are reinterpreted, not added.
@vercel

vercel Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
attune-ai.dev Ready Ready Preview, Comment Jul 1, 2026 8:28pm
website Ready Ready Preview, Comment Jul 1, 2026 8:28pm

@codecov

codecov Bot commented Jul 1, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@silversurfer562 silversurfer562 merged commit ad02188 into main Jul 1, 2026
51 of 52 checks passed
@silversurfer562 silversurfer562 deleted the feat/memory-curated-nodetypes branch July 1, 2026 21:41
silversurfer562 added a commit that referenced this pull request Jul 1, 2026
* fix(memory): honor status in add_finding; open friction-log spec

add_finding() never read finding["status"], so every node created
through the real public API silently got status="open" regardless of
what the caller passed - found reviewing PR #1207's curated-memory
NodeTypes (whose whole premise is that these nodes get
active/superseded/stale status). Predates #1207 but falsified its
design for every node written via the real API; the new regression
test asserted .type but not .status, so it didn't catch it.

Also opens docs/specs/memory-nodetype-friction-log/ to track real
dogfood usage of the 4 new NodeTypes and log taxonomy/field friction
separately from implementation bugs like this one.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix(memory): PersonalMemory.query() never actually returned results

query() did `for hit in pipeline.run(...)`, but the installed
attune_rag's RagPipeline.run() returns a RagResult dataclass (not
iterable), so every call raised TypeError, silently swallowed by a
broad except and returned []. This means personal_memory_recall - the
MCP tool surface - has returned zero results unconditionally since
attune_rag's return type changed. Existing mocked tests couldn't catch
this: their fake RagPipeline.run() returned a plain list, the same
wrong shape the code assumed, so mock and code agreed with each other
while both diverged from the real dependency.

Fixed: query() now reads rag_result.citation.hits (CitedSource objects
with .template_path/.category/.score/.excerpt), matching the pattern
already used correctly in workflows/rag_code_gen.py. Updated the 3
affected mocked tests to return the real RagResult/CitedSource shape
instead of a plain list, and added a new regression test that exercises
the real (unmocked) attune_rag dependency end to end - verified it
fails without the fix (TypeError swallowed -> empty result) and passes
with it.

Found via docs/specs/memory-recall-eval/ - a measure-before-build
benchmark (scripts/memory_recall_eval.py) built to answer "does
attune.memory's recall actually work" before investing further in the
subsystem. Post-fix: hit@1/hit@3 both 100% on an 18-query ground-truth
corpus (was 0%/0%). Full writeup + score-distribution caveats in the
spec's decisions.md.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix(scripts): remove unused false_positives variable in recall eval

CodeQL review comment on PR #1208 flagged the dead local.

---------

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
silversurfer562 added a commit that referenced this pull request Jul 1, 2026
…find_similar spot-check (#1210)

First real add_finding() captures through the 4 curated NodeTypes
(PR #1207), written to ~/.attune/memory/curated_graph.json and
receipt-verified from a fresh instance. Logs three friction points
(repo-tracked cwd-relative default path; RELATED_TO direction
defaults dropping the symmetric read; find_similar signature +
threshold=0.5 muting realistic queries) plus the clean fits, and
closes the session-starter's find_similar sanity-check item in the
recall-eval spec (alive, not query()-dead — default tuning issue).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant