fix(memory): close the content gap so agents can actually write and retrieve learnings

- run-selected-prompt.mjs: set SCHEDULER_MEMORY_FILE env var before spawning each agent so
  store.mjs has a well-known path to read from (memory-updates/<ts>__<agent>.md)
- retrieve.mjs: format memories.md using m.summary + date/tags in readable markdown sections
  instead of raw m.content blobs; agents can now scan injected context at a glance
- store.mjs: remove semantically-empty fallback placeholder events that polluted the store
  and degraded retrieval quality; also fix default cadence 'weekly' -> 'daily' to match retrieve
- scheduler-flow.md: replace vague "write insights" instruction with a concrete three-section
  template (Key findings / Patterns / Warnings) and clarify that $SCHEDULER_MEMORY_FILE is
  set by the scheduler automatically

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
This commit is contained in:
thePR0M3TH3ANandClaude Sonnet 4.6 committed 2026-02-20 10:51:03 -05:00
1 parent 87d6b3ef0c
commit 8a9080e7c3
4 files changed
+39 -32

No files matched your search

+8
View File
@@ -67,12 +67,20 @@ async function main() {
}
const runner = resolveRunner(promptMarkdown);
// Compute a stable, per-run memory output file path so agents know where to write learnings.
// store.mjs reads this env var; if absent it falls back to the root memory-update.md placeholder.
const agentName = process.env.SCHEDULER_AGENT || 'unknown-agent';
const runTs = new Date().toISOString().replace(/:/g, '-').replace(/\.\d+Z$/, 'Z');
const memoryFile = `memory-updates/${runTs}__${agentName}.md`;
const child = spawn(runner.command, runner.args, {
cwd: process.cwd(),
env: {
...process.env,
SCHEDULER_PROMPT_PATH: promptPath,
SCHEDULER_PROMPT_MARKDOWN: promptMarkdown,
SCHEDULER_MEMORY_FILE: memoryFile,
},
stdio: 'inherit',
});
+11 -1
View File
@@ -84,7 +84,17 @@ try {
writeFileSync(path.join(sessionDir, 'retrieve.json'), JSON.stringify(artifact, null, 2));
writeFileSync(path.join(sessionDir, 'retrieve.ok'), 'MEMORY_RETRIEVED\n');
writeFileSync(path.join(latestDir, 'retrieve.ok'), 'MEMORY_RETRIEVED\n');
writeFileSync(path.join(latestDir, 'memories.md'), retrieved.map(m => `- ${m.content}`).join('\n'));
// Format retrieved memories as readable markdown sections.
// Prefer m.summary (compact excerpt) over m.content (full blob) for prompt injection.
const memoriesMarkdown = retrieved.length > 0
? retrieved.map((m, i) => {
const date = new Date(m.created_at || m.last_seen || Date.now()).toISOString().split('T')[0];
const tagStr = Array.isArray(m.tags) && m.tags.length > 0 ? m.tags.join(', ') : '';
const text = (m.summary || m.content || '').trim();
return `### ${i + 1}. [${date}]${tagStr ? ` (${tagStr})` : ''}\n\n${text}`;
}).join('\n\n---\n\n')
: '_No memories found for this agent. This may be the first run or the memory store is empty._';
writeFileSync(path.join(latestDir, 'memories.md'), memoriesMarkdown);
// 5. Output success marker to stdout (for scheduler verification)
console.log('MEMORY_RETRIEVED');
+5 -30
View File
@@ -3,7 +3,7 @@ import { mkdirSync, readFileSync, writeFileSync, existsSync } from 'node:fs';
import path from 'node:path';
// Parse environment variables
const fallbackCadence = 'weekly';
const fallbackCadence = 'daily';
const cadence = process.env.SCHEDULER_CADENCE || fallbackCadence;
const agentId = process.env.SCHEDULER_AGENT || `scheduler-memory-${cadence}`;
const promptPath = process.env.SCHEDULER_PROMPT_PATH || '';
@@ -69,34 +69,9 @@ if (memoryContent) {
}
});
} else {
// Fallback if no file provided (preserve existing behavior for compatibility)
console.warn(`No memory input found (checked --file, env SCHEDULER_MEMORY_FILE=${envFile}, or default ${defaultFile}). using fallback placeholder.`);
events = [
{
agent_id: agentId,
content: `Store memory event A for ${cadence} :: ${promptIntent}`,
timestamp: baseTs,
tags: ['scheduler', cadence, 'store'],
metadata: {
session_id: runId,
source: 'scheduler-store',
importance: 0.55,
prompt_path: promptPath
}
},
{
agent_id: agentId,
content: `Store memory event B for ${cadence} :: ${promptIntent}`,
timestamp: baseTs + 1,
tags: ['scheduler', cadence, 'store'],
metadata: {
session_id: runId,
source: 'scheduler-store',
importance: 0.55,
prompt_path: promptPath
}
}
];
// No memory file found — skip ingest rather than storing meaningless placeholder events.
// Placeholder events pollute the store and degrade retrieval quality on future runs.
console.warn(`No memory input found (checked --file, env SCHEDULER_MEMORY_FILE=${envFile}, and default ${defaultFile}). Skipping ingest.`);
}
try {
@@ -120,7 +95,7 @@ try {
promptPath,
promptIntent,
events: events.length,
sourceFile: targetFile || 'fallback'
sourceFile: targetFile || 'none'
},
outputs: {
storedCount: stored.length,
+15 -1
View File
@@ -38,7 +38,21 @@ Every agent prompt invoked by the schedulers (daily/weekly) MUST enforce this co
- **Agent Action**: Review `.scheduler-memory/latest/<cadence>/memories.md` for relevant context.
5. **Store memory after implementation and before completion publish**:
- **Agent Action**: Write any new insights, learnings, or patterns to the file specified by `$SCHEDULER_MEMORY_FILE` (defaulting to `memory-updates/<timestamp>__<agent>.md`).
- **Agent Action**: Write any new insights, learnings, or patterns to the file specified by `$SCHEDULER_MEMORY_FILE`. The scheduler sets this env var to `memory-updates/<timestamp>__<agent>.md` automatically — do not compute a path yourself.
- **What to write** — use this template (keep each bullet to 1-2 lines; omit sections with nothing to say):
```markdown
# Memory Update — <agent-name> — <YYYY-MM-DD>
## Key findings
- <concrete fact discovered this run, e.g. "ESLint v9 requires flat config">
## Patterns / reusable knowledge
- <technique or pattern that will help this agent on its next run>
## Warnings / gotchas
- <env quirk, test failure, or constraint to watch for>
```
- Write only facts that will help the **same agent** on its **next run**. Skip meta-commentary about the task structure.
- Run configured memory storage workflow after prompt execution (for example via `scheduler.memoryPolicyByCadence.<cadence>.storeCommand`)
- Storage command MUST call real memory services (`src/services/memory/index.js#ingestEvents`, which uses ingestor/summarizer pipeline) and MUST emit deterministic marker `MEMORY_STORED`.
- Storage command MUST ingest content from the file at `$SCHEDULER_MEMORY_FILE` (or fallback to `memory-update.md` if present).