# lintlang — Full Technical Reference

> Static linter for AI agent tool descriptions, system prompts, and configs.
> Context engineering quality gate. Zero LLM calls. One dependency. Runs in CI.
> By Hermes Labs (https://hermes-labs.ai).

## What It Does

lintlang treats agent configs as lintable artifacts — the same way eslint lints JavaScript or ruff lints Python. It flags language patterns associated with ambiguous tool selection, missing termination conditions, structured-output problems, and context-boundary risk.

No LLM calls. No API keys. No network access. Static analysis uses regex, structural heuristics, and Python AST extraction.

## Install

Run once without installing:

```
uvx lintlang scan AGENTS.md
```

Install as an isolated persistent command:

```
pipx install lintlang
lintlang scan AGENTS.md
```

If pipx's app directory is not on `PATH`, run `pipx ensurepath`, open a new
shell, and retry the scan.

Install into the current Python environment:

```
pip install lintlang
```

Python 3.10+. Single runtime dependency: pyyaml.

## CLI Usage

```
lintlang scan config.yaml              # Scan a file
lintlang scan configs/                 # Scan a directory
lintlang scan config.yaml --format json # JSON output
lintlang scan config.yaml --fail-on fail    # CI gate: fail on CRITICAL/HIGH
lintlang scan config.yaml --fail-on review  # CI gate: fail on MEDIUM+
lintlang scan config.yaml --min-severity high  # Only show HIGH+
lintlang scan config.yaml --patterns H1 H3     # Only run specific detectors
lintlang patterns                      # List all detectors
printf '%s' 'Is it true that X?' | lintlang preflight - --format json
```

`preflight` is separate from repository scanning. It examines only the present
instruction and explicit caller-supplied context. It never retrieves history,
calls a provider/model, silently applies a patch, or sends the result.

## Programmatic API

```python
from lintlang import scan_file, scan_directory, compute_verdict

result = scan_file("config.yaml")
verdict = compute_verdict(result)  # ERROR, PASS, REVIEW, or FAIL

for finding in result.structural_findings:
    print(f"[{finding.severity.value}] {finding.pattern}: {finding.description}")
    print(f"  -> {finding.suggestion}")
```

Provider-neutral preflight:

```python
from lintlang import PreflightRequest, preflight_text

result = preflight_text(PreflightRequest(prompt="Is it true that X?"))
print(result.status.value)  # NOTICE
print(result.to_json())     # raw prompt/context/patch text redacted by default
```

Preflight states are `ALLOW`, `NOTICE`, `HOLD`, `UNAVAILABLE`, and `ERROR`.
Heuristic input-framing findings are notice-only. Only exact typed missing-context
or mechanical-conflict findings may hold. `ALLOW` is not proof of truth, safety,
quality, or provider compatibility.

## Verdict System

| Verdict | Meaning | Trigger |
|---------|---------|---------|
| ERROR | A requested input could not be inspected | Missing, unreadable, or malformed input |
| PASS | No MEDIUM/HIGH/CRITICAL finding remained | Recognized content passed the selected checks and filters |
| REVIEW | Review structural warnings | MEDIUM findings present |
| FAIL | Blocking structural finding detected | CRITICAL or HIGH findings |

`PASS` applies only to recognized content extracted from the requested inputs
and the checks and severity filters selected for that run. It does not mean
that every structure in an arbitrary JSON or YAML file was extracted.

## Structural Detectors (H1-H7)

### H1: Tool Description Ambiguity
Catches: empty descriptions, very short descriptions (<20 chars), vague verbs (get, handle, process, manage, do), duplicate tool names, high word overlap between tools (Jaccard similarity with stopword removal).

### H2: Missing Constraint Scaffolding
Catches: no termination conditions, unbounded retry loops ("keep trying until"), negative termination ("don't stop until"), "continue until" without limits, missing retry budgets.

### H3: Schema-Intent Mismatch
Catches: phantom required fields (listed in required but not in properties), parameters with no description, generic parameter names (data, input, value, payload), undescribed anyOf/oneOf variants, nested objects without descriptions.

### H4: Context Boundary Erosion
Catches: "remember everything" without scope, "use all/entire conversation/history/context" without bounds, "always keep/maintain/remember" without specifying what/how long, long prompts with no boundary markers.

### H5: Implicit Instruction Failure
Three-layer exemption system for negative instructions:
- Layer 1 (structural): Exempts negatives inside HTML comments, code blocks, inline code, generated-file markers.
- Layer 2 (phrase-level): Exempts privacy disclaimers ("never sent/shared/stored"), UI labels ("Never ask again"), descriptive text ("it doesn't"), idiomatic phrases ("don't reinvent"), "to avoid" constructions.
- Layer 3 (safety context): Exempts negatives near security/auth/policy keywords within 100-char window.

Also catches: vague qualifiers ("be concise", "be helpful", "use common sense"), ambiguous conditionals ("as needed", "when appropriate"), figurative verbs ("lean into", "err on the side of", "keep it simple"), high instruction count without priority ordering.

### H6: Template Format Contract Violation
Code-aware detection: strips fenced code blocks, inline code, filenames, and CLI flags before counting format keywords. Catches: multiple output formats without disambiguation, missing format specification, template variables, missing version markers.

### H7: Role Confusion
Catches: multiple system messages, system messages not at position 0, consecutive same-role messages, orphan tool results (no preceding tool_use), messages missing role field.

## Supported Formats

- YAML (.yaml, .yml) — recognized top-level agent fields and tool definitions
- JSON (.json) — recognized top-level agent fields, tool schemas, and message arrays
- Plain text (.txt, .md, .prompt) — System prompts, instruction docs
- Python source (.py) — AST extraction of embedded prompts and P1/P2 pipeline checks

Auto-detects format. Unknown extensions tried as JSON -> YAML -> plain text.
Nested vendor-specific layouts and raw top-level YAML arrays are not
automatically normalized.

## CI Integration

GitHub Actions:
```yaml
- uses: actions/checkout@v7
- uses: hermes-labs-ai/lintlang@v0.3.2
  with:
    path: configs/
```

Add to `.pre-commit-config.yaml`:
```yaml
repos:
  - repo: https://github.com/hermes-labs-ai/lintlang
    rev: v0.3.2
    hooks:
      - id: lintlang
        args: [AGENTS.md]
```

Activate and test it with `pre-commit install` and `pre-commit run lintlang`.

The pre-commit hook reports verdicts without blocking by default. After
reviewing the repository baseline, use
`args: [AGENTS.md, --fail-on, fail]` to block `FAIL` findings. Configured input
errors remain blocking.

Exit codes: 0 = pass, 1 = findings matched --fail-on threshold or error.

## Part of Hermes Labs Ecosystem

- little-canary: Prompt injection detection
- cogito-ergo: Three-layer memory retrieval for AI agents
- quickthink: Planning scaffolding for local LLMs
- zer0dex: Dual-layer memory for AI agents
- forgetted: Selective memory governance
- zer0lint: Memory extraction diagnostics
- suy-sideguy: Autonomous agent watchdog

## License

Apache 2.0

## About Hermes Labs

Hermes Labs is an independent AI-reliability lab building open-source tools that catch silent failure modes in production AI. More at [hermes-labs.ai](https://hermes-labs.ai).
