Token-Sheriff is a high-performance OAuth 2.0 and OpenID Connect token validation library for Java/Quarkus applications. This document guides AI coding agents when working with the codebase.
- Build tool: Maven, via the wrapper (
./mvnw) — never a locally installedmvn - Java version: as declared by
maven.compiler.releasein the rootpom.xml - Primary framework: Quarkus, pinned by
version.quarkusin the rootpom.xml
Deliberately no version numbers here: this file is not a source of truth for them and
silently goes stale. Read the root pom.xml and .mvn/wrapper/maven-wrapper.properties
when a concrete version matters.
Multi-module Maven project:
token-sheriff-validation/- The core Token-Sheriff validation librarytoken-sheriff-quarkus-parent/- Quarkus framework integrationbenchmarking/- Performance benchmarking modulesbom/- Bill of Materials for dependency management
# Development build
./mvnw clean verify
# Full build with all tests
./mvnw clean install
# Build single module
./mvnw clean install -pl <module-name>
# Run single test
./mvnw test -Dtest=ClassName#methodName- Indentation: 4 spaces (configured in
.editorconfig) - Line endings: Unix-style (LF)
- Encoding: UTF-8
- Java features: Use the modern Java features the configured release level allows (records, sealed classes, pattern matching, text blocks)
- Lombok: Use
@Builder,@Value,@NonNull,@ToString,@EqualsAndHashCodeappropriately
This project uses CUI logging standards with Java Util Logging:
- Logger:
de.cuioss.tools.logging.CuiLogger(private static final LOGGER) - Format specifier: Always use
%sfor parameter substitution (NEVER{},%.2f,%d) - Structured logging: Use
de.cuioss.tools.logging.LogRecordfor INFO/WARN/ERROR messages - LogRecord ranges: INFO (001-099), WARN (100-199), ERROR (200-299)
- Documentation: All log messages must be documented in
doc/LogMessages.adoc - Exception logging: Exception parameter always comes first
- Primary: JUnit 5 (Jupiter)
- Test patterns: AAA pattern (Arrange-Act-Assert)
- Coverage requirement: two independent rules apply, at different granularities and on different lanes. The rule inherited from
cui-java-parentiselement=BUNDLEand gates both counters —INSTRUCTION COVEREDRATIO >= 0.80andBRANCH COVEREDRATIO >= 0.80— so it counts instructions and branches, never lines. It is declared inside the opt-incoverageprofile, so a plain./mvnw clean verifynever applies it. Intoken-sheriff-clientthe five refresh-path classes (flow.RefreshFlow,token.RotationResult,token.RefreshTokenFamily,lifecycle.RefreshScheduler,lifecycle.InMemoryTokenStore) carry a stricter per-class floor —BRANCH >= 0.80andINSTRUCTION >= 0.90— enforced on the default lane, so a plain./mvnw clean verifyfails on a refresh-path coverage regression. Seedoc/client/specification/test-strategy.adoc§ Coverage gates. - Coverage check:
./mvnw clean verify -Pcoveragefor the module-wide bundle rule; the refresh-path per-class gate needs no profile flag and already runs under./mvnw clean verify. - Always measure with
clean: the JaCoCo agent writesjacoco.execwith its defaultappend=true, so a coverage run withoutcleansilently merges earlier runs and reports stale, inflated numbers.
This project has CUI Test Generator dependencies available for test data generation:
- Provides type-safe, consistent test data generation
- Use
@CsvSourcefor simple data - Use
@ValueSourcefor single parameter variations - Use
@MethodSourcefor complex parameterization
Mandatory for 3+ similar test variants. Common annotations:
@CsvSource@ValueSource@MethodSource
# Run all tests
./mvnw test
# Run integration tests
./mvnw clean verify -Pintegration-tests -pl token-sheriff-quarkus-parent/token-sheriff-quarkus-integration-tests -am
# Run micro-benchmarks
./mvnw clean verify -pl benchmarking/benchmark-core -Pbenchmark
# Run integration benchmarks
./mvnw clean verify -Pbenchmark -pl benchmarking/benchmark-integration-wrkCRITICAL: Execute this sequence before ANY commit:
-
Quality verification:
./mvnw -Ppre-commit clean verify
- Fix ALL errors and warnings (mandatory)
- Address OpenRewrite markers (see section below)
-
Final verification:
./mvnw clean install
- Must complete without errors or warnings
- All tests must pass
-
Integration tests:
./mvnw clean verify -Pintegration-tests -pl token-sheriff-quarkus-parent/token-sheriff-quarkus-integration-tests -am
Tasks are complete ONLY after all three steps succeed.
Pre-commit builds run OpenRewrite recipes that add markers to flag violations.
Marker pattern: /*~~(TODO: INFO needs LogRecord)~~>*/ or /*~~(TODO: [message])~~>*/
What markers indicate: Markers indicate ACTUAL BUGS:
- Placeholder/parameter count mismatches:
"value: %s"with 0 parameters - Wrong format specifiers: Using
%.2f,{:.2f},{},%dinstead of%s - Missing LogRecord definitions for production INFO/WARN/ERROR logs
- Generic Exception usage instead of specific types
- RuntimeException catches that should be specific exceptions
A rewrite run produces two categorically different things, and conflating them is how markers end up committed:
- A rewrite output is a transformation the recipe already applied — formatted whitespace, ordered imports, a removed unused import. It is finished work, and it is committed as-is.
- A rewrite request-to-act is an injected
/*~~(TODO: … Suppress: …)~~>*/marker. The recipe could not fix the code itself, so it is asking a human to act. It is an open work item, never a result. Committing it verbatim ships the request instead of the response.
Worked example. Four markers were once committed to this repository verbatim — three
InvalidExceptionUsageRecipe markers in token-sheriff-client and one
CuiLogRecordPatternRecipe marker in BearerTokenProducer. Each was a request the author left
unanswered. Reading the whole rewritten tree as "the tool's result" is the mistake: the formatting
was a result, the markers were not.
Acting on a request-to-act does not always mean changing the flagged code — sometimes the correct answer is that the recipe is wrong, and the act is to record that fact in the sanctioned suppression form (see below).
Never commit code with markers present.
Production code violations:
- Fix the actual bug (add missing placeholders, change format to %s)
- Create LogRecord constant for INFO/WARN/ERROR messages
- Replace generic Exception with specific types (IOException, IllegalStateException, etc.)
- Never catch or throw RuntimeException - use specific exception types
Test code violations:
- For diagnostic/performance logging: Add suppression comment at class level:
// cui-rewrite:disable CuiLogRecordPatternRecipe // This is a test/utility class that outputs diagnostic information for analysis
- For exception handling: Replace RuntimeException with AssertionError in test failures
- For format bugs: Fix placeholder mismatches even in tests (change to %s)
Suppression is not a valid strategy for InvalidExceptionUsageRecipe. The standing remedy is
to narrow the caught exception type against the throwers the callee actually declares — except
where the caught failures come from an open set of SPI implementations that declare no
throws, in which case there is nothing to narrow against and suppression is legitimate.
That carve-out is the rule's single limit, and it is part of the rule rather than a footnote: the
tree already holds its one sanctioned instance at
token-sheriff-validation/src/main/java/de/cuioss/sheriff/token/validation/pipeline/TokenBuilder.java:143,
where custom SPI claim mappers are translated into TokenValidationException. Everywhere else,
narrow.
Two practical notes from the sites already narrowed:
- Narrow against the declared throwers, not against what a test double happens to throw. When a test double throws a type the API does not declare, the double is what is wrong.
- Where a
catchwas load-bearing for an invariant — completing a future, releasing a latch — make thefinallyblock satisfy that invariant unconditionally before narrowing the catch. Narrowing first opens a window in which an unnamed throwable escapes with the invariant broken.
The class-level suppression form above is sanctioned in production code too, not only in test and utility classes — but only for a demonstrated recipe mis-fire on a call that is already correct, and only when the suppression is accompanied by a comment recording the specific mis-fire. The demonstration is required: this sanction is for proven false positives, never for silencing an inconvenient finding.
Worked instance. BearerTokenProducer calls
LOGGER.warn(e, BEARER_TOKEN_VALIDATION_FAILED, e.getMessage(), e.getEventType()). The recipe
mis-fires on the warn(Throwable, LogRecord, Object...) overload, mistaking the leading
Throwable for the format argument; the LogRecord template carries exactly two matching %s
placeholders and the call is correct. The remedy was a class-level
// cui-rewrite:disable CuiLogRecordPatternRecipe plus that explanation.
Corollary: never contort a correct logging call to satisfy a buggy recipe.
CuiLoggerStandardsRecipe misreads the argument-less %n line-separator conversion as a value
placeholder and rewrites it to %s, adding a format placeholder with no matching argument and
corrupting the format string. This is semantic damage, not cosmetic churn.
The sanctioned remedy is a // cui-rewrite:disable CuiLoggerStandardsRecipe at the affected site
with a comment citing the upstream defect cuioss/cui-open-rewrite#135 — the form both live sites
already use (JfrVarianceAnalyzer.printSummary() and TokenValidatorMetricsTest). A format string
carrying %n must never be "fixed" by accepting the rewrite.
The failure that produced the narrow-not-suppress rule was observed for a // cui-rewrite:disable
comment placed at the closing-brace / catch position, and only there. Class-level and
method-level suppression of InvalidExceptionUsageRecipe were never tested, so they are
untested, not refuted. Treat that as a known gap for a future investigation, not as a finding.
Adjacent evidence exists but must not be over-read: this tree relies on method-level
suppression of a different recipe (CuiLoggerStandardsRecipe, at the two %n sites), and a
rewrite:dryRun against the clean tree confirmed those suppressions do take effect at that
placement. That result speaks to the placement mechanism only. It says nothing about whether
InvalidExceptionUsageRecipe honours the same placements.
-Ppre-commit no longer exits 0 after rewriting your files. Two non-mutating post-conditions run
in the verify phase, after the mutating executions and in the same reactor pass:
license:check (assert-license-headers-unchanged) and rewrite:dryRun with
failOnDryRunResults=true (assert-no-rewrite-changes). If the gate would still change a file,
the build fails and names it.
So a green -Ppre-commit run is now a genuine clean-tree signal, and manually grepping for markers
afterwards is a second line of defence rather than the only one. A red assertion is reporting a
real mutation: fix the source, suppress at the site with a recorded rationale, or — only when
neither is possible — add an <exclusions> entry to the local pre-commit profile in the root
pom.xml. Never relax the assertions, and never redeclare activeRecipes locally: the parent's
recipe list is the single source of truth.
This project is PRE-1.0 (see the root pom.xml for the current version). Therefore:
- Never deprecate code - Remove it directly if not needed
- Never add transitional comments like "TODO: Remove in v2.0"
- Never enforce backward compatibility - Make breaking changes freely
- Never add @Deprecated annotations - Delete unnecessary code immediately
- Clean APIs aggressively - Remove unused methods, classes, and patterns
- Focus on final API design - Design for post-1.0 stability
This project includes custom commands for common workflows:
Comprehensive logging standards audit:
- Analyze CUI logging standards from
/Users/oliver/git/cui-llm-rules/standards/logging - Scan for logging violations in token-sheriff-validation module
- Check LogRecord compliance
- Validate documentation in
doc/LogMessages.adoc - Run logging-related tests
- Generate compliance report
Fix all OpenRewrite TODO markers in a module:
- Locate all markers with grep
- Analyze and fix each marker (production vs test code)
- Remove markers after fixing
- Verify fixes with pre-commit build
- Final validation with full test suite
Execute comprehensive quality verification and commit workflow for a specific module:
- Quality verification build (pre-commit profile)
- Final verification build (full integration)
- Error resolution loop
- Artifact cleanup verification
- Git commit
The project includes custom skills in .claude/skills/:
run-benchmark-suite- Run full benchmark suite with ablation sweep, connection sweep, JFR profiling, and doc updates
- Format: AsciiDoc with
.adocextension - Key documents:
README.adoc- Project overviewdoc/README.adoc- Documentation hubdoc/validation/requirements.adoc- Functional requirementsdoc/validation/architecture.adoc- Architecture referencedoc/LogMessages.adoc- Logging reference
- Cross-references: Use
xref:syntax (not<<>>) - Blank lines: Required before all lists
- Header: Include TOC and section numbering
- Source highlighting: Use
:source-highlighter: highlight.js
- Every public and protected class/interface must be documented
- Include clear purpose statement in class documentation
- Document all public methods with parameters, returns, and exceptions
- Include
@sincetag with version information - Document thread-safety considerations
- Include usage examples for complex classes and methods
- Every package must have package-info.java
- Use
{@link}for references to classes, methods, and fields
- Use constructor injection (mandatory over field injection)
- Single constructor rule: No
@Injectneeded for single constructors - Use
finalfields for injected dependencies - Use
@ApplicationScopedfor stateless services - Use
@QuarkusTestfor CDI context testing - Use
@QuarkusIntegrationTestfor packaged app testing - HTTPS required for all integration tests
- Use
.plan/temp/for ALL temporary files - Covered byWrite(.plan/**)permission (avoids permission prompts) - If in doubt, ask the user - Never make assumptions
- Always research topics - Use available tools (WebSearch, WebFetch, etc.) to find recent best practices
- Never guess or be creative - If you cannot find best practices, ask the user
- Do not proliferate documents - Always use context-relevant documents, never create without user approval
- Never add dependencies without approval - Always ask before adding any dependency
Key reference files for development:
doc/LogMessages.adoc- Complete logging referencedoc/README.adoc- Documentation hubdoc/validation/requirements.adoc- Functional requirementsdoc/validation/architecture.adoc- Architecture reference.editorconfig- Code formatting configurationlombok.config- Lombok configuration