Skip to content

v6.0.0: MemoryReviewer embedded prompts never substitute {{DA_NAME}}/{{PRINCIPAL_NAME}} — memory curator runs with literal placeholders #1411

Description

@badosanjos

Summary

ConfigRenderer family (#1204, #1135, #1143 — telegram surface). LIFEOS/TOOLS/MemoryReviewer.ts embeds REVIEWER_SYSTEM_PROMPT and builds its user prompt with literal {{DA_NAME}} / {{PRINCIPAL_NAME}} template variables. Nothing ever renders them (no ConfigRenderer exists in v6), so the memory curator's inference prompt goes out verbatim as:

You are {{DA_NAME}}'s Memory Reviewer — a background process that reads recent
conversation between {{PRINCIPAL_NAME}} and {{DA_NAME}} ...

Confirmed in the persisted run artifacts: MEMORY/OBSERVABILITY/reviewer-runs/<runId>/prompt.system.md and prompt.user.md (conversation turns are labeled {{PRINCIPAL_NAME}}: ... / {{DA_NAME}}: ...).

The values exist in settings.json (daidentity.name, principal.name) but are never injected. Impact: the curator of the hot-layer memory — which is loaded into every turn — operates without knowing either name, degrading extraction quality (e.g., it can't reliably attribute facts to the right actor).

Separate cosmetic nit in the same file: the JSON schema examples hardcode the upstream author's names as Actor enum wire tokens ("daniel" | "kai").

Environment

Ubuntu 24.04, LifeOS v6.0.0.

Fix suggestion (verified locally)

Render at prompt-build time from settings, with neutral fallbacks:

function loadIdentityNames(): { da: string; principal: string } {
  try {
    const raw = JSON.parse(readFileSync(pathJoin(CLAUDE_ROOT, "settings.json"), "utf8"));
    return {
      da: raw?.daidentity?.name?.trim() || "the DA",
      principal: raw?.principal?.name?.trim() || "the principal",
    };
  } catch { return { da: "the DA", principal: "the principal" }; }
}

function renderIdentity(template: string): string {
  const n = loadIdentityNames();
  return template.replaceAll("{{DA_NAME}}", n.da).replaceAll("{{PRINCIPAL_NAME}}", n.principal);
}

Wire tokens "daniel"/"kai" left untouched (they are Actor enum values that MemoryTypes.resolveStoragePath maps to the two memory files, not display names — the rendered prompt explains the token↔name mapping).

Worth a wider audit: any other runtime prompt carrying these placeholders shares the same gap (grep -rn '{{DA_NAME}}' --include='*.ts' across TOOLS/PULSE/hooks). Happy to send a PR.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions