# VibeTags — Agent Instructions

Start with [llms.txt](llms.txt) for a map of this repo's docs and layout.

The primary agent briefing is [CLAUDE.md](CLAUDE.md) — build commands, build
order, architecture invariants, and the `<project_guardrails>` block (locked
files, contract-frozen signatures, hot-path constraints). It is tool-neutral
despite the name: read and follow it whichever agent you are.

Ground rules for any agent working here:

- Respect the guardrails in CLAUDE.md: never edit files/elements listed as
  locked, never change contract-frozen signatures.
- Never hand-edit content between `VIBETAGS-START`/`VIBETAGS-END` markers in
  any file — it is generated by this project's own annotation processor.
- Build order matters: `vibetags-annotations` → `vibetags` → `vibetags-bom`
  → `example`. Always build from the subproject's own directory.
- Reference docs load on demand: `docs/ANNOTATIONS.md` (annotation reference),
  `docs/PLATFORMS.md` (output-file table), `docs/TESTS.md` (test coverage map),
  `docs/ARCHITECTURE.md` (deep dive), `docs/DEPENDENCIES.md` (third-party artifacts).

Note: this file is intentionally not VibeTags-managed — other AI config files
exist at the root, so the processor's AGENTS.md sole-file fallback leaves it
untouched.

<!-- VIBETAGS-START -->
# AUTO-GENERATED AI RULES
# Generated by VibeTags | https://github.com/PIsberg/vibetags
# Do not edit manually.

## LOCKED FILES (DO NOT EDIT)
- **se.deversity.vibetags.processor.AIGuardrailProcessor.generateFiles()**: Step order is load-bearing: fingerprint check → sidecar write → sidecar read → merge → file write → cache flush; reordering steps silently skips regeneration or corrupts multi-module output
- **se.deversity.vibetags.processor.internal.TransitiveManifest.RESOURCE_PACKAGE**: Must stay a valid Java package name. javac's CLASS_PATH location skips archive directories that are not package identifiers, so moving these manifests under META-INF/ leaves Filer.getResource listing zero entries and transitive discovery fails silently while the conventional location looks correct. TransitiveManifestPathTest pins the working path.
- **se.deversity.vibetags.processor.model.GuardrailAnnotations.ALL**: Append only. This order fixes the insertion order of every LinkedHashSet downstream, so reordering or removing an entry rewrites generated files in every consuming build, with nothing failing to name the cause. BuildFingerprint hashes in its own separately pinned order; the two are not the same list and must not be aligned.

## CONTEXTUAL RULES
- `se.deversity.vibetags.processor.internal.AnnotationCollector`: Focus on Accumulates annotated elements across multiple javac processing rounds, then snapshots them into a compiler-free GuardrailModel. Ordering is settled in GuardrailModel, which sorts every bucket by TaggedElement.path() — javac's getElementsAnnotatedWith has no specified iteration order, so anything that preserves it makes generated output depend on which machine compiled it. Avoid Restoring javac's iteration order as the output order, here or in GuardrailModel — it differs between Maven and Gradle and between machines, which churns committed guardrail files and misses the write cache. OutputOrderDeterminismTest pins it.
- `se.deversity.vibetags.processor.internal.GranularRulesWriter`: Focus on Writes granular rule files (per-class, or role-grouped when .vibetags-roles is present) for Cursor, Windsurf, Trae, Roo, and similar platforms; cleanup runs AFTER write to avoid delete-then-recreate cycles. Avoid Running cleanup before write — would delete files that are about to be recreated, causing spurious filesystem events and empty windows for incremental build tools.
- `se.deversity.vibetags.processor.internal.ServiceRegistry`: Focus on Maps platform service keys to output file paths; resolves active services by checking file existence on disk. Avoid Creating output files that do not already exist — file presence on disk is the user's explicit opt-in signal.

## 🧠 CORE FUNCTIONALITY
The following elements are well-tested core components. Make changes with extreme caution.

- **se.deversity.vibetags.processor.AIGuardrailProcessor** (sensitivity: critical): JSR 269 entry point; orchestrates annotation discovery, fingerprint short-circuit, sidecar aggregation, and all file writes
- **se.deversity.vibetags.processor.internal.GuardrailFileWriter** (sensitivity: high): Atomic marker-aware file writer; invariant: hand-authored content outside VIBETAGS-START/END markers must never be overwritten or lost
- **se.deversity.vibetags.processor.internal.ModuleSidecar** (sensitivity: high): Per-module sidecar for multi-module Maven/Gradle builds; the .vibetags-mod-* file format is shared across independently compiled modules — format changes break backward compatibility
- **se.deversity.vibetags.processor.internal.PartialRoundDetector** (sensitivity: high): Both conditions in unreadAnnotatedSources are load-bearing and neither may be dropped as redundant: without the missing-element check an excluded-but-annotated source stops VibeTags writing for good, and without the unread-source check a genuinely deleted annotation can never have its rule file retired
- **se.deversity.vibetags.processor.internal.WriteCache** (sensitivity: high): Per-file content cache backed by .vibetags-cache; false positives (wrongly treating stale output as unchanged) would silently corrupt generated files

## ⚡ PERFORMANCE CONSTRAINTS
Hot-path elements — never introduce O(n²) or worse. Always reason about complexity before proposing changes.

- **se.deversity.vibetags.processor.internal.BuildFingerprint.fingerprint(java.lang.String)**: O(N) in string length; uses String.hashCode() which HotSpot intrinsifies on x86; must not allocate intermediate byte[]
- **se.deversity.vibetags.processor.internal.WriteCache.isUnchanged(java.nio.file.Path,java.lang.String)**: O(1): one stat(2) syscall plus one 8-char string compare; must not allocate byte[] — the prior CRC32C implementation did and was removed for this reason

## 🔐 CONTRACT-FROZEN SIGNATURES
Internal logic may be modified, but never change method names, parameter types, parameter order, return types, or checked exceptions.

- **se.deversity.vibetags.processor.AIGuardrailProcessor.process(java.util.Set<? extends javax.lang.model.element.TypeElement>,javax.annotation.processing.RoundEnvironment)**: JSR 269 contract: must return false so peer annotation processors can claim the same annotations; return type is fixed by AbstractProcessor
- **se.deversity.vibetags.processor.internal.BuildFingerprint.compute(se.deversity.vibetags.processor.internal.AnnotationCollector,java.util.Set<java.lang.String>)**: Same inputs must always produce the same 8-hex output across JVM restarts; changing the algorithm silently invalidates all existing .vibetags-cache files
- **se.deversity.vibetags.processor.internal.GuardrailFileWriter.writeFileIfChanged(java.lang.String,java.lang.String,boolean)**: Public API since v0.1; tests and the processor both bind to the (String path, String content, boolean hasNewRules) signature and return semantics
- **se.deversity.vibetags.processor.internal.ModuleSidecar.mergeFor(java.lang.String,java.util.List<se.deversity.vibetags.processor.internal.ModuleSidecar>,boolean)**: Sub-marker format constants (SUB_MARKER_*_FORMAT) are embedded in generated CLAUDE.md and .cursorrules; changing them silently corrupts multi-module merged output on the next compile

## 🧪 TEST-DRIVEN REQUIREMENTS
Changes to the following elements MUST be accompanied by a matching test update in the same response.

- **se.deversity.vibetags.processor.AIGuardrailProcessor**: Coverage goal: 90%. Framework: JUNIT_5. Mock policy: Write the failing test first, against the real javac through ProcessorTestHarness; Mockito only where a ProcessingEnvironment cannot be real.
- **se.deversity.vibetags.processor.internal.GuardrailFileWriter**: Coverage goal: 90%. Framework: JUNIT_5. Mock policy: Write the failing test first; marker preservation is asserted on real files with hand content around the block, never on string fixtures alone.
- **se.deversity.vibetags.processor.internal.ModuleSidecar**: Coverage goal: 90%. Framework: JUNIT_5. Mock policy: Write the failing test first; the sidecar format is cross-module law, so tests read and write real .vibetags-mod-* files, never mocks of them.
- **se.deversity.vibetags.processor.internal.WriteCache**: Coverage goal: 90%. Framework: JUNIT_5. Mock policy: Write the failing test first; drive real files in a temp dir, never a mocked filesystem (a false cache positive silently corrupts output).

