Annotation Reference
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.
Annotation syntax
Section titled “Annotation syntax”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
Examples
Section titled “Examples”# Python# @spec SPEC-001# @decision DEC-003# @test SPEC-001/login-successdef authenticate_user(): ...// TypeScript// @spec SPEC-012// @norm NORM-A11Y-001function FilterPanel(): JSX.Element { ...}// Rust// @spec SPEC-020// @decision DEC-015fn process_order(order: &Order) -> Result<Receipt, OrderError> { ...}// Go// @spec SPEC-005// @decision DEC-008func (s *Server) HandleWebhook(w http.ResponseWriter, r *http.Request) { ...}-- SQL-- @spec SPEC-030-- @decision DEC-022CREATE TABLE users ( id SERIAL PRIMARY KEY, email VARCHAR(255) UNIQUE NOT NULL);Annotation types
Section titled “Annotation types”Links code to a behavioral specification.
# @spec SPEC-001This 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.
@decision
Section titled “@decision”Links code to an architectural decision record.
# @decision DEC-001Use 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-successdef test_login_returns_token_for_valid_credentials(): ...
# @test SPEC-001/login-failuredef 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-001def hash_password(password: str) -> str: ...Use this when code directly implements or enforces a norm rule.
Sub-IDs
Section titled “Sub-IDs”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-refreshdef refresh_token(refresh: str) -> TokenPair: ...
# @spec SPEC-001/token-validationdef 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-refreshdef test_refresh_returns_new_token_pair(): ...
# @test SPEC-001/expired-refreshdef test_refresh_rejects_expired_token(): ...Multiple annotations
Section titled “Multiple annotations”A single code element can have multiple annotations:
# @spec SPEC-001# @decision DEC-001# @norm NORM-SEC-001def 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.
Supported languages
Section titled “Supported languages”Contextia scans annotations in any language with single-line comments. The comment syntax is configured per language:
| Language | Comment syntax | File 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: "%"Scanner output
Section titled “Scanner output”The annotation scanner produces structured results:
@dataclassclass 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.
Parser modes
Section titled “Parser modes”Regex mode (default)
Section titled “Regex mode (default)”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,implkeywords 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: regexRegex mode has no external dependencies and works with any language that has single-line comments.
Tree-sitter mode
Section titled “Tree-sitter mode”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-sitterTree-sitter mode requires the optional py-tree-sitter dependency and language-specific grammar packages:
pip install contextia[tree-sitter]Comparison
Section titled “Comparison”| Feature | Regex | Tree-sitter |
|---|---|---|
| Dependencies | None | py-tree-sitter + grammars |
| Speed | Fast | Moderate |
| Context accuracy | Best-effort | Precise |
| Language coverage | Any with line comments | Languages with tree-sitter grammars |
| Nested context | May miss | Fully accurate |
Configuration options
Section titled “Configuration options”Changing the prefix
Section titled “Changing the prefix”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-003Adding custom comment syntax
Section titled “Adding custom comment syntax”For languages not in the default list:
annotations: comment_syntax: nim: "#" forth: "\\" ada: "--"Ignoring paths
Section titled “Ignoring paths”Exclude files from annotation scanning:
check: ignore_patterns: - "tests/fixtures/**" - "vendor/**" - "**/*.generated.*" - "**/*.min.js"Integrity checks
Section titled “Integrity checks”contextia check validates annotations against the knowledge layer:
| Check | Severity | Description |
|---|---|---|
| Orphan annotation | Error | @spec SPEC-999 where SPEC-999 does not exist. |
| Missing annotation | Warning | Spec lists paths: [src/auth.py] but src/auth.py has no @spec for that spec. |
| Duplicate annotation | Warning | Same @spec SPEC-001 appears multiple times in the same file on the same element. |
| Invalid ID format | Error | @spec my-spec does not match the expected ID pattern. |
With check.strict_bidirectional: true, missing annotation warnings become errors.
Best practices
Section titled “Best practices”- Annotate at the function or class level, not individual lines. The annotation represents a semantic link, not a line-level marker.
- Use sub-IDs for tests to map each test to a specific behavior in the spec.
- Keep annotations consecutive and immediately before the code element. Do not separate them with blank lines or unrelated comments.
- 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.
- Run
contextia checkafter refactoring to catch moved or deleted files that still have annotations or are still listed in spec paths.
Next steps
Section titled “Next steps”- See the Frontmatter Schemas for the spec, decision, and norm schemas that annotations link to.
- Read Writing Effective Specs for spec authoring guidelines.
- Check the Configuration Reference for all annotation settings.