{
  "$schema": "https://raw.githubusercontent.com/YawLabs/ctxlint/main/schemas/ctxlint-catalog.schema.json",
  "title": "AI Agent Session Lint Rules",
  "description": "Machine-readable catalog of lint rules for AI agent session data cross-project consistency",
  "specVersion": "1.0.0-draft",
  "specDate": "2026-04-08",
  "repository": "https://github.com/YawLabs/ctxlint",
  "categories": [
    {
      "id": "session",
      "name": "Session Audit",
      "description": "Cross-project consistency checks using AI agent session data.",
      "scope": "cross-project"
    }
  ],
  "rules": [
    {
      "id": "session/missing-secret",
      "category": "session",
      "severity": "error",
      "description": "GitHub secret set on sibling repos but not this project.",
      "trigger": "gh secret set command found in agent history for 2+ siblings but not current project.",
      "message": "Secret \"{name}\" is set on {count} sibling repos but not this project",
      "fixable": false,
      "stability": "stable"
    },
    {
      "id": "session/diverged-file",
      "category": "session",
      "severity": "warning",
      "description": "Canonical file has diverged from sibling repos.",
      "trigger": "File exists in current project and siblings with 20-90% line overlap.",
      "message": "\"{file}\" has {overlap}% overlap with {sibling} — may have diverged",
      "fixable": false,
      "canonicalFiles": [
        "release.sh",
        ".github/workflows/ci.yml",
        ".github/workflows/release.yml",
        "biome.json",
        ".prettierrc",
        ".eslintrc.json",
        "tsconfig.json",
        ".gitignore"
      ],
      "stability": "stable"
    },
    {
      "id": "session/missing-workflow",
      "category": "session",
      "severity": "warning",
      "description": "GitHub Actions workflow missing from this project.",
      "trigger": "Workflow file exists in 2+ siblings but not current project (which has .github).",
      "message": "Workflow \"{workflow}\" exists in {count} sibling repos but not this project",
      "fixable": false,
      "stability": "stable"
    },
    {
      "id": "session/stale-memory",
      "category": "session",
      "severity": "info",
      "description": "Memory file references paths that no longer exist.",
      "trigger": "Claude Code memory file contains path references to files that have been deleted or moved.",
      "message": "Memory references \"{path}\" which no longer exists",
      "fixable": false,
      "stability": "stable"
    },
    {
      "id": "session/duplicate-memory",
      "category": "session",
      "severity": "info",
      "description": "Near-duplicate memory entries across projects.",
      "trigger": "Two memory files from different projects have >60% line overlap, and at least one of the pair belongs to the current project (otherwise every lint run from any repo would resurface the same unrelated cross-project duplicates).",
      "message": "\"{file1}\" and \"{file2}\" have {overlap}% content overlap",
      "fixable": false,
      "stability": "stable"
    },
    {
      "id": "session/consecutive-repeat",
      "category": "session",
      "severity": "warning",
      "description": "Same command run 3+ times consecutively in agent history.",
      "trigger": "Agent history for the current project (entries lacking a project path or timestamp are excluded), grouped into sessions (by provider + session ID, split at >30-minute timestamp gaps), shows the same display string 3+ times in a row within a single session segment. Single-command sessions are additionally merged per provider into one stream (split at the same gaps) so rapid one-shot respawn loops are caught.",
      "message": "Command run {count} times consecutively: \"{command}\"",
      "fixable": false,
      "stability": "experimental"
    },
    {
      "id": "session/cyclic-pattern",
      "category": "session",
      "severity": "warning",
      "description": "Cyclic command pattern detected in agent history.",
      "trigger": "Within a single session segment (same provider + session ID, split at >30-minute gaps; entries lacking a project path or timestamp are excluded), agent history shows a repeating sequence of 2-3 commands (e.g. A,B,A,B) repeated 2+ times. Spans already reported by session/consecutive-repeat are not re-reported. The merged one-shot stream is not scanned for cycles.",
      "message": "Cyclic pattern repeated {count} times: {cycle}",
      "fixable": false,
      "stability": "experimental"
    },
    {
      "id": "session/memory-index-overflow",
      "category": "session",
      "severity": "warning",
      "description": "MEMORY.md exceeds Claude Code's session-load cap. The first 200 lines / 25KB are loaded every session; anything beyond is effectively invisible to the agent.",
      "trigger": "~/.claude/projects/<encoded-project>/memory/MEMORY.md exceeds 200 lines OR 25,600 bytes.",
      "message": "MEMORY.md has {N} lines — only the first 200 are loaded. {excess} lines are effectively invisible.",
      "fixable": false,
      "source": "code.claude.com/docs/en/memory",
      "stability": "stable"
    },
    {
      "id": "session/shared-temp-path",
      "category": "session",
      "severity": "error",
      "description": "A fixed, non-session-scoped temp path is written and later read back.",
      "trigger": "Agent history writes a literal path under /tmp, /var/tmp, $TMPDIR or %TEMP% with no per-run component, then reads the same path.",
      "message": "Fixed temp path \"{path}\" is written and later read back",
      "fixable": false,
      "stability": "stable"
    },
    {
      "id": "session/unverified-gate-claimed-clean",
      "category": "session",
      "severity": "warning",
      "description": "A quality gate is asserted as passing while its invocation failed or produced no output.",
      "trigger": "A lint/typecheck/test/build command errors or emits nothing, and nearby agent prose claims it passed.",
      "message": "'{gate}' asserted as passing, but the invocation produced no output",
      "fixable": false,
      "stability": "stable"
    },
    {
      "id": "session/default-branch-accumulation",
      "category": "session",
      "severity": "warning",
      "description": "Many files edited on the default branch with no intervening commit.",
      "trigger": "Ten or more distinct files written while on main/master without a commit or branch-away in between.",
      "message": "{count} files edited on '{branch}' with no intervening commit",
      "fixable": false,
      "stability": "stable"
    },
    {
      "id": "session/unresolvable-sha",
      "category": "session",
      "severity": "warning",
      "description": "A memory cites a git SHA that does not resolve in the repository.",
      "trigger": "A 7-40 character hex token outside code fences, preceded on the same line by a commit-citation cue (commit, SHA, HEAD, landed in, reverted, ...), does not resolve via git cat-file. UUID members, digest prefixes, decimal ids and version fragments are excluded before resolution; without a git repository the rule reports nothing.",
      "message": "cited commit {sha} does not resolve in this repository",
      "fixable": false,
      "stability": "experimental"
    }
  ],
  "dataSources": [
    {
      "id": "claude-code",
      "name": "Claude Code",
      "provider": "Anthropic",
      "historyLocation": "~/.claude/history.jsonl",
      "historyFormat": "JSONL",
      "memoryLocation": "~/.claude/projects/*/memory/*.md",
      "documented": true
    },
    {
      "id": "codex-cli",
      "name": "Codex CLI",
      "provider": "OpenAI",
      "historyLocation": "~/.codex/history.jsonl",
      "historyFormat": "JSONL",
      "sessionsLocation": "~/.codex/sessions/**/*.jsonl",
      "documented": true
    },
    {
      "id": "aider",
      "name": "Aider",
      "provider": "Paul Gauthier",
      "historyLocation": ".aider.chat.history.md",
      "historyFormat": "Markdown",
      "documented": true,
      "notes": "History file is per-project, stored in the project root."
    },
    {
      "id": "vibe-cli",
      "name": "Vibe CLI",
      "provider": "Mistral",
      "historyLocation": "~/.vibe/sessions/*/messages.jsonl",
      "historyFormat": "JSONL",
      "documented": false,
      "notes": "Partially documented."
    },
    {
      "id": "amazon-q",
      "name": "Amazon Q Developer",
      "provider": "AWS",
      "historyLocation": "~/.aws/amazonq/history/chat-history-*.json",
      "historyFormat": "JSON",
      "documented": false,
      "notes": "Semi-documented; file layout inferred from observation."
    },
    {
      "id": "goose",
      "name": "Goose",
      "provider": "Block",
      "historyLocation": "~/.local/share/goose/sessions/sessions.db",
      "historyFormat": "SQLite",
      "documented": true,
      "notes": "Requires SQLite dependency to read session data."
    },
    {
      "id": "continue",
      "name": "Continue",
      "provider": "Continue.dev",
      "historyLocation": "~/.continue/sessions/*.json",
      "historyFormat": "JSON",
      "documented": false,
      "notes": "Partially documented."
    },
    {
      "id": "windsurf",
      "name": "Windsurf",
      "provider": "Codeium",
      "historyLocation": "~/.windsurf/transcripts/*.jsonl",
      "historyFormat": "JSONL",
      "documented": false,
      "notes": "Partially documented; format marked unstable by vendor."
    }
  ],
  "agents": [
    "claude-code",
    "codex-cli",
    "aider",
    "vibe-cli",
    "amazon-q",
    "goose",
    "continue",
    "windsurf"
  ]
}
