Your markdown has no lifecycle. That's the bug.
kontext gives context documents one — so you can prove which docs are stale,
pack fresh context for agents, and hand off state between sessions.
Quickstart · How it works · For AI agents · Changelog · The spec
Your project has a hundred markdown files. Some are load-bearing truth. Some describe a system you refactored away eleven months ago.
They look identical. Same folder, same font, same confident tone.
You cope by ignoring the docs and reading the code. Your agent can't. Hand Claude a
stale runbook and it will follow the stale runbook, because it has no way to smell that
src/auth/ was rewritten in March. So every team lands on one of two losing strategies:
- Load everything → burn the context window on documents that are mostly wrong
- Load nothing → rediscover the codebase from scratch, every single session
Neither is context management. They're surrender in two directions.
The root cause isn't volume. It's that a markdown file is born and then immortal. Code has tests. Dependencies have versions. Infrastructure has health checks. Documentation alone never has to prove it still works.
A context document isn't a file. It's a claim about code — and claims can be checked.
Let a document declare what it describes. Git already knows every change to that subject, timestamped and attributed. So git can answer the question the document can't answer about itself:
Has the thing this document describes changed since the document last did?
That has an objective answer. Two git log calls and a comparison. No LLM, no
embeddings, no vibes.
---
kontext: 1
id: auth-session-flow
kind: guide
describes:
- src/auth/**
- src/middleware/session.ts
verify: npm run test:auth
---That describes field is the whole trick. It turns an unfalsifiable assertion into a
checkable claim.
npm install -g @patkusch/kontextOr run it without installing anything:
npx @patkusch/kontext doctorFrom source instead — no build dependencies beyond TypeScript
git clone https://github.com/patkusch/kontext.git && cd kontext && npm install && npm run build && npm linkThen, in any repo you want to look at:
kontext doctorWorks on any repo right now, with zero setup and zero frontmatter. It'll tell you how bad things actually are.
kontext checkkontext check · 5 docs · ~42 tokens · ~/demo
── orphaned (1) ────────────────────────────────────────────────────────────────
⊘ docs/legacy-queue.md 7 1 dead glob
· orphaned: `describes` glob workers/queue/** matches zero tracked files
— the code this doc describes was deleted, renamed, or moved
── stale (2) ───────────────────────────────────────────────────────────────────
○ docs/auth.md 10 12 commits · 181d behind · 1 file
· stale: 12 commits touched src/auth/** since this doc was last updated
(latest: eeefbd7 'auth change 11', 19 days ago); the described code is
181 days ahead of the doc (doc: 808f909 'initial: code and docs
together', 200 days ago) — past the 45-day stale threshold
· files that moved: src/auth/session.ts
── unverified (1) ──────────────────────────────────────────────────────────────
· docs/philosophy.md 50
· unverified: no `describes` field, so this doc makes no falsifiable
claim about source files — staleness can be neither proven nor ruled
out. Add `describes: [<globs>]` to make it checkable.
── fresh (1) ───────────────────────────────────────────────────────────────────
● docs/api.md 100 1 file
1 fresh · 1 unverified · 2 stale · 1 orphaned
● fresh ◐ drifting ○ stale ⊗ expired ⊘ orphaned ⇥ superseded · unverified
check failed: 3 docs in fail set [stale, expired, orphaned]
(Real output, not a mockup — that's kontext check run against a demo repo.)
Every verdict cites its evidence. You can check the reasoning and overrule it — a tool that says "trust me, it's stale" deserves the same fate as the docs it's judging.
kontext init --dry-run # propose frontmatter for existing docs, write nothing
kontext init --yes # prepend it, preserving every body byte-for-byteinit infers an id from the filename, guesses kind from structure, and proposes
describes globs from the code paths your doc actually mentions. These are guesses,
and it marks them as guesses. Narrow them by hand — that's the part that matters.
- run: kontext check --fail-on stale,orphaned # needs fetch-depth: 0That fetch-depth: 0 matters: kontext proves staleness from git history, and a shallow
clone makes every doc look freshly committed.
Now a PR that changes src/auth/ and not its docs fails the build. Documentation joins
the set of things that must keep working.
your docs ──┐
├──► scan ──► assess ──► rank ──► pack
git history ┘ │ │
│ └──► budgeted bundle
└──► evidence: for one task
commits, dates,
drift days
Freshness verdicts:
| Verdict | Meaning | |
|---|---|---|
| ● | fresh |
Described code hasn't moved since the doc did |
| ◐ | drifting |
Code moved, within the warning threshold |
| ○ | stale |
Code moved materially after the doc — don't trust it blind |
| ⏱ | expired |
Past its expires / ttlDays |
| ↪ | superseded |
Another doc declares it replaced |
| ⊘ | orphaned |
describes matches no files — the subject is gone or moved |
| · | unverified |
No describes — no claim to check |
unverified isn't a failure. Most docs in most repos start there. It marks absence of
evidence, and the honest thing to do with that is say so.
When a folder moves. Say a doc describes src/auth/** and the team moves that folder
to src/identity/**. The glob now matches nothing, so the doc reads as orphaned. kontext
asks git where the files went, and says so:
⊘ docs/auth.md 10 1 dead glob
· `src/auth/**` matches nothing now, but it used to match 4 files (the last
of them left in 65fa565 'move auth to identity', 79 days ago). 4 of 4 now
appear at `src/identity/**` (git matched them by content; the weakest
match is 98% identical). Update `describes` to `src/identity/**` if that
is where this doc's subject lives.
That is a suggestion. kontext never edits your doc, and the doc stays orphaned until you do.
It only suggests a new glob when more than half of the files moved to one place (3 of 4,
not 2 of 4). Git decides what counts as the same file, at 50% alike or more. If files were
moved and heavily rewritten in one commit, kontext says it cannot tell instead of guessing.
Move first and edit in a second commit, and git can follow it.
Age is not staleness. A 2019 ADR explaining why you rejected microservices is still
true — its subject is a historical decision, and history doesn't drift. A runbook from
last month with the wrong port is actively harmful. kontext ranks decision and
runbook docs differently for exactly this reason.
kontext pack "refactor session expiry" --budget 8000Ranks by relevance weighted by freshness, fills the token budget, truncates on heading boundaries, stamps provenance on everything.
And the rule that matters most:
A pack never silently omits anything.
Everything excluded gets reported — stale docs under excludedForStaleness, budget
casualties under omitted, each with a reason. An agent that doesn't know something was
withheld will reason confidently from a hole in its context and never signal doubt.
Silent truncation is indistinguishable, from the inside, from complete knowledge. That's
a worse failure than the stale docs this tool exists to fix, because it's invisible.
The moment context reliably dies: you close the session. What you'd ruled out, what surprised you, what you were mid-way through — all of it lived in a conversation that just ended.
kontext handoff --task "session expiry refactor" \
--message "ruled out Redis TTL; see open question on clock skew"Captures branch, recent commits, working-tree changes, freshness of the docs touching
those files, open questions, next steps → .kontext/handoff.md, with ttlDays: 7
because working state rots faster than anything else you write.
The acceptance test: a fresh agent with no prior context can read it and resume the work. If it needs the original conversation to make sense, it's a note, not a handoff.
This is where it stops being a linter and starts being infrastructure.
Instead of an agent slurping every markdown file, it gets a freshness-aware context API — ranked, budgeted, and tagged with how much each source can be trusted.
claude mcp add kontext -- node /absolute/path/to/kontext/dist/mcp/server.jsClaude Desktop / other MCP clients
Claude Desktop's working directory isn't your repo, so KONTEXT_ROOT is required there:
{
"mcpServers": {
"kontext": {
"command": "node",
"args": ["/absolute/path/to/kontext/dist/mcp/server.js"],
"env": { "KONTEXT_ROOT": "/absolute/path/to/your/repo" }
}
}
}Tools exposed:
| Tool | What it's for |
|---|---|
kontext_pack |
Ranked, budgeted context bundle for a task |
kontext_freshness |
"Can I trust this doc?" — verdict + evidence |
kontext_search |
Cheap discovery without pulling full bodies |
kontext_read |
A doc, with its trust verdict attached |
kontext_handoff_write |
Hand working state to the next agent |
kontext_handoff_read |
Pick up where the last one left off |
kontext_conflicts |
"Do my sources disagree?" |
kontext_read never returns a document without telling the agent whether it's
trustworthy. That's the entire thesis in one API decision.
Two docs, two different answers, and an agent picks one at random:
kontext doctor⚠ contradiction docs/setup.md ↔ README.md
└ dev server port: "localhost:3000" vs "localhost:8080"
⚠ duplicate docs/onboarding.md ↔ docs/getting-started.md
└ 87% similar — one probably supersedes the other
Deliberately conservative. A false contradiction is worse than a missed one, because it costs you trust in the tool.
- Evidence over opinion. Every claim traces to a commit.
- Absence of evidence is reported as such.
unverifiedis a real answer. - Never omit silently. What got dropped, and why, is part of the output.
- Degrade, don't crash. No frontmatter, no commits, broken YAML, globs matching nothing — all normal, none fatal.
- No lock-in. Frontmatter is inert YAML. Delete kontext tomorrow and you've lost nothing but the tool.
- No LLM in the staleness path. Determinism is the feature. Same commit, same answer, every time — which is the only reason CI can gate on it.
- Judge whether a doc is good — only whether it's current. Different problems.
- Auto-update your docs. A tool that rewrites docs to match the code produces text describing the code, which the code already does better. The why is what documentation is for, and only you know it.
- Use an LLM to detect staleness. See principle 6.
kontext doctor |
Diagnose the repo. Start here. |
kontext check |
Freshness verdicts + evidence. CI gate. |
kontext map |
Full corpus inventory and freshness heatmap. |
kontext pack <task> |
Budgeted context bundle. |
kontext init |
Propose frontmatter for existing docs. |
kontext handoff |
Capture working state for the next session. |
kontext mcp |
Run the MCP server (stdio). |
All commands support --json.
- The Context Lifecycle Spec — the full model, the reasoning, and the open questions. Start here if you want to argue with the design.
Early. v0.1. The core mechanism — git-provable drift — is the part I'm confident in. The ranking heuristics and conflict detection are where feedback would help most.
The open questions are genuinely open: monorepo scoping, section-level staleness, cross-repo context. Rename tracking is closed: a folder that moves is found and suggested, and the reasoning is written down.
MIT © patkusch