Context Lifecycle Management
The Problem
AI collaborators start every session with zero context. Their effectiveness depends entirely on the quality of the context they receive. For short-lived projects (2-3 sessions), a single context file works. For long-running projects spanning weeks or months, that file grows unboundedly — combining four types of information with fundamentally different lifecycles:
| Information type | Access pattern | Growth pattern | Ideal treatment |
|---|---|---|---|
| Working memory (current state, active tasks) | Every session | Constant | Keep lean, refresh often |
| Episodic memory (session logs) | Rarely after 1 week | Unbounded append | Archive monthly |
| Semantic memory (stable facts, reference) | Most sessions | Slow, update-in-place | Separate file |
| Completed work records | Almost never | Unbounded append | Delete after archiving |
Combining all four in one file means the file grows linearly with session count, with no mechanism for information to leave. This is the classic hot/warm/cold data problem from database engineering, manifesting in AI context management.
The Architecture
Three Tiers
project/
├── CONTEXT.md # Working memory (budget: ≤150 lines)
├── REFERENCE.md # Semantic memory (stable facts, update in place)
├── reference/ # Semantic memory, sharded — once one file is not enough
│ └── <topic>.md # One topic per file; REFERENCE.md becomes its index
├── sessions/ # Episodic memory (archived session logs)
│ └── YYYY-MM.md # Monthly files
└── [other files] # Transcripts, artifacts, etc.
This maps to both cognitive science and systems engineering:
| Human memory | CPU cache | Synthesis equivalent | Properties |
|---|---|---|---|
| Working memory | L1 cache | CONTEXT.md | Small capacity, constantly refreshed, always loaded |
| Semantic memory | L2 cache | REFERENCE.md | Facts and relationships, updated in place, loaded on demand |
| Episodic memory | L3 cache | sessions/ | Chronological events, append-only, searched when needed |
| Procedural memory | Firmware | CLAUDE.md / AGENTS.md + lessons/ | How to do things, rules, patterns |
These are design principles, not metaphors. Each memory type has different storage, retrieval, and maintenance characteristics.
For cross-agent work, the project context files are the durable memory layer. Chat history, model memory, and compaction summaries may help within one tool, but they are not the source of truth. Claude Code, Codex, Cursor, or another capable agent should be able to resume from the same CONTEXT.md, REFERENCE.md, and sessions/ archive.
CONTEXT.md — Working Memory
Purpose: Everything the AI collaborator needs to be effective in THIS session.
Budget: ≤150 lines (hard). For completed projects: ≤80 lines.
Contains ONLY:
- Phase/status header (~5 lines)
- Current state (~15 lines)
- Active tasks with priorities (~50 lines)
- Recent session summaries — last 1-2 only (~30 lines)
- Links to REFERENCE.md and sessions/ (~5 lines)
- Budget footer (~2 lines)
Does NOT contain:
- Completed task checklists (archive to sessions/ first, verify, then remove)
- Session logs older than 1 week (move to sessions/)
- Stable reference facts (live in REFERENCE.md)
- Detailed historical narrative (live in session archive)
- Per-session agent provenance (attribution lines live in sessions/; at most a short
(via Codex)-style tag in the status header when agent identity changes how to interpret state — see Agent Attribution)
Template — new project:
# [Project Name] — Working Context
**Phase:** Initial
**Status:** [description]
**Last session:** YYYY-MM-DD
---
## Current State
[What exists, what doesn't, starting conditions]
## What's Next
1. [ ] [First task]
2. [ ] [Second task]
---
*This file follows the Tiered Context Architecture. Budget: ≤150 lines.*
Template — mature project:
# [Project Name] — Working Context
**Phase:** [Current phase]
**Status:** [Active/Paused]
**Last session:** YYYY-MM-DD
For stable reference facts: see [REFERENCE.md](REFERENCE.md)
For session history: see [sessions/](sessions/)
---
## Current State
- **Production:** [version, deployment status]
- **Blockers:** [if any]
*State as of: YYYY-MM-DD (round N)* ← as-of marker; see Editing below
## What's Next — Prioritized
**High:**
1. [ ] [Task with context]
**Medium:**
2. [ ] [Task]
**Deferred:**
3. [ ] [Task — reason for deferral]
## Recent Session: YYYY-MM-DD
[Summary: what was done, decisions made, outcomes]
---
*This file follows the Tiered Context Architecture. Budget: ≤150 lines.*
Template — completed project:
# [Project Name] — Context
**Status:** Completed
**Completed:** YYYY-MM-DD
**Outcome:** [1-2 sentence summary]
---
## Summary
[What was built/accomplished, 5-10 lines]
## Key Decisions
[Notable decisions that might matter if revisited, 5-10 lines]
---
*Completed project. For historical sessions, see [sessions/](sessions/).*
REFERENCE.md — Semantic Memory
Purpose: Stable facts that don't change session-to-session.
Budget: ≤300 lines (soft) for a bounded project. Exceeding it signals the scope may be too broad — the right response is usually to split the project or move narrative into sessions/.
For a standing project, that reading is wrong, and saying it anyway produces advice nobody can take. A project declared bounded: false in index.yaml — an operations seat, a standing stewardship — exists precisely to accumulate durable operating knowledge. Its reference has no natural ceiling, and "your scope is too broad" is not a defect report about a seat, it is a description of what a seat is. See Sharding the semantic tier below.
Contains:
- Project overview and goals (if not obvious from name)
- Team roster with roles
- URLs, repos, remotes, deployment configuration
- Architecture decisions and conventions
- File indexes (transcript logs, artifact locations)
- Setup and cleanup instructions
Key property: Update IN PLACE, not append. When a team member leaves, update the roster — do not add a dated note. When a URL changes, change the URL. This is a living reference document, not a log.
Template:
# [Project Name] — Reference
Stable facts for this project. Updated in place when facts change.
---
## Quick Reference
| Resource | Location |
|----------|----------|
| [Key URL] | [value] |
| [Key command] | [value] |
## Team
| Name | Role | Notes |
|------|------|-------|
| [Name] | [Role] | [Status] |
## Architecture
[Key decisions, conventions, patterns]
## Related Files
[Index of transcripts, artifacts, external documents]
Sharding the semantic tier — reference/
The episodic tier solved unbounded growth years ago: sessions/ is a directory, and no single file has to hold every session. The semantic tier never got that treatment. REFERENCE.md was a single file with a soft cap and no overflow mechanism, which is fine for a bounded arc whose scope really is limited, and structurally broken for a standing project whose whole function is accumulating operating knowledge.
reference/ is the same move, one tier over.
When to shard. A bounded project should not: hitting 300 lines is real information about its scope. A standing project shards when one file stops being readable — in practice around the same 300 lines.
What changes when you do. REFERENCE.md stops being the content and becomes the index over it:
# [Project] — Reference
Stable facts, sharded by topic. Each entry links one file in `reference/`.
| Topic | What lives there |
|---|---|
| [People and roles](reference/people.md) | roster, reporting lines, who owns what |
| [Tooling and auth](reference/tooling.md) | CLIs, credentials posture, known limitations |
| [Routing](reference/routing.md) | what this seat owns and where work goes |
Budgets after sharding. The index is working-memory-shaped and held to ≤150 lines; each reference/<topic>.md gets the old ≤300. The scope signal moves from one line count to the number of topics — which is the honest measure for a standing project anyway.
The invariant that keeps sharding safe: every topic file is linked from the index. A topic nothing points at is unreachable from session start, which makes sharding a way to lose content rather than organise it. The doctor reports an unlinked topic (reference-index-orphan) and a reference/ with no index at all (reference-index-missing, a defect).
Migration is not required. A project under the budget keeps one REFERENCE.md and nothing changes. bounded defaults to true when unset, so projects that never declare themselves standing behave exactly as before.
The whole vocabulary, so a finding is a remedy and not just a string. A report names the check that fired; a name absent from this skill leaves its reader nothing to do.
| Check | Fires when | Severity |
|---|---|---|
reference-budget |
a bounded project's REFERENCE.md is over 300 lines |
warning |
reference-shard |
a standing project's REFERENCE.md is over 300 lines — outgrown one file, not overbroad in scope |
warning |
reference-index-budget |
once sharded, REFERENCE.md is over 150 lines — the index has started holding content again |
warning |
reference-topic-budget |
a reference/<topic>.md is over 300 lines |
warning |
reference-index-orphan |
a topic file is not linked from the index | warning |
reference-index-missing |
reference/ exists with no REFERENCE.md index at all |
defect |
sessions/ — Episodic Archive
Purpose: Historical record of what happened and when. Rarely read, but searchable when historical context is needed.
Organization: Monthly files named YYYY-MM.md.
Template:
# Session Archive — [Month] [Year]
Archived from CONTEXT.md on YYYY-MM-DD. See REFERENCE.md for stable project facts.
---
### YYYY-MM-DD: [Session title — what was accomplished]
[Summary: 5-15 lines per session. What was done, decisions made, outcomes.]
*Attribution — agent: … · model: … · effort: … · scope: … · verified: … · ref: …* ← optional; see Agent Attribution
Agent Attribution — recording which agent did what
Multiple agents can write to the same project files — Claude Code, Codex, Cursor, subagents, or the same tool at different model/effort settings — and git authorship often cannot distinguish them: different tools commonly commit under the same human author identity, and Co-Authored-By trailers are authored claims, not harness-verified facts. When agent provenance would help future work, record it explicitly.
When to attribute. Only when it helps future work: cross-agent handoffs; sessions where an agent's tool or capability gap shaped the scope; multi-model or subagent contributions; work whose verification status a future reader must trust or re-check. Routine sessions in a single-agent project need no attribution line. This is provenance, not telemetry — never log every edit, and never let attribution bloat CONTEXT.md.
Format. One italic line at the end of the session entry in sessions/YYYY-MM.md, one line per materially-contributing agent:
*Attribution — agent: <app/tool> · model: <version string or unknown> · effort: <setting or unknown> · scope: <what this agent did> · verified: <checks actually run, or none> · ref: <commit hash / artifact path or unknown>*
Field rules:
- agent — the app or tool:
Claude Code,Codex CLI,Cursor,Claude Code subagent (Explore). - model — the exact model/version string, ONLY if the current session or the user explicitly provides it (e.g., the session's own environment states it). Otherwise the literal word
unknown. - effort — reasoning-effort or mode setting (
max,high,default) when explicitly known; otherwiseunknown. - scope — what this agent contributed to this entry, one clause.
- verified — the verification actually performed (
plan re-run to zero,tests green,none). Never claim a check that did not run. - ref — durable pointer: commit hash or
resources/artifacts/path;unknownif none exists yet.
Unknown means unknown. Never infer model/effort from memory, prior sessions, vibes, or git trailers. A wrong provenance claim is worse than an explicit unknown.
Never record secrets. No token values, OAuth or callback URLs, credential material, or private config values in any attribution field.
Placement by tier:
sessions/YYYY-MM.md— the home for attribution lines (episodic, append-only).CONTEXT.md— at most a short parenthetical tag —(via Codex)— in the status/Last-session line, and only when agent identity changes how to interpret state. Never full attribution lines.REFERENCE.md— no per-session provenance. Stable agent facts only (e.g., "Codex sessions lack the Gmail connector; scope sweeps accordingly"), updated in place and removed when no longer true.resources/artifacts/— a substantial standalone artifact MAY open with a short Provenance block (agent / model / effort / date / verification / commit) when it will outlive its session entry.
Cache-vs-truth still applies. An attribution line is a claim recorded at write time by the writing agent. When provenance matters downstream, re-verify against git log and the artifact itself rather than trusting the line.
Examples.
Routine single-agent session (line optional; include once a project becomes multi-agent):
*Attribution — agent: Claude Code · model: claude-fable-5 · effort: unknown · scope: full sweep + session log · verified: plan re-run to zero · ref: a1b2c3d*
Cross-agent handoff (each agent's entry carries its own line; a capability gap that shaped scope belongs in scope):
*Attribution — agent: Codex CLI · model: unknown · effort: unknown · scope: single-stack sweep only (session lacked the Gmail connector) · verified: plan re-run to zero · ref: d4e5f6a*
Multi-model / subagent work (one line per contributor under the orchestrating entry):
*Attribution — agent: Claude Code · model: claude-fable-5 · effort: max · scope: orchestration + final review · verified: acceptance audit of subagent output · ref: b7c8d9e*
*Attribution — agent: Claude Code subagent (Explore) · model: unknown · effort: unknown · scope: repo-wide call-site inventory · verified: none (inventory only) · ref: resources/artifacts/2026-07-05-call-sites.md*
Session Start Protocol — MANDATORY before substantive project work
The tiered architecture (CONTEXT.md / REFERENCE.md / sessions/) is only useful if the agent reads it. LLMs default to working from in-context memory; rules at session start lose salience as conversation grows. The Session Start Protocol makes the read explicit and non-skippable.
When you begin substantive work on any project — at client session start, when
the user first mentions it, or after switching from another project — run these
steps in order before substantive action. First resolve this conversation's
established project through its Git-tracked registry with the installed
project-management resolver, --no-fetch --no-coordination-refresh and
GIT_OPTIONAL_LOCKS=0; no automatic fast-forward. A global pointer cannot
override the conversation's project. CONFLICT/FAIL/UNKNOWN stops dependent
project reads and writes, including generated-state build and migration.
- Verify current time. Run
date "+%Y-%m-%d %H:%M:%S %Z (%A)". The model has no clock; the OS does. Use the output as your authoritative "today" anchor for the rest of the session. The harness may have injected a date earlier, but that injection drifts;datedoes not. - Inspect Git publication and working state. Run
git log -10 --pretty=format:"%h %ai %ci %s" -- <project-path>andgit status --short -- <project-path>. These establish commit author/committer times and pending changes. Commit times do not define session workdays; delayed publication, overnight work and timezone boundaries may put them on different dates. - Read CONTEXT.md. Read the full working-memory file. Treat its "Last session" header as a claim to compare with dated session records and current source evidence. A commit-date difference alone does not make it stale, and matching dates alone do not establish semantic currency.
- Read the latest dated entries in sessions/YYYY-MM.md. Locate the newest recorded workday across the archive, not just the file's final heading. Compare the narrative with CONTEXT.md, structured state where present, and index metadata. Report older or newer cache dates with both sources; recover missing, invalid or unreadable evidence without inventing a session date from Git. When archiving older work, retain its verified workday and label the current recording time separately. A repair done today may have its own entry; it must not rewrite the earlier workday to match its commit.
- Skim REFERENCE.md if you have not recently. This is the project's stable facts and design spec. Full read on the first session resumption of the day; quick skim of section headers otherwise.
- Name this session after the project where the client allows it. When the client exposes session/thread renaming, set this conversation's own title to the project's id (e.g.
synthesis-ecosystem-engineering) so the thread list reads as the work queue. Claude Code: MCP toolmcp__ccd_session_mgmt__set_session_titlewith{session_id: "self", title: "<project-id>"}(verified 2026-09-21; a user-set title asks approval first, auto-generated titles replace silently). No verified equivalent on other clients as of that date — check yours and apply the same convention if one exists; if not, this step does not apply. - Only then begin substantive work.
When synthesis-agent-conformance is available and this session has an accepted
owner claim, record the local active-project pointer after verification.
Report-only refreshes and contributors do not activate the canonical pointer:
python3 <skill-root>/scripts/conformance.py activate \
--project <project-directory> --session-id <coordination-session-id>
The pointer accelerates SessionStart and PostCompact recovery. It is a cache; the project files and git history remain authoritative.
Seamless client and computer switching
Rajiv never has to run this protocol or save state manually. The working agent owns the checkpoint before it yields. A normal stopped-task transition is:
- the agent updates
CONTEXT.md, stableREFERENCE.mdfacts, the current session log, and the controlling plan as the work changes; - the client adapter records the exact context paths changed by that session;
- Stop hashes the attributed files into a local receipt without committing or
using the network; an interruption before Stop leaves the manifest plus Git
working-tree state as
LOCAL_RECOVERABLEevidence; - an explicit remote handoff or day-end publishes source work under repository policy and batches exact private-context paths before the destination computer fast-forwards; and
- when Rajiv names the project, the receiving client resolves it through the
git-tracked
projects/index.yamland runs this Session Start Protocol automatically.
A valid live pointer accelerates the same-client case. Its absence after claim release is normal and must not block recovery from the durable record. A single global durable "current project" marker is prohibited because independent Claude Code and Codex tasks may own different projects simultaneously.
The guarantee has explicit boundaries: do not switch while a task is still running; an offline origin can preserve a local checkpoint but cannot make it available on another computer; divergence, a behind checkout, missing authentication, and overlapping claims must surface visibly instead of being called seamless.
Why this order matters. Steps 1 and 2 establish ground truth from external sources (OS clock, git). Step 3 reads the cache. Step 4 reads the most recent narrative. The order means by the time you act, you have verified facts AND the project's own framing — and you have noticed any discrepancy between them.
Visible to the user. Show the verification step in your first response of the session. Example:
Session start verified. Today: 2026-05-27 10:49 EDT (Wednesday). Last project commit: 2026-05-26 12:47 EDT (
51b8e6d, "Maintain context: refresh inbox-cleanup CONTEXT.md"). CONTEXT.md matches git log. Proceeding with [next task].
The visible verification is the L4 cross-tool drift-detection mechanism — the user must be able to see that ground truth was checked.
Mid-Session Refresh Protocol — MANDATORY under drift conditions
Long conversations cause context drift. The mid-session refresh protocol re-syncs the agent against ground truth without requiring a full restart.
Mandatory triggers. Re-run the Session Start Protocol (or invoke the synthesis-checkpoint skill, which is the codified version of these steps) under ANY of these conditions:
- Before any time-interval claim in output. "Yesterday", "N days ago", "last session", "this week", "earlier today" — verify the clock and the dated evidence for the event BEFORE generating the claim. Use
git logfor commit intervals; use session records for session dates, and do not infer an exact elapsed interval from a date-only entry. After-the-fact correction is more expensive than upfront verification. - After a long real-time pause. If
datereveals more than 1 hour has passed since you last checked, re-read CONTEXT.md and re-rungit log. Long pauses correlate with the user resuming after a break — the world may have changed. - After ~25 substantive tool calls since the last refresh. This is the unconditional cadence: even with no drift signal, re-read CONTEXT.md and
git logto verify your accumulated context still matches disk. - On any drift signal:
- You say or think "I don't recall" about a recent decision
- A file read returns content you didn't expect
- The user references a decision you have no record of
- The user corrects you ("that's not right", "actually...", "you said earlier...")
- You notice the conversation has touched many topics and feel uncertain about project state
- Before writing to a session-log file (a markdown file under
sessions/). The date you write into the header MUST be fromdate, not from memory. - Before generating a commit message that mentions dates or intervals. The interval claim must be backed by
git log.
The protocol itself. Run the steps from synthesis-checkpoint (preferred if loaded), or as a fallback the same steps inline:
date "+%Y-%m-%d %H:%M:%S %Z (%A)"— verify current timegit log -10 --pretty=format:"%h %ai %s" -- <project-path>— verify project history- Re-read CONTEXT.md from disk
- Re-read the latest sessions/YYYY-MM.md entry
- Reconcile: where does in-context memory disagree with disk/git? Report the discrepancy in the next response.
- If CONTEXT.md is stale, update it and preserve session-attributed local evidence. Publish it during explicit remote handoff or day-end.
Compaction detection signals. Context-window compaction (the harness summarizing older turns) is opaque — you cannot reliably detect when it happened. Treat these as red flags suggesting compaction may have occurred:
- You suddenly cannot recall the user's stated goal for the session
- A task you remember as in-progress has unclear next steps
- Tool outputs reference files or decisions you have no context for
- Your last few tool calls feel disconnected from the current request
When any of these fire, run the Mid-Session Refresh Protocol unconditionally.
Delegation. When the synthesis-checkpoint skill is available, prefer invoking it — it is the canonical codification of this protocol, runs the same steps every time, and produces consistent visible output the user can spot. Use the inline fallback only when synthesis-checkpoint is not loaded.
Editing a Durable Context File — MANDATORY for scripted edits
A scripted edit to CONTEXT.md, REFERENCE.md, or a session log is an
assertion that a specific change was made. A bare str.replace() asserts
nothing: when an anchor no longer matches — because another agent legitimately
rewrote that region between sessions — the replacement silently becomes a
no-op while the surrounding "updated" message stays cheerful and false. The
result is committed, and record-versus-git checks still pass, because the file
is committed. It is simply not current.
Never hand-roll replacement logic against a durable context file. Use
scripts/context_edit.py, which fails closed:
python3 scripts/context_edit.py set-field --file CONTEXT.md \
--field Phase --value "Round 3 complete"
python3 scripts/context_edit.py replace --file CONTEXT.md \
--anchor "$OLD" --replacement "$NEW" [--count N] [--max-lines 150]
python3 scripts/context_edit.py insert-before --file sessions/2026-08.md \
--anchor "## 2026-08-20" --text "$NEW_ENTRY"
python3 scripts/context_edit.py delete-line --file REFERENCE.md \
--anchor "| Completed item | Done |"
It refuses, without writing, when the anchor is absent, when it matches a
different number of times than declared, when the replacement would leave the
file byte-identical, when the result would exceed a stated line budget, or when
the target is a symlink. It writes atomically and then re-reads the file to
confirm the change is actually on disk. There is no flag that makes a missing
anchor succeed. --dry-run previews without writing and still refuses a bad
anchor. Import replace_once, set_field or delete_line to use it from Python.
Line boundaries are protected too. insert-before requires an anchor at the
start of a line and text ending with a real newline. replace refuses an edit
whose outer boundary would fuse surviving lines, including repeated adjacent
matches. Whole-line deletions and deliberate restructuring inside the anchor
remain valid. To change a boundary intentionally, include the neighboring line
in both anchor and replacement. The helper preserves LF and CRLF bytes; it
does not insert or normalize separators to make an unsafe edit pass.
Use delete-line to remove one exact, unique physical line, including its
existing LF or CRLF ending; the anchor is the full line text without that ending.
Partial and ambiguous matches refuse. Markdown pipe tables remain a unit:
ordinary replacements cannot leave a blank or non-row line inside a table,
remove its header or delimiter while retaining its rows, or cross a table
boundary. Valid cell edits and complete data-row deletions pass. To replace
or remove an entire table deliberately, name the complete table in the anchor.
Fenced examples are not live tables. Archive durable facts before deleting them.
The helper also refuses to create a stale header: an edit that leaves
**Phase:** ahead of **Last session:** in the same ordinal family (round,
wave, phase, step, part) is refused with both fields named —
--allow-header-lag records an explicit override. Update Last session
first or in the same change; it may lead Phase mid-update. Independently,
the context doctor fails a project whose header describes an older state than
its own session log (header-currency), including same-day staleness where
date comparison sees nothing. Each field is judged separately, so a fresh
Phase cannot mask a stale Last session.
Body currency. Header freshness is necessary, not sufficient: three
real defects advanced the header while Current State kept routing agents to
superseded work — and a current header above stale operational sections is a
stronger false receipt than an obviously stale file. Operational sections
(## Current State, ## What's Next) therefore end with an as-of marker:
*State as of: 2026-08-24 (round 14)*
The marker converts prose currency into the structured comparison the header
already gets. With it in place: the doctor fails a section whose marker lags
the session log (body-currency); context_edit.py refuses a header advance
that leaves a marker behind (--allow-stale-body records an override); and
advancing a marker while its section's prose is byte-identical requires
--state-reviewed, which records the assertion that the section was re-read
and still holds — a silent bump would recreate the header defect one level
down. Markerless records are reported as unverifiable, never as clean.
The completion signal is deliberately honest: every gated edit's success line
names the body state (as-of markers current, body lags, or body currency unverifiable). A tool that mechanizes the easy half of a task and prints
unqualified success for it manufactures a completion signal for partial work
— that mechanism-shaped failure caused all three real occurrences, and the
signal is the part of this design that addresses it.
Two companion rules, because the tool cannot enforce them alone:
- Re-read before editing. When re-taking a claim on a project another
agent may have touched, read the current file and build anchors from what it
says now — never from strings you remember writing. Alternating agents on
one
CONTEXT.mdis a standing pattern in cross-agent work, not an accident. - Never report success you did not verify. A message saying a record was updated is a claim about your own action, and nothing else in the system checks it.
The Archival Protocol
When to Archive
Archive when ANY of these conditions are true:
- CONTEXT.md exceeds 120 lines (approaching 150-line budget)
- Session logs in CONTEXT.md are older than 1 week
- A project phase transition occurs
- The user explicitly requests cleanup
Step by Step
- Read CONTEXT.md and count lines.
- Identify cold content:
- Completed task items
- Session summaries older than 1 week
- Stable facts that belong in REFERENCE.md
- Detailed narratives that belong in sessions/
- Create files if needed:
- REFERENCE.md (if stable facts exist and no REFERENCE.md yet)
- sessions/ directory
- sessions/YYYY-MM.md for the relevant month
- Archive FIRST (two-phase commit — write to destination before removing from source):
- Session logs → sessions/YYYY-MM.md (append chronologically)
- Stable facts → REFERENCE.md (organize by category)
- Completed tasks → sessions/YYYY-MM.md (summarize, then remove from CONTEXT.md)
- Verify archives exist — Confirm moved content is present in its destination file.
- Only then rewrite CONTEXT.md with archived content removed.
- Verify:
- CONTEXT.md ≤150 lines
- No information lost (everything archived before removal)
- Cross-references updated (CONTEXT.md points to REFERENCE.md and sessions/)
- Record local readiness. The client edit hook attributes the changed files automatically. Commit and push during an explicit remote handoff or day-end, scoped to the exact files and repository policy.
CRITICAL: Archive FIRST, then delete. NEVER delete content from CONTEXT.md before confirming it exists in sessions/ or REFERENCE.md. Two-phase commit: write to destination, verify, then remove from source.
ALSO CRITICAL: local continuity and remote readiness are different states. Do not create a network commit after every context edit. Keep the local tiers current, and publish them through explicit remote handoff or day-end.
Decision Tree: Where Does This Content Belong?
Is this information needed for TODAY's work?
├── Yes → CONTEXT.md
└── No
├── Is it a stable fact (team, URL, architecture)?
│ ├── Yes → REFERENCE.md (update in place)
│ └── No
│ ├── Is it a record of what happened during a session?
│ │ ├── Yes → sessions/YYYY-MM.md
│ │ └── No
│ │ └── Is it a reusable lesson?
│ │ ├── Yes → lessons/
│ │ └── No → delete it
└── Exception: completed milestones (≤10 lines) stay in CONTEXT.md
Migration Guide
For Projects Over 500 Lines
Full restructuring. Do NOT mechanically split — each project needs judgment about what is working memory vs reference vs archive.
- Read the entire CONTEXT.md
- Identify the four content types
- Create REFERENCE.md with semantic content
- Create sessions/ with episodic content (grouped by month)
- Rewrite CONTEXT.md as fresh working memory
- Verify nothing was lost
For Projects 150-500 Lines
Moderate restructuring:
- Extract obvious semantic content (team, URLs, architecture) → REFERENCE.md
- Move session logs → sessions/
- Tighten CONTEXT.md to ≤150 lines
For Projects Under 150 Lines
Lightweight touch:
- Add budget footer
- If >20 lines of reference material exist, consider extracting to REFERENCE.md
- If completed, simplify to completion summary format
Project Status Transitions
| Transition | CONTEXT.md Action | Other Actions |
|---|---|---|
| active → completed | Rewrite as completion summary (≤80 lines) | Simplify REFERENCE.md |
| active → paused | Add "Paused State" header with reason | Archive session logs |
| paused → active | Remove "Paused State" header, refresh | Update last_session |
| completed → archived | Freeze all files | Set status in index.yaml |
| active → spawned | Remove spawned scope | Create new project |
Project Spawning
When a sub-scope exceeds the parent project's boundaries:
- Create new project directory
- Seed CONTEXT.md with fresh working memory (not a copy)
- Add to index.yaml with
related:linking to parent - Remove spawned scope from parent's CONTEXT.md
- Cross-reference both projects
The test: Would a new team member reading only the parent's CONTEXT.md be confused by the spawned work? If yes, spawn it.
Repo Families and Deletion Units
The three tiers describe how a project's context is structured. One level up sits a different question: which repository may a piece of context live in at all? For anyone whose work spans multiple professional relationships — clients, employers, partnerships — the durable-memory layer divides into two families with fundamentally different lifecycles:
- The permanent knowledge root. The person's own long-lived knowledge base — their projects, lessons, daily plans, accumulated career record. It survives every professional relationship and is never deleted wholesale.
- Per-engagement private repos. Workspace-scoped context repositories created for one client, employer, or engagement. Each one is a deletion unit: if the counterparty exercises a delete-my-data request — at contract end, under a nondisclosure obligation, during offboarding — the repo is deleted or returned as a unit. The repo boundary is what makes the promise keepable. Design for that day from the first commit.
The routing test
Before writing engagement-adjacent content into any repository, ask: would this survive the relationship's end?
- Material the counterparty could rightfully ask to have deleted — information they shared in confidence, their internal discussions, work products they own, context learned inside their walls — routes to the engagement repo, the deletion unit.
- The person's own permanent record routes to the permanent root.
The test is about the content's rightful owner and lifecycle, not about where the content happened to arrive or which window was open when it was learned.
Both misplacement directions fail — asymmetrically
Engagement material in the permanent root is a compliance failure. When the deletion request comes, the misplaced material silently survives a deletion the person promised — or is legally bound — to perform. Nothing in the permanent root's lifecycle will ever remove it, and honoring the request now requires hunting down every stray copy, which is exactly the manual process repo-level deletion units exist to make unnecessary. The failure is against someone else, and it is discovered (if ever) by the counterparty.
Permanent material in an engagement repo is self-inflicted loss. When the deletion unit is deleted — correctly, on request — the person's own records are destroyed along with the counterparty's data: records they were entitled to keep and may one day need. Recovery is impossible precisely because the deletion was performed properly.
Neither direction is curable after deletion day. That is why routing happens at write time, not at cleanup time.
The ALWAYS-PRESERVE class
Some records concern an engagement but belong to the person: they document the person's own side of the professional relationship, and a counterparty's delete-my-data request does not reach them. The generic class:
- contracts and signed agreements
- pay, equity, and benefits records
- hiring and negotiation correspondence
- termination and separation records
- performance reviews, given and received
- IP assignments and licensing grants
- evidence relevant to an actual or foreseeable dispute
ALWAYS-PRESERVE material routes to the permanent root always — even when it arrives through engagement channels, even mid-engagement, even when the surrounding conversation is otherwise engagement-confidential. A copy may exist inside the deletion unit for working convenience; the canonical record may never live only there, because the deletion unit's lifecycle would take it.
Inventories count; they never itemize
When an inventory of one repository is produced for any audience beyond its owner — a deletion attestation, an offboarding report, a migration plan — items outside the inventory's scope are counted, never itemized. An identifier plus a descriptive title is already a disclosure of the item's existence and subject. "Four items out of scope for this inventory" conveys completeness; a filename-and-title listing of out-of-scope material leaks the very content the repo boundary protects.
Instance specifics live in private configuration
This section is the mechanism. Which repositories are deletion units, which root is permanent, and any additions to the preserve class are facts about one person's setup — declared in that person's private agent instructions or configuration, never in this public skill. An agent applying the mechanism reads the instance declarations first, and asks rather than guesses when a repository's family is undeclared.
Measuring Context Quality
Quantitative
| Metric | Target |
|---|---|
| CONTEXT.md line count | ≤150 (active) / ≤80 (completed) |
| REFERENCE.md line count | ≤300 (bounded projects); standing projects shard into reference/ |
| REFERENCE.md as index, once sharded | ≤150 |
reference/<topic>.md line count |
≤300 each |
Unlinked files in reference/ |
0 |
| Stale session logs (>1 week old in CONTEXT.md) | 0 |
| Completed tasks remaining in CONTEXT.md | 0 |
| Budget footer present | Yes |
Qualitative
After reading CONTEXT.md, the AI collaborator should be able to answer:
- What is the current state of this project?
- What should I work on next?
- What was done in the last session?
- Where do I find stable reference information?
If any question cannot be answered from CONTEXT.md alone (with a pointer to REFERENCE.md), the working memory is incomplete.
Executable Working State — resources/scripts/
Durable prose is incomplete when its cited computation exists only in the session that wrote it. If a script produces a number or conc
…(truncated)