APM supports both group chat and private (main session DM) memory management. The two structures are completely independent and do not interfere with each other.
AspectGroup ChatDM (Main Session)Memory pathmemory/groups/{group_name}/``memory/main/Loading5-step progressive (index → P0 → P1 → P2 → P3)3-layer progressive (index → attention → longterm)Index filememory/groups/{group_name}.md``memory/main/index.mdPriority filesattention.md, project.md, experience.md, people.mdattention.md, longterm.mdEntry ruleMust read index first, no subdirectory bypassMust read index firstFlush triggerPer-group flush on /rememFull-session flush on /remem
Key differences:
Agent must follow the layered disclosure protocol when accessing memory in group chats: read the index first, then load layers on demand.
memory/groups/{group_name}.md (index file)memory/groups/{group_name}/memory_search subdirectoriesmemory/groups/group_names.json to a friendly name {group_name} before useStepActionOutput0. Group name mappingCheck memory/groups/group_names.json, convert room ID to {group_name}Friendly name1. Route to indexmemory_get("memory/groups/{group_name}.md")Hard Rules + routing table2. Intent matchingMatch current message to trigger scenarioFile to load3. Explicit loadmemory_get("memory/groups/{group_name}/{file}.md")Actual memory content4. Sub-layer controlLoad only if Step 3 file references conventions/1 sub-file max5. Budget circuit-breakerStop when cumulative tokens exceed memory_budgetStop loading
Only one best-matching file is loaded per session (highest priority wins).
Per group chat (5-step flow):
conventions/ (only if Step 3 referenced it)Per DM session (3-layer flow):
memory/main/index.md (always)When switching group chats, clear old context first, then restart from Step 1
experience.mdattention.md[DEPRECATED] instead of removing)TriggerConditionAction/rememUser manual triggerFull flushPre-compactContext near limitAuto-flush before lossIdle timeout30min inactive, no flushOne-time auto-flush
When a flush is triggered for a session whose memory system has not yet been initialized, the Agent must initialize it based on available information.
ScenarioActionWho does itGroup index missing ( memory/groups/{name}.md)Create from memory/groups/group_names.json + context; ask group "purpose" if first-joinapm_session_start hook skips injection; agent handles per AGENTS.md "First-Join Flow"DM index missing ( memory/main/index.md)Create from MEMORY.md contentapm_session_start hook skips injection; agent handles per AGENTS.md "Every Session"memory/groups/group_names.json missingCreate from existing room id list (if any)Operator (out of band)
Initialization is not overwriting: existing files are never overwritten — only created when completely absent.
⚠️ The
apm_session_starthook handles the read side (skip injection if index missing → defer to agent). The write side (create the index) is always the agent's responsibility. The hook never writes to memory files.
text
memory/groups/
├── {group_name}.md # L0: Entry gate (mandatory read)
└── {group_name}/ # Group memory (direct access forbidden)
├── attention.md # P0: Current focus
├── project.md # P1: Project static info
├── experience.md # P2: Experience / decision log
├── people.md # P3: People profiles
└── conventions/ # L3: Detailed specifications
├── api.md
└── ...
memory/main/ # DM progressive disclosure
├── index.md # L0: DM entry index
├── attention.md # P0: Current tasks, blockers
├── longterm.md # P1: MEMORY.md distilled summary
├── daily-synced.md # P2: Daily notes summary
└── projects/ # P3: Project context
└── {name}.md
PriorityFileContentWhen to LoadP0attention.mdActive tasks, blockersAny work conversationP1project.mdTech stack, architectureTechnical decisionsP2experience.mdLessons learnedSearch hit + appendP3people.mdTeam roles, contactsNeed to find someone
PriorityFileContentWhen to LoadP0memory/main/attention.mdActive tasks, blockersAlways (cold start)P1memory/main/longterm.mdMEMORY.md distilled summaryWhen project context neededP2memory/main/daily-synced.mdDaily notes summaryRecent activity contextP3memory/main/projects/{name}.mdProject-specific deep contextProject-specific questions
DM uses 3-layer progressive loading (not 4-P like group): the index → attention → longterm, with daily-synced and projects/ loaded on-demand per the routing table in
memory/main/index.md.
⚠️
MEMORY.mdis the authoritative long-term memory but must remain lean and stable. All project-level, event-level, and temporary information MUST go elsewhere.
CategoryExampleReasonIdentityAgent self-reference (role, style, principles)Self-reference, stableUser basicsName, timezone, tech stackRarely changesEnvironmentServer host, key DB pathsConstantsProject indexOne-liner per project → filePointer, not contentOther memory entry pointersmemory/main/..., memory/groups/...Routing only
CategoryWhere it goes insteadProject progressmemory/main/projects/{name}.mdEvent logs (incident, fix)memory/YYYY-MM-DD.mdDecisions & lessonsmemory/main/longterm.mdTool lists (iqtree, bcftools...)Runtime which / command -vGroup chat metadatamemory/groups/{name}.mdTemporary fix instructionsDaily notes (auto-rotate)Cross-snapshot result narrativesProject report files
- If a new agent could figure it out from
lsorwhich, don't write it to MEMORY.md.- If it's about a specific project, write to
projects/{name}.md.- If it changes more than once a month, don't put it in MEMORY.md.
- If
MEMORY.mdexceeds 50 lines, it's bloated — extract project details.
text
❌ 95 lines of "long-term memory" with project details
❌ Tool lists that `which` can answer
❌ Temporary fix notes lingering in MEMORY.md a month later
❌ Group names listed directly
❌ Event outcomes with specific samples or measurements
✅ 30-line identity + index
✅ Project index table → projects/{name}.md
✅ Key events → daily notes + longterm index
✅ Group chats → memory/groups/{name}.md
All file templates (MEMORY.md, projects/{name}.md, longterm.md, group index, attention) live in ADDENDUM.md → "File Templates" section. Use them as starting points.
markdown
## Every Session
1. Read `SOUL.md` → USER.md → MEMORY.md
2. **If in MAIN SESSION** (DM):
- Read `memory/main/index.md` — DM progressive index
- Based on routing, load up to 2 files
- Report `last flush: YYYY-MM-DD HH:mm` (from `memory/flush-state.json` — DM-only)
3. Read `memory/YYYY-MM-DD.md` (today + yesterday)
⚠️ The
flush-state.jsonreferenced here is the DM flush state (memory/flush-state.json), NOT the group one. See "Flush State Files" above for the two-file split.
APM uses two separate flush-state files to keep DM and group memory isolated:
FileScopeOwnermemory/flush-state.jsonDM (main session)remem-flush, precompact-remem, apm_session_start (DM path)memory/groups/flush-state.jsonGroup chatremem-flush, precompact-remem, apm_session_start (group path)
This separation is a privacy boundary — group sessions must never read or write the DM flush-state (and vice versa).
memory/flush-state.json (DM)#json
{
"last_flush_time": "2026-06-20T17:20:23+08:00",
"flush_number": 6,
"context_usage_at_flush": null,
"pending_items": [],
"<file>_mtime": "ISO-8601"
}
memory/groups/flush-state.json (group)#Same shape, scoped to one group. Multiple groups each have their own
flush-state under memory/groups/{name}/flush-state.json (when groups
are nested under a parent group name); the top-level
memory/groups/flush-state.json aggregates across all groups.
The
mtimekeys are written byremem-flushandprecompact-rememto enable delta-only flushes. See HOOKS.md → "Shared Conventions".
text
❌ Load all at once → ✅ Load only one at a time
❌ Skip index → ✅ Read index first
❌ Load two groups → ✅ Clear old, reload from Step 1
/remem + Pre-compact + Idleprojects/{name}.mdRun this mental check whenever you touch MEMORY.md:
memory/groups/)?projects/{name}.md file behind them?If any answer is no → extract to proper layer.