Skip to content

fix(naming): derive memory name as a real label, not a text truncation#41

Merged
idapixl merged 3 commits into
masterfrom
claude/cortex-engine-issue-40-dsrrx3
Jul 20, 2026
Merged

fix(naming): derive memory name as a real label, not a text truncation#41
idapixl merged 3 commits into
masterfrom
claude/cortex-engine-issue-40-dsrrx3

Conversation

@idapixl

@idapixl idapixl commented Jul 17, 2026

Copy link
Copy Markdown
Collaborator

Summary

Closes #40. A memory's name was derived by truncating its definition, not by generating a concise label. goal_set did a raw mid-word slice(0, 60) with no word boundary and no ellipsis, so names rendered as visibly broken fragments (…verifiable tr); the high-salience observe promotion and the dream create phase each truncated differently, so identical-length memories got differently-shaped names.

This implements the issue's intended behavior: generate a genuine short concept label at mint time, with a solid deterministic fallback, and factor the logic into one shared helper used by all three creation paths.

Changes

  • New src/engines/naming.ts centralising name derivation:
    • deriveName(text, llm) — asks the LLM for a real title-like label at creation time (temperature 0.2, tight token budget), sanitizes the response (strips wrapping quotes, Title: prefixes, trailing punctuation), and never throws — any LLM failure, empty response, or over-long label degrades to the heuristic.
    • deriveNameHeuristic(text) — the deterministic fallback: prefer the first sentence when it fits, otherwise truncate on a word boundary and append an ellipsis so a shortened name reads as deliberately short, not broken mid-word.
  • New label-concept prompt added to the versioned prompt registry (engines/prompts.ts) and its version snapshot test.
  • Wired into all three mint sites: tools/goal.ts (goal_set), tools/observe.ts (novel high-salience promotion), and engines/cognition.ts (dream create phase) now call deriveName(...) instead of a slice(0, 60).
  • Tests: src/engines/naming.test.ts pins the heuristic's shape (word-boundary truncation, ellipsis, first-sentence preference) and the LLM-with-fallback behavior, including the exact issue repro.

Display-only fallbacks in the CLI (wander-cmd.ts, maintain-cmd.ts) are left as-is — they defensively handle legacy un-named memories and are no longer papering over broken names for newly-minted ones.

Testing

  • npm run build — clean.
  • npm test — 25 files, 212 tests pass (16 new).

🤖 Generated with Claude Code


Generated by Claude Code

goal_set, high-salience observe promotion, and the dream create phase
each derived a memory's `name` by slicing its definition. goal_set did a
raw mid-word `slice(0, 60)` with no word boundary and no ellipsis, so
names rendered as broken fragments ("…verifiable tr"); the paths also
named identical-length memories inconsistently.

Add engines/naming.ts centralising naming. deriveName(text, llm) mints a
genuine short concept label via the LLM at creation time (the intended
behaviour), falling back to deriveNameHeuristic(text) — first-sentence
preference, word-boundary truncation, ellipsis on elision — whenever the
LLM is unavailable, errors, or returns nothing usable. Wire it into
goal.ts, observe.ts, and cognition.ts; add the versioned label-concept
prompt and unit tests.

Closes #40

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GXWLLrZdEzJZRPHyy1U8Dv
Copilot AI review requested due to automatic review settings July 17, 2026 22:01

Copilot AI 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.

Pull request overview

Updates memory minting so Memory.name becomes a true short label (LLM-generated with deterministic fallback) instead of inconsistent raw text truncation across creation paths. This aligns goal_set, high-salience observe promotion, and the dream create phase to use a shared naming helper.

Changes:

  • Added a centralized naming helper (deriveName + deriveNameHeuristic) and adopted it in all three memory-creation paths.
  • Added a versioned label-concept prompt to the prompt registry and pinned its version in tests.
  • Added targeted unit tests for the naming heuristic and LLM-fallback behavior; documented the change in CHANGELOG.md.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
src/tools/observe.ts Uses shared deriveName for high-salience novel memory creation.
src/tools/goal.ts Uses shared deriveName for goal_set memory names.
src/engines/prompts.ts Adds the label-concept prompt and registers it.
src/engines/prompts.test.ts Pins the new prompt id/version in the registry version test.
src/engines/naming.ts Introduces centralized naming + sanitization + heuristic fallback logic.
src/engines/naming.test.ts Adds tests covering heuristic truncation shape and LLM fallback/sanitization.
src/engines/cognition.ts Uses shared deriveName in the dream create/promotion path.
CHANGELOG.md Documents the behavioral change for upcoming release notes.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/engines/naming.ts Outdated
Comment thread src/engines/naming.ts Outdated
…dary words

Address review feedback on the naming heuristic:

- Word-boundary truncation no longer drops a full word that ends exactly on
  the cut when the next character is punctuation. The previous
  slice+`replace(/\s+\S*$/, '')` stripped the trailing token unconditionally;
  it now accumulates whole words and ignores trailing punctuation on the
  boundary word when measuring fit.
- Align on a single limit: NAME_MAX_LEN is 60 (matching the label-concept
  prompt and the CLI's 60-char display clip) and the ellipsis is budgeted
  inside it, so a derived name never exceeds 60 chars and a downstream
  `name.slice(0, 60)` can't reintroduce a mid-word cut.

Add a regression test for the punctuation-boundary case.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GXWLLrZdEzJZRPHyy1U8Dv
Comment thread src/engines/naming.ts Fixed
Comment thread src/engines/naming.ts Fixed
…elper

CodeQL flagged the `/[...]+$/` trailing-trim patterns as polynomial-ReDoS:
an unanchored end-anchored `+` over a character class is retried from every
start position, so a long run of matching characters (e.g. many '!' or tabs)
in the exported, caller-supplied text runs in O(n²).

Introduce `trimEndChars(s, re)` — a linear loop that tests one character at a
time from the end — and use it for every trailing trim (first-sentence
punctuation, word-accumulation boundary, base clip, label sanitization).
Leading `^`-anchored and global trims are already linear and left as-is.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GXWLLrZdEzJZRPHyy1U8Dv
@idapixl
idapixl merged commit 7993cc3 into master Jul 20, 2026
8 checks passed
@idapixl
idapixl deleted the claude/cortex-engine-issue-40-dsrrx3 branch July 20, 2026 20:14
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.

Memory name is a raw text truncation, not a label (goal_set cuts mid-word, no ellipsis)

4 participants