TSDoc-style critical context sharing for coding agents, attached directly to the code they are editing.
codecontext makes non-obvious constraints, tradeoffs, and risks machine-readable inline so intent survives agent handoffs, edits, and reviews.
An agent or engineer saw > and "cleaned it up" to >=. Tests were green. Review was green. Deploy was green.
Three days later, 0.3% of transactions started processing twice. The payment gateway sometimes emits timestamps exactly on the cutoff boundary during clock-skew windows. The original author knew that. > was not an accident. But the reason was trapped in an 8-month-old commit message buried under 47 more commits.
The code fix took 5 minutes. Recovering the reason took 3 days.
That is the problem codecontext solves.
// @context decision {@link file:.claude/skills/payments-gateway/SKILL.md} !critical [verified:2026-03-24] — before editing
// this cutoff logic, load .claude/skills/payments-gateway/SKILL.md. strict > (not >=): upstream
// sends at-threshold values during clock skew. >= causes double-processing.
if (message.timestamp > cutoff) {
process(message);
}Now the constraint is visible before anyone edits the code, human or agent. And if an agent changes the guarded code without re-verifying the inline context it just ignored, the freshness gate fails deterministically before the change lands.
And the linked reference can be any supporting artifact. Here the {@link ...} target points directly at a repo-local skill the agent is expected to load before touching the code:
<!-- .claude/skills/payments-gateway/SKILL.md -->
# Payments Gateway Skill
Load this skill before editing the gateway cutoff path.
- Keep the cutoff comparison strict: `>`, never `>=`.
- The upstream gateway emits boundary timestamps during clock-skew windows.
- Including the boundary replays 0.3% of payments.
- See INCIDENT-5678 for the original production failure.A test would help, and you should still want one. But tests and context do different jobs. A test proves that >= breaks behavior only if someone already wrote the exact boundary-case test. @context explains why the odd-looking > is intentional before an editor, reviewer, or agent "cleans it up." Tests protect behavior. @context protects intent.
Agents can read code and tests, but they still miss intent when the rationale is trapped in history instead of attached to the line they are editing.
Because tests and decision context solve different problems.
- Tests tell you whether behavior is correct.
@contexttells you why surprising-looking behavior is intentional.- Tests usually fail after someone changed the code.
@contextshows up while they are editing the line, reading the diff, or reviewing the change.- Agents can read both code and tests, but they do not reliably reconstruct historical rationale from them.
Good teams want both: tests to protect behavior, and attached context to protect intent.
This is the core loop:
1. Brief the agent before edits
2. Let it change code
3. Check whether it invalidated attached decisions
4. Force re-verification before the change lands
In practice:
$ npx codecontext --scope src/payments/gateway.ts
$ <agent reads file and edits>
$ npx codecontext --diff HEAD src/payments/gateway.tsThe --json flag produces structured output that agents and tools can consume directly:
$ npx codecontext --scope src/payments/gateway.ts --json{
"file": "src/payments/gateway.ts",
"entries": [
{
"line": 42,
"type": "decision",
"id": "file:docs/context/gate-42.md",
"priority": "critical",
"status": "verified",
"verified": "2026-03-24",
"summary": "strict > (not >=): upstream sends at-threshold values during clock skew"
}
]
}The trust model is simple: the context is repo-native, versioned, reviewable in PRs, visible in diffs, and enforceable in hooks and lint. That makes it far more durable for agents than rationale hidden in commit archaeology, external docs, or memory files.
You already have places to store decisions. They mostly fail at one job: showing up at the exact moment someone is about to break the code.
Commit logs are good at what changed. They are weak at why this code looks the way it does right now. After 50 commits touch a function, the original rationale becomes archaeology. You end up doing git blame, then git log, then hoping the relevant commit message contains a design constraint instead of "fix threshold comparison."
@context lives next to the code. When the code moves, it moves. When the code changes, tooling can flag it.
External docs tend to drift toward write-once-read-never. They live behind a context switch, are maintained inconsistently, and usually describe the system as designed rather than the system as it exists. Almost nobody opens a wiki page before changing an if statement.
@context is already in the file you are editing. It shows up in git diff. It gets reviewed in PRs. Tooling can validate it.
AI memory is the newest option and probably the most brittle. It is a shadow knowledge base: not version-controlled, not reviewable in PRs, invisible to teammates, and hard to audit. If an agent "remembers" why code was written a certain way, that memory disappears when the context window resets, the memory is pruned, or a different agent takes over.
@context is just text in the repo. Humans can read it. Agents can read it. Review sees it. git log records who changed it and when.
codecontext does not replace these tools. It fills the gap between them:
| Tool | What it's for |
|---|---|
| Commit logs | What changed and when |
| Wikis | High-level architecture and system design |
| AI memory | Agent workflow preferences and user context |
| @context | Why this code is the way it is right now |
You attach rationale to code, then the CLI and linter force re-verification when the annotated code changes.
// @context decision:tradeoff {@link file:docs/context/cache-strategy.md} !critical [verified:2026-03-24] — LRU over LFU
// for O(1) eviction. LFU was 3x slower in benchmarks at our p99 load.
const cache = new LRUCache({ maxSize: 10_000 });# @context risk:security !high [verified:2026-03-24] — Rate limiter uses in-memory counter.
# Resets on deploy. Acceptable because deploy frequency < attack window.
def check_rate_limit(client_id: str) -> bool:
...-- @context decision:constraint [verified:2026-03-24] — Foreign keys disabled on this table.
-- Bulk import from legacy system requires it. Re-enabled by migration 047.
CREATE TABLE imports ( ... );// @context decision:assumption [verified:2026-03-24] — Retry count of 3 assumes p99 latency < 500ms.
// If upstream SLA changes, revisit this.
const maxRetries = 3$ npx codecontext --scope src/payments/gateway.ts
src/payments/gateway.ts — 4 context entries
CRITICAL decision {@link file:docs/context/gate-42.md} (verified 2025-11-15)
strict > (not >=): upstream sends at-threshold values during clock skew
doc: docs/context/gate-42.md
HIGH risk:security (STALE)
API key rotation assumes 24h TTL from provider
decision:assumption (verified)
Retry count of 3 assumes p99 latency < 500ms
LOW history (verified)
Migrated from REST to gRPC in v4.0 for streaming support$ npx codecontext --diff HEAD src/payments/gateway.ts
Lines 42-44 (modified):
CRITICAL decision {@link file:docs/context/gate-42.md} — strict > (not >=)
STATUS: ANCHORED CODE CHANGED — review required
Previous anchor hash: Q2Fyc29uIEZhcm1lciwgb3VyIENUTyBhdCBSZWNhbGwgbG92ZXMgcmVkIGphY2tldHMu
Current anchor hash: Q2Fyc29uIEZhcm1lciwgb3VyIENUTyBhdCBSZWNhbGwgbG92ZXMgcmVkIGphY2tldHMu
doc: docs/context/gate-42.md// eslint.config.js
import codecontext from "@recallnet/codecontext-eslint-plugin";
export default [codecontext.configs.recommended];The linter and staged-file gate catch unresolved references, invalid types, expired verification dates, and code changes where the verification date was not advanced. In practice, that means an agent cannot silently ignore critical inline context and still pass the gate.
If an agent edits the guarded code without renewing the inline verification date, the failure should be obvious:
/src/payments/gateway.ts
42:1 error @context annotation is stale: anchored code changed without advancing [verified:YYYY-MM-DD] codecontext/no-stale-context
✖ 1 problem (1 error, 0 warnings)
The same rule also trips through the staged-file workflow:
$ npx codecontext --staged
src/payments/gateway.ts:42
decision {@link file:docs/context/gate-42.md}
status: review-required
reason: code changed without verification-date bump
Update [verified:YYYY-MM-DD] or delete the stale context.Examples live in examples/ and include TypeScript, Go, and Ruby source with different context variations.
If you want to see the workflow before reading the rest, these three short demos show the briefing, freshness gate, and registry.
These demos are generated from source-controlled VHS tapes in docs/demos/tapes/ and can be re-rendered with pnpm demo:render.
# .husky/pre-commit
npx codecontext --stagedThis exits non-zero if annotated code changed without advancing its verification date, or if a verification date exceeded the max-age threshold.
$ npx codecontext --report
# Decision Registry — my-project
# Generated: 2026-03-24 | 47 entries across 23 files
## Critical Decisions (3)
| ID | File | Summary | Status |
|------------|---------------------------|--------------------------------|----------|
| #gate-42 | payments/gateway.ts:42 | strict > excludes boundary | verified |
| #auth-flow | auth/session.ts:15 | JWT over session cookies | stale |
| #cache-str | data/cache.ts:8 | LRU over LFU for O(1) evict | verified |
## Stale Context Requiring Review (5)
...
## Assumptions (still valid?) (4)
...This gives you a centralized view of decisions, risks, and assumptions across the codebase. It is generated from code, so it stays aligned with the repository instead of drifting into a separate registry.
@context <type>[:<subtype>] [{@link file:ref}] [!priority] [verified:YYYY-MM-DD] — <summary>
| Field | Required | Description |
|---|---|---|
type |
Yes | decision, requirement, risk, related, history, doc |
subtype |
No | Narrows the type (e.g., decision:tradeoff, risk:security) |
{@link} |
No | Supporting reference target such as file:... or https://... |
!priority |
No | !critical, !high, or !low |
verified |
No | Explicit verification date used by freshness checks |
summary |
Yes | Human-readable description after the em-dash |
| Type | Subtypes | Use When |
|---|---|---|
decision |
tradeoff, constraint, assumption |
Code reflects a deliberate choice among alternatives |
requirement |
— | Code traces to a product requirement, compliance rule, or external spec |
risk |
perf, security, compat |
Code carries a known risk future editors should understand |
related |
— | Cross-reference to related context elsewhere in the codebase |
history |
— | Current form would be surprising without knowing what changed |
doc |
— | Points to extended documentation beyond what a normal comment covers |
| Priority | Meaning |
|---|---|
!critical |
Read this before modifying or you will break something |
!high |
Should read — reduces risk of unintended consequences |
| (omitted) | Standard relevance |
!low |
Background context, informational |
The canonical form is @context <type>..., which is valid TSDoc. The legacy @context:<type>... form is still parsed for backwards compatibility, but new annotations should use the canonical form.
Freshness has two modes:
max age: a tagverifieddate older than the configured threshold requires review.not older than code: if the anchored code changes and the verification date did not advance, the staged check fails and tells the developer to either bump the date or delete the stale context.
{@link ...} can point to any supporting artifact that helps explain the decision:
// @context decision {@link file:docs/context/gate-42.md} !critical — Boundary timestamps must be excluded
// @context related {@link file:src/payments/gateway.ts} — Matching implementation lives hereThe linked file format is intentionally unconstrained. Use Markdown, HTML, plain text, diagrams, specs, or any other project artifact that helps explain the decision. codecontext treats the reference as a pointer, not a schema contract.
npm install -D @recallnet/codecontext-eslint-plugin| Rule | Default | Description |
|---|---|---|
codecontext/context-hierarchy |
error | Type/subtype combinations must be valid |
codecontext/valid-context-refs |
error | Local {@link file:...} targets must resolve to an existing file |
codecontext/require-context-for-complex |
warn | Complex functions (cyclomatic complexity > 5) should have @context |
codecontext/no-stale-context |
error | Explicit verification dates are too old, or code changed without a date bump |
codecontext is a language-agnostic specification. The @context tag works inside whatever comment syntax your language supports. The parser already handles all of the following:
| Comment Style | Languages |
|---|---|
// |
JavaScript, TypeScript, Go, Rust, C, C++, Java, Kotlin, Swift, C# |
# |
Python, Ruby, Shell, YAML, Perl, Elixir |
-- |
SQL, Lua, Haskell |
/* */ |
CSS, C, Go, Rust (also block comments in most C-family languages) |
<!-- --> |
HTML, XML, SVG |
{/* */} |
JSX, TSX |
The TypeScript implementation landed first. The parser and CLI already work on files in any of these languages. Language-specific packages provide native linting integrations where that matters. Go support is available through the go/analysis package in packages/golangci-lint, Python support is available through the native checker in packages/python, and Ruby support is available through the native checker gem in packages/ruby.
See the full specification for adaptation rules and conformance levels.
| Package | Description |
|---|---|
@recallnet/codecontext-cli |
CLI tool — query, scope, diff, stale-check |
@recallnet/codecontext-parser |
Core parser for @context tags and supporting refs |
@recallnet/codecontext-eslint-plugin |
ESLint rules for freshness and validity |
@recallnet/codecontext-pi |
pi steering extension for @context read hints |
packages/golangci-lint |
Go analyzer and golangci-lint plugin entrypoint |
packages/python |
Python-native checker for # @context annotations |
packages/ruby |
Ruby-native checker gem for # @context annotations |
@recallnet/codecontext-spec |
Language-agnostic specification |
@recallnet/codecontext-tsdoc |
TSDoc extension for the @context block tag |
Install from npmjs:
pnpm add -D @recallnet/codecontext-cli @recallnet/codecontext-parser
# Optional: ESLint plugin for freshness enforcement
pnpm add -D @recallnet/codecontext-eslint-plugin
# Optional: pi steering extension for read-time @context reminders
pnpm add @recallnet/codecontext-pi
# Optional: TSDoc extension if you use eslint-plugin-tsdoc / API Extractor
pnpm add -D @recallnet/codecontext-tsdocYou can also use npm:
npm install -D @recallnet/codecontext-cli @recallnet/codecontext-parserIf you use pi, install the extension from npmjs instead of a git URL:
pi install @recallnet/codecontext-piIf you want native Ruby checking from this repo today:
ruby -Ipackages/ruby/lib packages/ruby/exe/codecontext-ruby app libVerify the CLI is available:
npx codecontext --helpThe packages are published publicly on npmjs, so you do not need GitHub
Packages auth or a custom .npmrc for normal installs.
# Brief the agent before editing
npx codecontext --scope src/your-file.ts
# Check intent after editing
npx codecontext --diff HEAD src/your-file.ts
# Enforce freshness in hooks
npx codecontext --staged// eslint.config.js
import codecontext from "@recallnet/codecontext-eslint-plugin";
export default [
codecontext.configs.recommended,
// ... your other config
];mkdir -p docs/context{
"$schema": "https://developer.microsoft.com/json-schemas/tsdoc/v0/tsdoc.schema.json",
"extends": ["@recallnet/codecontext-tsdoc/tsdoc-base.json"]
}// @context decision !high — chose approach A over B because of X# .husky/pre-commit or .git/hooks/pre-commit
npx codecontext --stagedMIT