## 🧵 THREAD-SAFE BY DESIGN
These elements are explicitly designed to be thread-safe. Preserve the synchronization invariant on every change.

- **se.deversity.vibetags.processor.VibeTagsLogger**: Strategy: THREAD_LOCAL. Note: Per-thread project-root tracking partitions Logback loggers by root, so parallel compilations never detach each other's appenders (VibeTagsLoggerAsyncTest proves it)
- **se.deversity.vibetags.processor.internal.EnforcementBaseline**: Strategy: SYNCHRONIZED. Note: update() alone is safe, and across processes as well as threads: a per-root monitor plus an exclusive lock on .vibetags-baseline.lock serialise the re-read and rename that a parallel reactor's modules run against one shared file. The read side is an unguarded snapshot on purpose
- **se.deversity.vibetags.processor.internal.GuardrailFileWriter**: Strategy: IMMUTABLE. Note: Stateless aside from injected Messager/Logger references; every write is an atomic temp-file replace, so the parallel write phase never interleaves partial content (GuardrailFileWriterAsyncTest proves it)
- **se.deversity.vibetags.processor.internal.ModuleSidecar**: Strategy: OTHER. Note: Atomic temp-file moves (ATOMIC_MOVE with plain-move fallback); concurrent saves and reads never tear a sidecar or prune a sibling's (ModuleSidecarAsyncTest proves it)
- **se.deversity.vibetags.processor.internal.WriteCache**: Strategy: SYNCHRONIZED. Note: Safe for concurrent calls on one instance (WriteCacheAsyncTest proves it); instances must own disjoint roots, because two instances over the same .vibetags-cache race by design
- **se.deversity.vibetags.processor.internal.validation.ValidationRule**: Strategy: IMMUTABLE. Note: Implementations must hold no state. ValidationRules keeps one instance per rule for the life of the JVM, and a Gradle daemon runs that instance against many unrelated compilations in sequence, so a field added here carries one project's elements into another project's diagnostics. Everything a check needs arrives as the ValidationContext and Element arguments.

## ❄️ IMMUTABLE TYPES
The following types are immutable. Do not introduce non-final fields, setters, or mutating methods.

- **se.deversity.vibetags.processor.internal.BuildFingerprint**: Purely stateless; private constructor prevents instantiation; all computation results are returned as values

## 🏛️ ARCHITECTURAL BOUNDARY CONSTRAINTS
Strict layering must be respected. No illegal boundary crossing references:

- **se.deversity.vibetags.processor.internal.content**: Belongs to layer: `rendering`. Prohibited from referencing: [javax.lang.model, javax.annotation.processing, javax.tools, com.sun.source, se.deversity.vibetags.processor.internal]
- **se.deversity.vibetags.processor.model**: Belongs to layer: `model`. Prohibited from referencing: [javax.lang.model, javax.annotation.processing, javax.tools, com.sun.source, se.deversity.vibetags.processor, se.deversity.vibetags.processor.internal]

## 🔐 SECURITY-CRITICAL CODE
Do not weaken security properties of these elements. Review every change for security impact:

- **se.deversity.vibetags.processor.internal.JsonValueSpans**: Security-critical code [Splices annotation text, including attributes copied out of third-party dependency JARs, into greptile.json and .greptile/config.json, review configurations the user owns. The span body must stay Escape.json-encoded and marker-defused: without the first a dependency can close the string and add settings such as skipReview, and without the second it can end the span early so the value grows a copy of itself on every build.]. Do not weaken security properties. Flag any change for security review.
- **se.deversity.vibetags.processor.internal.TransitiveManifestReader**: Security-critical code [Trust boundary. Manifests read here are authored by third-party dependency JARs, and their rules are merged into the consumer's always-loaded instruction files, so a dependency can put text in front of the consumer's agent. Treat every value as untrusted input: keep the MAX_LOOKUPS cap and the SKIPPED_PREFIXES list, and route interpolation through Escape rather than widening what a manifest may contain.]. Do not weaken security properties. Flag any change for security review.
- **se.deversity.vibetags.processor.internal.content.Escape**: Security-critical code [Output encoding for the generated instruction files. Every interpolated value reaches an aggregate through here, including annotation attributes copied verbatim out of third-party dependency JARs; a weakened method lets that text close a tag and forge its own <locked_files> or <rule> entries in a file the agent loads on every session.]. Do not weaken security properties. Flag any change for security review.

## 🧩 LOAD-BEARING ODDITIES
These look wrong, redundant, or over-defensive and are deliberate. Refactoring is allowed only while the stated invariant survives.

- **se.deversity.vibetags.processor.internal.JsonValueSpans**: Looks removable but is deliberate. Invariant: Edits are offsets spliced into the original text; the document is validated with Json but never parsed and re-serialised. Breaks if changed: Re-serialising rewrites key order, whitespace, number spelling and the user's own escape sequences in a file VibeTags does not own, on every build, with nothing failing except the byte-preservation cases in JsonValueSpansTest and GreptileEndToEndTest.
- **se.deversity.vibetags.processor.internal.content.PlatformRenderer**: Looks removable but is deliberate. Invariant: A renderer whose output is YAML declares mergeShape(); a renderer whose marker-free output varies per module declares wholeFileMerge(). The defaults return null, which means plain concatenation. Breaks if changed: Silent data loss across a reactor. Concatenated YAML repeats a top-level key, so the parse either fails or keeps only the last module; a marker-free file is a whole-file overwrite, so it ends up holding one module's view of the whole project. Neither shows up in a single-module build, which is where a new renderer gets tested.

# AUTO-GENERATED AI RULES
# Generated by VibeTags | https://github.com/PIsberg/vibetags
# Do not edit manually.

## LOCKED FILES (DO NOT EDIT)
- **se.deversity.vibetags.annotations.AnnotationDefinitionsTest.TestLockedClass**: Do not modify this code under any circumstances.
- **se.deversity.vibetags.annotations.AnnotationDefinitionsTest.testAILockedCanBeUsedOnMethods()**: Test reason

## CONTEXTUAL RULES

## 🛡️ MANDATORY SECURITY AUDITS
When proposing edits or writing code for the following files, you MUST perform a security review before outputting the final code. You must explicitly state in your response that you have audited the changes for the required vulnerabilities.

* `se.deversity.vibetags.annotations.AnnotationDefinitionsTest.TestAuditClass`
  - Required Checks: XSS
* `se.deversity.vibetags.annotations.AnnotationDefinitionsTest.testAIAuditCanBeUsedOnMethods()`
  - Required Checks: SQL Injection, Thread Safety

## IGNORED ELEMENTS
The following elements must be completely excluded from AI context and completions:

- `se.deversity.vibetags.annotations.AnnotationDefinitionsTest.TestIgnoreClass` - Auto-generated code
- `se.deversity.vibetags.annotations.AnnotationDefinitionsTest.testAIIgnoreCanBeUsedOnMethods()` - Test reason

## 🔒 PII / PRIVACY GUARDRAILS
The following elements handle PII. Never include their runtime values in logs,
console output, external API calls, test fixtures, or mock data.

- `se.deversity.vibetags.annotations.AnnotationDefinitionsTest.TestPrivacyClass`: Test PII field
- `se.deversity.vibetags.annotations.AnnotationDefinitionsTest.testAIPrivacyCanBeUsedOnMethods()`: Test reason

## 🧠 CORE FUNCTIONALITY
The following elements are well-tested core components. Make changes with extreme caution.

- **se.deversity.vibetags.annotations.AnnotationDefinitionsTest.TestCoreClass** (sensitivity: High): Core business logic
- **se.deversity.vibetags.annotations.AnnotationDefinitionsTest.testAICoreCanBeUsedOnMethods()** (sensitivity: Critical): Test core logic

## 🔐 SECURITY-CRITICAL CODE
Do not weaken security properties of these elements. Review every change for security impact:

- **se.deversity.vibetags.processor.internal.TransitiveManifestReaderLimitsTest**: Security-critical code [Enforces the trust boundary: manifests come from third-party dependency JARs and their text is merged into the consumer's always-loaded instruction files. These cases are the MAX_LOOKUPS cap and the SKIPPED_PREFIXES list; relaxing one to make a test pass widens what a dependency may put in front of an agent]. Do not weaken security properties. Flag any change for security review.

Guardrails for test code are in TESTING.md. Read it before modifying anything under a test source set.
<!-- VIBETAGS-END -->
