| name | memory-discipline |
| description | This skill should be used before any non-trivial coding action — editing code, writing new files, running migrations, deploying, or answering a question that involves factual claims about this codebase. It forces the agent to consult hoangsa-memory's persistent memory (USER.md, MEMORY.md, LESSONS.md, the indexed code graph) and acknowledge relevant lessons before acting. Trigger phrases: "edit", "refactor", "implement", "fix the bug in", "add a feature", "deploy", "why does this do", "how does X work".
|
| metadata | {"version":"0.1.0"} |
Memory Discipline
You are coding inside a repository that has a hoangsa-memory memory server attached
via MCP. That server gives you four things you MUST use before taking any
load-bearing action:
- Indexed code graph — via
memory_recall (hybrid BM25 + symbol +
vector search over the tree).
- User preferences —
.hoangsa/memory/USER.md, first-person style + workflow
choices that apply across projects.
- Project facts —
.hoangsa/memory/MEMORY.md, durable invariants about this
codebase.
- Reflective lessons —
.hoangsa/memory/LESSONS.md, action-triggered advice.
USER.md + MEMORY.md + LESSONS.md are injected verbatim at SessionStart, so
you already have them in context. The memory_recall hit extends that with
relevant code chunks.
Skipping this loop causes drift. Past sessions spent hours fixing
hallucinated APIs, re-learning patterns already documented in LESSONS.md,
or refactoring against conventions they never checked. Don't repeat them.
The loop
Run this before writing code or asserting a non-obvious fact:
1. Recall
Call memory_recall with a query derived from the user's intent. If the
intent is "add a retry wrapper around the HTTP client", recall
"http client retry". Prefer nouns from the user's request over verbs.
Read the returned chunks. Every chunk has a path:line-span you can cite.
2. Honour USER.md + LESSONS.md
USER.md and LESSONS.md were already injected at SessionStart. Before
acting, scan them for:
- USER.md entries that shape HOW you respond (tone, language, commit
style, testing preferences). Apply them without being asked.
- LESSONS.md triggers that match your planned action. Restate the
relevant lessons before proceeding — if a lesson advises against your
plan, stop and ask the user.
3. Act
Proceed only after honouring preferences + lessons. If you edit code,
quote the recalled chunk ids you relied on.
4. Reflect
After the action completes (tests pass/fail, file saved, command run),
decide what to persist. Three surfaces, three tools:
- Preference (
memory_remember_preference) — first-person, stable
across projects ("user prefers Vietnamese responses", "user runs
make test not cargo test"). Writes to USER.md.
- Fact (
memory_remember_fact) — project-specific invariant
("HTTP retry lives in crates/net/retry.rs"). Writes to MEMORY.md.
- Lesson (
memory_remember_lesson) — action-triggered advice
("when adding a retry → use RetryPolicy, not reqwest middleware").
Writes to LESSONS.md.
Be conservative — only save memory that is specific, durable, and
non-obvious.
Lesson confidence counters are bumped for you: the Stop hook records a
success or failure against every lesson it surfaced that turn. You do not
call anything to make that happen — there is no MCP tool for it.
Handling cap_exceeded
All three remember_* tools return a structured error when the write
would exceed [memory].cap_*_bytes. The error JSON has this shape:
{
"code": "cap_exceeded",
"kind": "fact",
"current_bytes": 13784,
"cap_bytes": 16384,
"attempted_bytes": 14200,
"hint": "Call memory_replace or memory_remove to free space, then retry.",
"preview": [
{"index": 0, "first_line": "...", "bytes": 396, "tags": [...]}
]
}
When you see this, do NOT append to a sibling file or silently drop the
new memory. Instead:
- Read the
preview list. Each entry has index, first_line, and
bytes.
- Pick the entry(s) to consolidate or drop — prefer dropping stale
session-handoff / bare-SHA / outdated entries over real invariants.
- Call
memory_replace { kind, query, new_text } to consolidate
(merges the new memory into an existing entry), or
memory_remove { kind, query } to free space outright.
- Retry the original
remember_* call.
For bulk cleanup of a legacy MEMORY.md / LESSONS.md that accumulated
pre-cap entries, run hoangsa-memory memory migrate --llm from the shell —
classifier triages every entry as keep / move-to-USER.md / drop, then
applies via the same replace/remove verbs.
Anti-hallucination rules
- Never assert a name, signature, or behaviour without a recall hit.
If
memory_recall returns nothing relevant, say so explicitly: "I can't
find that in the indexed code — can you point me at it?"
- Quote chunk ids. Citations look like
[chunk-id] in your answer;
the hoangsa-memory server uses them to validate that you grounded the response.
- Bail on deny. If a LESSONS.md trigger applies and the advice is
"don't do X", and your plan is X, stop and ask the user.
When NOT to run the loop
Skip the loop only for:
- pure conversation (no tool calls),
- read-only questions about files the user explicitly pasted,
- trivial one-line comment or typo fixes.
For everything else, run the loop. It takes ~5 seconds of tool calls and
saves hours of rework.
Configuration
Live knobs in <root>/config.toml:
[curation] (legacy alias: [discipline]):
grounding_check = false — opt-in; adds the memory.grounding_check
prompt to the MCP catalog.
quarantine_failure_ratio = 0.66 / quarantine_min_attempts = 5 —
thresholds the forget pass uses to quarantine bad lessons.
[memory]:
cap_memory_bytes = 16384 — hard cap for MEMORY.md.
cap_user_bytes = 4096 — hard cap for USER.md.
cap_lessons_bytes = 16384 — hard cap for LESSONS.md.
strict_content_policy = false — when true, ephemeral-looking inputs
(session-handoff prose, bare SHAs, date-only entries) are rejected at
the remember_* entry point instead of just warning.
PreToolUse gating is handled by hoangsa-cli hook enforce against
.hoangsa/rules.json and .hoangsa/state/enforcement.events — see the
hoangsa-cli rule docs, not this file.
Duplicate triggers
memory_remember_* writes straight to its target file. The one thing the
server refuses to do is silently overwrite: if a lesson with your
trigger already exists, the call returns an error carrying the existing
advice. Read it and decide — if yours supersedes it, call memory_replace { kind: "lesson", query: "<trigger>", new_text: "<trigger>\n<advice>" };
if it says the same thing, keep what is already there. Do not re-submit
the same trigger expecting it to win.
The dream pass
Consolidation you do not have to drive: when [dream].enabled = true, an
idle daemon hands MEMORY.md / LESSONS.md / USER.md to a model that
merges duplicates, rewrites stale entries, and drops ones the code no
longer supports. [dream].mode = "review" (default) collects proposals in
DREAM.md; "auto" applies them, archiving every dropped entry to
<SURFACE>.dropped.md with its reason. Run it by hand with
hoangsa-memory memory dream [--force] [--dry-run].
Audit log
Every memory mutation lands in .hoangsa/memory/memory-history.jsonl (one JSON
per line) with op, kind, title, actor, reason, and a timestamp.
Ops include: append, replace, remove, quarantine, dream_drop,
dream_merge, dream_rewrite. Read it directly — there is no CLI reader.
The log is size-capped and self-truncates; old entries past the session
window are intentionally shed since reflection debt counts from
.session-start anyway.
Proposing new skills
When you've hit the same pattern in ≥5 lessons, consolidate them into a
reusable skill via memory_skill_propose:
slug: kebab-case directory name.
body: full SKILL.md text starting with ---\nname: ... frontmatter.
source_triggers: the triggers of the lessons being consolidated.
The draft lands at .hoangsa/memory/skills/<slug>.draft/SKILL.md and an entry is
written to the history log. The user promotes the draft via hoangsa-memory skills install .hoangsa/memory/skills/<slug>.draft once they've reviewed it.
Drafts are NOT auto-installed — a human must review before a proposed
skill starts shaping future sessions. This is the main guardrail against
runaway self-modification.
Why the gate exists
Prompts alone are bypassable — a self-confident agent can talk itself
into skipping the recall step. The gate is implemented by
hoangsa-cli hook enforce: it reads .hoangsa/rules.json (block / warn
rules) plus .hoangsa/state/enforcement.events (stateful checks such as
require-memory-impact) and replies {"decision": "block", ...} when a
mutation tool call is missing a prerequisite. The block response tells
you exactly what to call first — obey it instead of retrying blind.