Design: the fact asset type — durable bundle-level knowledge
Status: accepted (phase 1 implemented) Author: akm Date: 2026-06-20
Problem
akm has no home for durable, mostly-static facts about a user, team, or project. Concretely, users want a bundle to carry things like:
- Personal — name, email, address, timezone, favorite blogs, writing/working style.
- Team — tool stacks, primary languages, CI/CD platform, deployment targets, web addresses.
- Conventions / constitution — coding conventions, architecture principles, lint rules.
- Bundle meta — folder organization, naming conventions, expected frontmatter, the active-projects list.
Today these have nowhere first-class to live:
memoryis episodic — recency-decayed, belief-state ranked, captured ad-hoc viaakm remember. It models "what I observed in a session," not "what is durably true about this bundle."knowledgeis curated reference material — documents an agent reads on demand, not bundle-level identity/conventions injected as context.lessonis awhen_to_usetrigger distilled from feedback.- The
.meta/convention (src/core/asset/stash-meta.ts, see alsodocs/guides/concepts.md) is documentary only: not indexed, not searchable, not ranked, and not surfaced to agents automatically.
Conceptual grounding
The agent-memory literature splits long-term memory three ways (CoALA, Cognitive Architectures for Language Agents, arXiv:2309.02427; echoed by LangMem and Letta/MemGPT). akm already covers two of the three:
| Memory class | "answers" | akm |
|---|---|---|
| Episodic — timestamped events | "what happened" | session (#561) |
| Procedural — executable how-to | "how to do X" | skill, command, workflow |
| Semantic — durable facts/identity | "what is true" | fact (this doc) |
Karpathy's operating-system analogy frames the design: model weights are the CPU/ROM (can't retrain), the context window is scarce RAM, and everything else is disk that must be paged in. A fact is disk-resident knowledge selectively loaded into RAM — neither baked into weights nor permanently pinned in the system prompt. Anthropic's Effective context engineering for AI agents reinforces this: curate aggressively, prefer just-in-time retrieval, and pin only a small high-signal core (cf. CLAUDE.md, kept short).
Design
Type: fact
A first-class, indexed, searchable markdown asset type. Stored under
facts/ in a bundle; ref form fact:<name> (nesting allowed, e.g.
fact:team/tool-stack). Singular name matches akm convention
(skill, memory, lesson, task, session).
<stash>/facts/
personal/identity.md # fact:personal/identity
personal/writing-style.md
team/tool-stack.md
conventions/coding.md # the "constitution"
meta/naming-conventions.md
meta/active-projects.md
One type, categorized — not a type explosion
Rather than separate fact / constitution / profile types, a single
fact type carries a category frontmatter dimension. This keeps akm's
minimalist type set and matches the LangMem "namespace/scope" approach.
Recommended values: personal, team, project, convention, meta.
Normative facts (the "constitution") are stored as facts and phrased as
guidance at injection time.
Frontmatter
---
description: <one-line, indexed for search> # like other markdown types
category: personal|team|project|convention|meta
pinned: false # true → part of the always-injected core (keep this set small)
updated: 2026-06-20
---
description and category are the curation surface; pinned selects the
small always-on core. Additional keys (source, status, …) are accepted
but not required in phase 1.
Retrieval & injection model: pinned core + JIT retrieval
Two tiers, matching the context-engineering guidance:
pinned: truefacts form the small always-injected core (CLAUDE.md style — keep it short). Phase 1 makespinneda real, captured, query- surfaceable property (tag + search hint + ranking boost); phase 2 wires the assembled pinned-core into harness system prompts.- Everything else stays on "disk" and is surfaced through normal
akm search/akm curate/akm show(JIT retrieval). This works the day the type ships — no new retrieval path required.
Ranking
fact gets a high TYPE_BOOST (authoritative, like knowledge), plus a
modest additional boost for pinned facts so the core outranks ordinary
facts on otherwise-equal queries.
Implementation (phase 1)
Following the session/task template, metadata is encoded as tags +
search hints (no new DB columns or StashEntry fields):
| Concern | File |
|---|---|
| Type spec | PLACEMENT_SPECS in src/core/asset/asset-placement.ts — fact: { stashDir: "facts", ...markdownSpec } |
| Renderer + action | TYPE_PRESENTATION in src/core/type-presentation.ts (supersedes the earlier TYPE_TO_RENDERER/ACTION_BUILDERS split) |
| Renderer + metadata | src/output/renderers.ts — factMdRenderer (frontmatter category/pinned parsed inline in buildShowResponse) |
| File classification | src/indexer/walk/matchers.ts — DIR_TYPE_MAP facts/ |
| Ranking | src/indexer/search/ranking-contributors.ts — TYPE_BOOST + pinnedFactRankingContributor |
| Lint | factDiagnostics in src/core/adapter/adapters/akm-lint.ts, called from src/commands/lint/index.ts (warns missing-category on absent/unrecognized category) |
| Authoring hint | src/integrations/agent/prompts.ts — TYPE_HINTS.fact |
| Graph extraction (opt-in) | not currently wired: graphExtractionIncludeTypes (src/core/config/schema/index-config.ts) takes any string, but the runtime consumer's SUPPORTED_GRAPH_EXTRACTION_INCLUDE_TYPES set (src/indexer/graph/graph-extraction.ts) does not include fact — configuring it is a silent no-op today |
| Type union | KNOWN_TYPES in src/core/recognition-util.ts, which includes fact |
fact: refs resolve automatically — the ref resolver derives its type set
from PLACEMENT_SPECS, not KNOWN_TYPES: typeNameFromConceptId
(src/core/asset/resolve-ref.ts) calls typeForStashDir
(src/core/asset/asset-placement.ts), and fact has a PLACEMENT_SPECS
entry (stashDir: "facts"). The ref-resolver contract note in
src/commands/lint/base-linter.ts confirms the same split: refToRelPath is
"DERIVED FROM THE PLACEMENT SPECS ... rather than hand-encoded."
Phase 2 (follow-up, not in this change)
- Assemble the pinned-fact core and inject it into harness system prompts
(
src/integrations/agent/**, per-harness builders). - Optional
akm factCLI surface and a hot-capture path (à laakm remember). - Staleness/conflict handling (
status: active|stale|superseded), since the documented failure mode of fact stores is update, not storage.
Relationship to .meta/
.meta/ stays the human-oriented, non-indexed orientation convention.
fact is its machine-facing sibling: indexed, searchable, rankable, and
(phase 2) agent-injected.