xp-update
Write directly, then show the marker snippet. No exploring. Session knowledge only.
Precondition
.xp/db.jsonl must exist. If not → call /xp-index first.
Purpose
The XP-System is a semantic blueprint of the codebase. Each entry describes a labeled symbol so that a new agent, after one grep, understands within 30 seconds what it is, what it's connected to, and what isn't in the code.
Not exception knowledge. Baseline knowledge.
Process
1. What gets entered?
Every symbol that was tagged with an @xp marker in this session, or already
carries one whose note is outdated or incomplete.
An entry answers three questions:
| Question | What belongs in it |
|---|---|
| What is it? | Semantic role, not syntax — what the symbol means in the system |
| What does it connect to? | Direct dependencies, consumers, related concepts in the system |
| What isn't in the code? | Implicit contracts, naming discrepancies, edge cases, traps |
2. Filter
| Check | Result |
|---|---|
Already in db.jsonl? (grep "slug" .xp/db.jsonl) |
Upsert — old line gets replaced |
| Entry outdated / irrelevant? | Actively delete the line (upsert without appending) or omit it |
| Would the note add nothing beyond the readable code? | Add it anyway — semantic role + connections still count |
3. Write the entry (upsert)
Format:
{"id": "domain:slug", "keywords": ["natural-language", "search", "terms"], "note": "Semantic role. Connections + why. Traps or implicit contracts.", "files": ["src/relevant.ts"], "file_hash": "abc123", "updated": "YYYY-MM-DD"}
keywords — natural-language terms from how the task was described, not code
symbols. Goal: grep -i "ordering" hits the entry sequencer:resolve-chain.
At least 3, at most 8 keywords per entry.
Determine the file hash:
git hash-object src/relevant.ts
Upsert — replace existing id, append new ones:
python3 -c "
import json, sys
new = json.loads(sys.argv[1])
lines = [l for l in open('.xp/db.jsonl') if json.loads(l)['id'] != new['id']]
lines.append(json.dumps(new, ensure_ascii=False) + '\n')
open('.xp/db.jsonl', 'w').writelines(lines)
" '<JSON>'
No appending without checking first. No status: obsolete. No duplicates.
The note — density over completeness:
Everything in one or two sentences. Three aspects, no filler.
❌ "Shape is an interface in types.ts" — syntax, not semantics
❌ "Refactored Shape today" — session diary
✅ "Central canvas data structure — stored in the DB as canvas_object (naming discrepancy). ShapeNormalizer must run before every render call, or it crashes. Consumers: RenderEngine, ExportService." — role + connections + trap
✅ "Single entry point for all API errors — handling elsewhere is not caught. Connects to Logger and ResponseBuilder. Naming convention: E_USER_ prefix for user-facing errors." — semantics + topology + contract
4. Split vs. merge
One entry = one precise hit. Cramming multiple facts into one note creates
exactly the noise the XP-System is meant to avoid: someone searching for
_frameAssignments would otherwise also get name-migration context along
with it.
Split when:
- the facts can become true/done independently of each other (one can be finished while the others aren't)
- a user would plausibly search for only one of the facts, not all of them
Merge when:
- the facts are inseparable — neither makes sense without the other
- one fact without the other would be misleading
Cluster term: when several slugs belong to the same effort, add a shared
term to all their keyword arrays (e.g. "frame-labels-rollout"). A broad
search then hits all of them; a precise search hits only one. No depends_on
field needed — the domain prefix in the id (frame-labels:) acts as an
implicit cluster when grepping.
5. Display
Show all new or changed entries.