Skip to content

fix(profile): make accepted preferences durable - #9

Merged
happycastle114 merged 2 commits into
mainfrom
feat/profile-memory-quality-v0-6
Aug 6, 2026
Merged

fix(profile): make accepted preferences durable#9
happycastle114 merged 2 commits into
mainfrom
feat/profile-memory-quality-v0-6

Conversation

@happycastle114

Copy link
Copy Markdown
Owner

Summary\n- persist accepted user/style preferences in an atomic local profile cache\n- merge cache entries with active Graphiti facts during profile retrieval\n- keep Graphiti as the primary temporal store while surviving asynchronous 202 processing failures\n- release v0.6.1\n\n## Validation\n- 26 tests passing\n- typecheck passing\n- build passing\n- npm release workflow via v0.6.1 tag

Soungmin and others added 2 commits August 6, 2026 20:56
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
@happycastle114
happycastle114 merged commit 4f37ffe into main Aug 6, 2026
2 checks passed

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a278a04181

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

}
const staticFacts = [...profileFacts].sort((left, right) => left.localeCompare(right));
const cachedFacts = profileCache.read(userGroupId);
const mergedFacts = [...new Set([...cachedFacts, ...staticFacts])].slice(0, CONFIG.maxProfileItems);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Do not resurrect invalidated preferences from the cache

When Graphiti invalidates or expires a previously cached preference—for example, replacing “concise” with “detailed”—the loop correctly filters the stale remote fact, but this merge unconditionally adds its cached text back. Because cache entries have no invalidation or removal mechanism, obsolete instructions remain in every profile and can conflict with their replacements; reconcile cached entries with Graphiti's temporal status before merging them.

Useful? React with 👍 / 👎.

await store.write({ ...current, [groupId]: nextEntries });
},
read(groupId): readonly string[] {
return (store.read()?.[groupId] ?? []).map((entry) => entry.content);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Keep newest cache entries within the profile limit

When a user accumulates more than CONFIG.maxProfileItems preferences (five by default), save appends new entries but this method returns them oldest-first; both clients prepend that array and retain only its first maxProfileItems elements. Consequently, the sixth and every subsequent accepted preference can be saved successfully but never appear in the injected profile until older entries age out of the 20-entry cache, so return the newest entries first or apply the limit from the tail.

Useful? React with 👍 / 👎.


return {
async save(groupId, content): Promise<void> {
const current = store.read() ?? {};

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Serialize the whole cache update transaction

When two profile addMemory operations complete concurrently, both save calls can read the same snapshot here before either queued store.write executes. Although AtomicJsonStore serializes the resulting writes, each write contains a separately computed whole-file snapshot, so the later one discards the earlier preference or another group's update; queue the read/merge/write transaction as one operation to preserve concurrent accepted preferences.

Useful? React with 👍 / 👎.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant