Git Intent & Anti-Patterns β
A linter can tell you a function is complex. Only git history can tell you that the last three people who touched it reverted their changes. Ctxo records that history per symbol so agents stop walking into the same trap.
Why it exists β
Code reads as a snapshot. Bugs and design pressure live in the deltas. An agent that cannot see "this file has been reverted twice in six weeks" will happily re-introduce the same broken optimization. Surfacing commit intent plus revert patterns turns the agent from reactive ("I will try this") into proactive ("this area has a history; let me be careful").
Where the data comes from β
The adapter is packages/cli/src/adapters/git/simple-git-adapter.ts, a thin wrapper around simple-git.
Two calls do the heavy lifting:
getCommitHistory(filePath, maxCount)usesgit log --followto track a file across renames.getBatchHistory(maxCount)a singlegit log --name-onlypass that builds afile -> commitsmap for the whole repo. This is whatctxo indexuses so indexing stays under the 500 ms target even on large repos.
If git is not available, the adapter logs and returns empty arrays. No crash, no blocking.
Two record types per file β
The JSON index stores commit data in two arrays, both validated by zod:
{
"intent": [
{ "hash": "abc123", "message": "fix race condition", "date": "2024-03-15", "kind": "commit" }
],
"antiPatterns": [
{ "hash": "def456", "message": "revert: remove mutex", "date": "2024-02-01" }
]
}intentevery commit message that touched this file, in reverse- chronological order. Raw material for the agent.antiPatternsthe subset of commits classified as reverts or back-outs.
Anti-pattern detection β
The classifier is RevertDetector in core/why-context/revert-detector.ts. It recognizes three families of signal:
Explicit reverts
Revert "some message"(git's default revert format)revert: some message(conventional-commits style)undo: .../rollback: ...
Indirect keywords searched in the full message:
revert,reverts,reverted,revertingrollback,roll back,rolled backundo,undoes,undone,undoingbacked out,backs outremove broken,remove buggy,remove faulty
A single match flags the commit as an anti-pattern. The heuristic errs on the side of surfacing too much rather than hiding incidents.
False positives are expected
"Revert the cursor to start" or "undo button" will trip the keyword matcher. The agent consuming the output is expected to read the message, not just the classification. See the get_why_context response format.
What this catches that linters do not β
| Signal | Linter | Ctxo |
|---|---|---|
| Cyclomatic complexity | yes | yes |
| "This file was reverted twice last quarter" | no | yes |
| "Last commit here fixed a race condition" | no | yes |
| "Three authors rolled back the same function" | no | yes |
These are the signals that prevent re-introducing a regression. They live exclusively in git history, and Ctxo is the one delivering them to the agent.
Related β
get_why_contextthe MCP tool that returns intent + anti-patterns + optional change intelligence for a symbol.- Change intelligence complements intent with churn-weighted complexity.
Implementation detail
Commit intent is currently file-scoped, not symbol-scoped. Every symbol in a file inherits the file's commit history. Finer granularity (via git blame per line range) is on the roadmap; see docs/roadmap.md.