Aller au contenu

Annotation Reference

Ce contenu n’est pas encore disponible dans votre langue.

Annotations are comments in your source code that create traceable links from code to Contextia artifacts. They are the bottom-up counterpart to the top-down paths field in spec frontmatter.

The general format is:

{comment_char} {prefix}{type} {id} [{sub_id}]

Where:

  • {comment_char} is the language’s comment syntax (e.g., #, //, --)
  • {prefix} is the configured annotation prefix (default: @)
  • {type} is the annotation type: spec, decision, test, norm
  • {id} is the artifact identifier (e.g., SPEC-001, DEC-003)
  • {sub_id} is an optional sub-identifier for linking to a specific behavior within a spec
# Python
# @spec SPEC-001
# @decision DEC-003
# @test SPEC-001/login-success
def authenticate_user():
...
// TypeScript
// @spec SPEC-012
// @norm NORM-A11Y-001
function FilterPanel(): JSX.Element {
...
}
// Rust
// @spec SPEC-020
// @decision DEC-015
fn process_order(order: &Order) -> Result<Receipt, OrderError> {
...
}
// Go
// @spec SPEC-005
// @decision DEC-008
func (s *Server) HandleWebhook(w http.ResponseWriter, r *http.Request) {
...
}
-- SQL
-- @spec SPEC-030
-- @decision DEC-022
CREATE TABLE users (
id SERIAL PRIMARY KEY,
email VARCHAR(255) UNIQUE NOT NULL
);

Links code to a behavioral specification.

# @spec SPEC-001

This is the most common annotation. It says: “this code implements (or is part of) SPEC-001.” When an agent loads context for a task that references SPEC-001, it knows to look at files with this annotation.

Links code to an architectural decision record.

# @decision DEC-001

Use this when code directly implements a specific architectural choice. For example, if DEC-001 says “use JWT with RS256”, annotate the JWT signing function with @decision DEC-001.

Links test code to a specific spec behavior.

# @test SPEC-001/login-success
def test_login_returns_token_for_valid_credentials():
...
# @test SPEC-001/login-failure
def test_login_returns_401_for_invalid_credentials():
...

The @test annotation connects test cases to spec behaviors, enabling traceability from requirement to test.

Links code to a project norm.

# @norm NORM-SEC-001
def hash_password(password: str) -> str:
...

Use this when code directly implements or enforces a norm rule.

Sub-IDs allow linking to a specific behavior within a spec, rather than the spec as a whole. The format is:

{id}/{sub_id}

The sub_id is a kebab-case label that corresponds to a behavior in the spec:

# @spec SPEC-001/token-refresh
def refresh_token(refresh: str) -> TokenPair:
...
# @spec SPEC-001/token-validation
def validate_access_token(token: str) -> Claims:
...

Sub-IDs are particularly useful for @test annotations, where each test should map to a specific behavior:

# @test SPEC-001/token-refresh
def test_refresh_returns_new_token_pair():
...
# @test SPEC-001/expired-refresh
def test_refresh_rejects_expired_token():
...

A single code element can have multiple annotations:

# @spec SPEC-001
# @decision DEC-001
# @norm NORM-SEC-001
def authenticate_user(username: str, password: str) -> Token:
...

Each annotation is a separate comment line. They must be consecutive (no blank lines between them) and immediately precede the code element they annotate.

Contextia scans annotations in any language with single-line comments. The comment syntax is configured per language:

LanguageComment syntaxFile extensions
Python#.py, .pyi
TypeScript//.ts, .tsx
JavaScript//.js, .jsx, .mjs, .cjs
Rust//.rs
Go//.go
Java//.java
Kotlin//.kt, .kts
Swift//.swift
Ruby#.rb
C//.c, .h
C++//.cpp, .hpp, .cc, .hh
C#//.cs
SQL--.sql
Lua--.lua
Haskell--.hs
Shell#.sh, .bash, .zsh
YAML#.yaml, .yml
TOML#.toml

To add an unlisted language, specify its comment syntax in config:

annotations:
comment_syntax:
elixir: "#"
erlang: "%"

The annotation scanner produces structured results:

@dataclass
class AnnotationResult:
file: Path # File containing the annotation
line: int # Line number (1-based)
type: str # "spec", "decision", "test", "norm"
id: str # Artifact ID (e.g., "SPEC-001")
sub_id: str | None # Sub-ID if present (e.g., "token-refresh")
context: str | None # Enclosing function/class name (best-effort)

The context field is populated differently depending on the parser mode.

The regex parser scans files line by line, looking for comments that match the annotation pattern. It uses simple heuristics to detect the enclosing function or class:

  • Looks for def, fn, func, function, class, struct, impl keywords on subsequent lines.
  • Extracts the name after the keyword.
  • Works well for most common code patterns but may miss context in deeply nested or unusual structures.
annotations:
parser: regex

Regex mode has no external dependencies and works with any language that has single-line comments.

The tree-sitter parser uses language-specific AST parsing to precisely determine the enclosing code element. It provides accurate function names, class names, and method scoping.

annotations:
parser: tree-sitter

Tree-sitter mode requires the optional py-tree-sitter dependency and language-specific grammar packages:

Terminal window
pip install contextia[tree-sitter]
FeatureRegexTree-sitter
DependenciesNonepy-tree-sitter + grammars
SpeedFastModerate
Context accuracyBest-effortPrecise
Language coverageAny with line commentsLanguages with tree-sitter grammars
Nested contextMay missFully accurate

If @spec conflicts with an existing annotation system in your project (e.g., Java @Override, TypeScript decorators), change the prefix:

annotations:
prefix: "@ctx:"

Annotations then look like:

# @ctx:spec SPEC-001
# @ctx:decision DEC-003

For languages not in the default list:

annotations:
comment_syntax:
nim: "#"
forth: "\\"
ada: "--"

Exclude files from annotation scanning:

check:
ignore_patterns:
- "tests/fixtures/**"
- "vendor/**"
- "**/*.generated.*"
- "**/*.min.js"

contextia check validates annotations against the knowledge layer:

CheckSeverityDescription
Orphan annotationError@spec SPEC-999 where SPEC-999 does not exist.
Missing annotationWarningSpec lists paths: [src/auth.py] but src/auth.py has no @spec for that spec.
Duplicate annotationWarningSame @spec SPEC-001 appears multiple times in the same file on the same element.
Invalid ID formatError@spec my-spec does not match the expected ID pattern.

With check.strict_bidirectional: true, missing annotation warnings become errors.

  1. Annotate at the function or class level, not individual lines. The annotation represents a semantic link, not a line-level marker.
  2. Use sub-IDs for tests to map each test to a specific behavior in the spec.
  3. Keep annotations consecutive and immediately before the code element. Do not separate them with blank lines or unrelated comments.
  4. Do not over-annotate. If a file has 10 functions and all implement the same spec, annotate the module-level or class-level declaration, not each function individually.
  5. Run contextia check after refactoring to catch moved or deleted files that still have annotations or are still listed in spec paths.