Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 66 additions & 0 deletions .gemini/rules/async-test-configuration.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<!-- VIBETAGS-START -->
# Rules for async-test-configuration

## Locked Status

### se.deversity.asynctest.DetectorType
- **Reason**: Each enum constant requires synchronized changes in five places: (1) @AsyncTest attribute, (2) AsyncTestConfig field, (3) AsyncTestConfig.Builder default, (4) both branches of AsyncTestConfig.build() (detectAll block + excludes block), and (5) DetectorRegistry constructor. Adding a value here in isolation breaks the system.

## Mirrored — Keep In Sync

### se.deversity.asynctest.DetectorType
- **Rule**: Free to change, but every mirror must change in the same commit.
- **Mirrors**: se.deversity.asynctest.AsyncTest, se.deversity.asynctest.AsyncTestConfig, se.deversity.asynctest.DetectorRegistry, se.deversity.asynctest.spi.LegacyDetectorFactories, META-INF/services/se.deversity.asynctest.spi.DetectorFactory
- **Reason**: A detector is only reachable from the public API when all of these agree. The enum constant is the name users type in @AsyncTest(excludes=...); the annotation attribute, the config field and its Builder default carry it through resolution; the registry constructor instantiates it; and the SPI factory plus its services entry are what detectAll loads. Adding the constant alone compiles and silently detects nothing.
- **Enforced by**: se.deversity.asynctest.spi.AllDetectorsSpiCoverageTest

## Context & Focus

### se.deversity.asynctest.AsyncTestConfig
- **Focus**: Maintain strict 1:1 mapping between @AsyncTest attributes, Builder fields, from(AsyncTest), build() logic, and DetectorRegistry
- **Avoid**: mutable state — this class must remain immutable after construction

### se.deversity.asynctest.DetectorRegistry
- **Focus**: Each new detector requires exactly three steps in this class: (1) a final field declaration, (2) conditional construction in the constructor keyed on the config flag, (3) an analyzeAll() call in the correct phase block. All three steps must be added together.
- **Avoid**: partial patterns — a field without construction or analysis silently skips detection

## Core Functionality

### se.deversity.asynctest.AsyncTestConfig
- **Sensitivity**: Critical
- **Note**: Adding a new detector requires synchronized changes across six places: @AsyncTest attribute, AsyncTestConfig field, Builder default, from(AsyncTest) call chain, build() detectAll/excludes blocks, and DetectorRegistry constructor.

## Immutable Type
- **Rule**: These types are immutable. Never introduce non-final fields, setters, or mutating methods.

### se.deversity.asynctest.AsyncTestConfig
- **Note**: Immutable snapshot of @AsyncTest parameters to ensure thread safety.

### se.deversity.asynctest.Preset
- **Note**: Enum constants — JVM guarantees structural immutability. Internal enabled-set is captured at class init.

## Feature Flag Gate
- **Rule**: This code is gated behind a feature flag. Preserve the flag check. Never assume the flag is always active.

### se.deversity.asynctest.AsyncTestConfig.enableBenchmarking
- **Flag**: 'async-test.benchmarking.enabled' (default: false)

### se.deversity.asynctest.AsyncTestConfig.licenseMockMode
- **Flag**: 'license.mock.mode' (default: false)

## Thread-Safety Guarantee

### se.deversity.asynctest.DetectorRegistry
- **Strategy**: SYNCHRONIZED
- **Note**: Guards conditional access to internal detector initialization and phase blocks.

## Contract-Frozen Signature

### se.deversity.asynctest.AsyncTest
- **Constraint**: You may change internal logic, but MUST NOT modify the method name, parameters, return type, or checked exceptions.
- **Reason**: Public annotation API used directly in user test methods. Attribute names, types, and defaults are part of the stable public API — any change is a breaking change for all consumers.

## Public API Surface Protection
- **Rule**: Exposes public API. Preserve signature, Javadoc, and behavior without breaking backwards or source compatibility.
- **Applies to**: `se.deversity.asynctest.AsyncTest`, `se.deversity.asynctest.Preset`
<!-- VIBETAGS-END -->
221 changes: 221 additions & 0 deletions .gemini/rules/async-test-detectors.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,221 @@
<!-- VIBETAGS-START -->
# Rules for async-test-detectors

## Performance Constraints

### se.deversity.asynctest.diagnostics.SiteCapture
- **Rule**: Optimal complexity required. O(n^2) is forbidden on hot paths.
- **Constraint**: Called from detector recordAccess paths; do not allocate when a site is already captured for a given key.

## Test-Driven Requirements
- **Rule**: Changes MUST be accompanied by a matching test update.
- **Coverage Goal**: 80%
- **Frameworks**: JUNIT_5

### se.deversity.asynctest.diagnostics.CompletableFutureBlockingCallbackDetector
- **Test Location**: src/test/java/se/deversity/asynctest/diagnostics/CompletableFutureBlockingCallbackDetectorTest.java

### se.deversity.asynctest.diagnostics.CompletableFutureObtrudeDetector
- **Test Location**: src/test/java/se/deversity/asynctest/diagnostics/CompletableFutureObtrudeDetectorTest.java

### se.deversity.asynctest.diagnostics.DaemonThreadHygieneDetector
- **Test Location**: src/test/java/se/deversity/asynctest/diagnostics/DaemonThreadHygieneDetectorTest.java

### se.deversity.asynctest.diagnostics.FileChannelPositionRaceDetector
- **Test Location**: src/test/java/se/deversity/asynctest/diagnostics/FileChannelPositionRaceDetectorTest.java

### se.deversity.asynctest.diagnostics.FinalFieldMutationDetector
- **Test Location**: src/test/java/se/deversity/asynctest/diagnostics/FinalFieldMutationDetectorTest.java

### se.deversity.asynctest.diagnostics.HighContentionAtomicDetector
- **Test Location**: src/test/java/se/deversity/asynctest/diagnostics/HighContentionAtomicDetectorTest.java

### se.deversity.asynctest.diagnostics.JdbcConnectionSharedDetector
- **Test Location**: src/test/java/se/deversity/asynctest/diagnostics/JdbcConnectionSharedDetectorTest.java

### se.deversity.asynctest.diagnostics.LazyConstantMisuseDetector
- **Test Location**: src/test/java/se/deversity/asynctest/diagnostics/LazyConstantMisuseDetectorTest.java

### se.deversity.asynctest.diagnostics.LockUpgradeDeadlockDetector
- **Test Location**: src/test/java/se/deversity/asynctest/diagnostics/LockUpgradeDeadlockDetectorTest.java

### se.deversity.asynctest.diagnostics.NonAtomicConcurrentMapUpdateDetector
- **Test Location**: src/test/java/se/deversity/asynctest/diagnostics/NonAtomicConcurrentMapUpdateDetectorTest.java

### se.deversity.asynctest.diagnostics.NotifyWithoutMonitorDetector
- **Test Location**: src/test/java/se/deversity/asynctest/diagnostics/NotifyWithoutMonitorDetectorTest.java

### se.deversity.asynctest.diagnostics.SharedByteBufferDetector
- **Test Location**: src/test/java/se/deversity/asynctest/diagnostics/SharedByteBufferDetectorTest.java

### se.deversity.asynctest.diagnostics.SharedCharsetCoderDetector
- **Test Location**: src/test/java/se/deversity/asynctest/diagnostics/SharedCharsetCoderDetectorTest.java

### se.deversity.asynctest.diagnostics.SharedChecksumDetector
- **Test Location**: src/test/java/se/deversity/asynctest/diagnostics/SharedChecksumDetectorTest.java

### se.deversity.asynctest.diagnostics.SharedDeflaterDetector
- **Test Location**: src/test/java/se/deversity/asynctest/diagnostics/SharedDeflaterDetectorTest.java

### se.deversity.asynctest.diagnostics.SharedIteratorDetector
- **Test Location**: src/test/java/se/deversity/asynctest/diagnostics/SharedIteratorDetectorTest.java

### se.deversity.asynctest.diagnostics.SharedJsonMapperReconfigDetector
- **Test Location**: src/test/java/se/deversity/asynctest/diagnostics/SharedJsonMapperReconfigDetectorTest.java

### se.deversity.asynctest.diagnostics.SharedKdfDetector
- **Test Location**: src/test/java/se/deversity/asynctest/diagnostics/SharedKdfDetectorTest.java

### se.deversity.asynctest.diagnostics.SharedMessageDigestDetector
- **Test Location**: src/test/java/se/deversity/asynctest/diagnostics/SharedMessageDigestDetectorTest.java

### se.deversity.asynctest.diagnostics.SharedSecureRandomDetector
- **Test Location**: src/test/java/se/deversity/asynctest/diagnostics/SharedSecureRandomDetectorTest.java

### se.deversity.asynctest.diagnostics.SharedStatefulCryptoDetector
- **Test Location**: src/test/java/se/deversity/asynctest/diagnostics/SharedStatefulCryptoDetectorTest.java

### se.deversity.asynctest.diagnostics.SpuriousWakeupDetector
- **Test Location**: src/test/java/se/deversity/asynctest/diagnostics/SpuriousWakeupDetectorTest.java

### se.deversity.asynctest.diagnostics.ThisEscapeDetector
- **Test Location**: src/test/java/se/deversity/asynctest/diagnostics/ThisEscapeDetectorTest.java

### se.deversity.asynctest.diagnostics.ThreadLocalRandomMisuseDetector
- **Test Location**: src/test/java/se/deversity/asynctest/diagnostics/ThreadLocalRandomMisuseDetectorTest.java

### se.deversity.asynctest.diagnostics.TryLockMisuseDetector
- **Test Location**: src/test/java/se/deversity/asynctest/diagnostics/TryLockMisuseDetectorTest.java

### se.deversity.asynctest.diagnostics.WeakHashMapSharedDetector
- **Test Location**: src/test/java/se/deversity/asynctest/diagnostics/WeakHashMapSharedDetectorTest.java

## Thread-Safety Guarantee

### se.deversity.asynctest.diagnostics.CompletableFutureBlockingCallbackDetector
- **Strategy**: OTHER
- **Note**: ThreadLocal tracks active callbacks; ConcurrentHashMap stores violations.

### se.deversity.asynctest.diagnostics.CompletableFutureObtrudeDetector
- **Strategy**: OTHER
- **Note**: ConcurrentHashMap stores state per CF instance.

### se.deversity.asynctest.diagnostics.DaemonThreadHygieneDetector
- **Strategy**: OTHER
- **Note**: Per-thread access map is a ConcurrentHashMap; first-registration-wins via putIfAbsent.

### se.deversity.asynctest.diagnostics.FileChannelPositionRaceDetector
- **Strategy**: OTHER
- **Note**: Per-instance state in ConcurrentHashMap with get-then-computeIfAbsent hot path; thread-id/name sets are ConcurrentHashMap.newKeySet() and track only implicit-position accessors.

### se.deversity.asynctest.diagnostics.FinalFieldMutationDetector
- **Strategy**: OTHER
- **Note**: Per-field state in ConcurrentHashMap with get-then-computeIfAbsent hot path; thread-id/name sets are ConcurrentHashMap.newKeySet().

### se.deversity.asynctest.diagnostics.HighContentionAtomicDetector
- **Strategy**: OTHER
- **Note**: Per-instance state in ConcurrentHashMap with get-then-computeIfAbsent hot path; counters are LongAdder; thread-id/name sets are ConcurrentHashMap.newKeySet().

### se.deversity.asynctest.diagnostics.JdbcConnectionSharedDetector
- **Strategy**: OTHER
- **Note**: ConcurrentHashMap-backed JDBC-resource tracking; per-resource State holds ConcurrentHashMap.newKeySet() for accessing threads.

### se.deversity.asynctest.diagnostics.LazyConstantMisuseDetector
- **Strategy**: OTHER
- **Note**: Per-constant state in ConcurrentHashMap with get-then-computeIfAbsent hot path; thread-id sets are ConcurrentHashMap.newKeySet(); reports are synchronized lists.

### se.deversity.asynctest.diagnostics.LockUpgradeDeadlockDetector
- **Strategy**: OTHER
- **Note**: ConcurrentHashMap tracks read lock ownership and violations.

### se.deversity.asynctest.diagnostics.NonAtomicConcurrentMapUpdateDetector
- **Strategy**: OTHER
- **Note**: Per (map,key) state in a ConcurrentHashMap with get-then-computeIfAbsent hot path; thread-id/name sets are ConcurrentHashMap.newKeySet().

### se.deversity.asynctest.diagnostics.NotifyWithoutMonitorDetector
- **Strategy**: SYNCHRONIZED
- **Note**: Attempts list mutated under a single intrinsic monitor on the list itself; sampling Thread.holdsLock requires no locking.

### se.deversity.asynctest.diagnostics.SharedByteBufferDetector
- **Strategy**: OTHER
- **Note**: Per-instance state in ConcurrentHashMap with get-then-computeIfAbsent hot path; thread-id/name and operation sets are ConcurrentHashMap.newKeySet().

### se.deversity.asynctest.diagnostics.SharedCharsetCoderDetector
- **Strategy**: OTHER
- **Note**: Per-instance state in ConcurrentHashMap with get-then-computeIfAbsent hot path; thread-id/name sets are ConcurrentHashMap.newKeySet().

### se.deversity.asynctest.diagnostics.SharedChecksumDetector
- **Strategy**: OTHER
- **Note**: Per-instance state in ConcurrentHashMap with get-then-computeIfAbsent hot path; thread-id/name sets are ConcurrentHashMap.newKeySet().

### se.deversity.asynctest.diagnostics.SharedDeflaterDetector
- **Strategy**: OTHER
- **Note**: Per-instance state in ConcurrentHashMap with get-then-computeIfAbsent hot path; thread-id/name sets are ConcurrentHashMap.newKeySet().

### se.deversity.asynctest.diagnostics.SharedIteratorDetector
- **Strategy**: OTHER
- **Note**: Per-instance state in ConcurrentHashMap with get-then-computeIfAbsent hot path; thread-id/name sets are ConcurrentHashMap.newKeySet().

### se.deversity.asynctest.diagnostics.SharedJsonMapperReconfigDetector
- **Strategy**: OTHER
- **Note**: Per-instance state in ConcurrentHashMap with get-then-computeIfAbsent hot path; using-thread sets are ConcurrentHashMap.newKeySet(); violating mutations recorded in a CopyOnWriteArrayList.

### se.deversity.asynctest.diagnostics.SharedKdfDetector
- **Strategy**: OTHER
- **Note**: Per-instance state in ConcurrentHashMap with get-then-computeIfAbsent hot path; thread-id/name sets are ConcurrentHashMap.newKeySet().

### se.deversity.asynctest.diagnostics.SharedMessageDigestDetector
- **Strategy**: OTHER
- **Note**: Per-instance state in ConcurrentHashMap with get-then-computeIfAbsent hot path; thread-id/name sets are ConcurrentHashMap.newKeySet().

### se.deversity.asynctest.diagnostics.SharedSecureRandomDetector
- **Strategy**: OTHER
- **Note**: Per-instance state in ConcurrentHashMap with double-check (get-then-computeIfAbsent) hot path; thread-id/name sets are ConcurrentHashMap.newKeySet().

### se.deversity.asynctest.diagnostics.SharedStatefulCryptoDetector
- **Strategy**: OTHER
- **Note**: Per-instance state in ConcurrentHashMap with double-check (get-then-computeIfAbsent) hot path; thread-id/name sets are ConcurrentHashMap.newKeySet().

### se.deversity.asynctest.diagnostics.SpuriousWakeupDetector
- **Strategy**: OTHER
- **Note**: ConcurrentHashMap stores state per monitor instance.

### se.deversity.asynctest.diagnostics.ThisEscapeDetector
- **Strategy**: OTHER
- **Note**: Per-instance state in ConcurrentHashMap with get-then-computeIfAbsent hot path; escape descriptions and observer-thread sets are ConcurrentHashMap.newKeySet(); the completed flag is volatile.

### se.deversity.asynctest.diagnostics.ThreadLocalRandomMisuseDetector
- **Strategy**: OTHER
- **Note**: Per-instance state in ConcurrentHashMap with get-then-computeIfAbsent hot path; misusing-thread sets are ConcurrentHashMap.newKeySet().

### se.deversity.asynctest.diagnostics.TryLockMisuseDetector
- **Strategy**: OTHER
- **Note**: ConcurrentHashMap tracks tryLock attempts, results, and unlock violations.

### se.deversity.asynctest.diagnostics.WeakHashMapSharedDetector
- **Strategy**: OTHER
- **Note**: ConcurrentHashMap-backed instance tracking; per-instance State holds ConcurrentHashMap.newKeySet() for thread ids/names.

## Security-Critical Code
- **Rule**: This code is security-critical. Do not weaken security properties. Every change must be explicitly reviewed for security impact.

### se.deversity.asynctest.diagnostics.SharedMessageDigestDetector
- **Aspect**: cryptography (hash integrity / MAC / signature state)

### se.deversity.asynctest.diagnostics.SharedSecureRandomDetector
- **Aspect**: cryptography (RNG quality)

### se.deversity.asynctest.diagnostics.SharedStatefulCryptoDetector
- **Aspect**: cryptography (confidentiality / integrity / authenticity state)

## Immutable Type

### se.deversity.asynctest.diagnostics.SiteCapture.Site
- **Rule**: This type is immutable. Never introduce non-final fields, setters, or mutating methods.
- **Note**: Java record — fields are final by language; types are all primitives or String.

## Public API Surface Protection

### se.deversity.asynctest.diagnostics.SiteCapture.Site
- **Rule**: Exposes public API. Preserve signature, Javadoc, and behavior without breaking backwards or source compatibility.
<!-- VIBETAGS-END -->
15 changes: 15 additions & 0 deletions .gemini/rules/async-test-instrumentation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<!-- VIBETAGS-START -->
# Rules for async-test-instrumentation

## Core Functionality

### se.deversity.asynctest.agent.AsyncTestAgent
- **Sensitivity**: Critical
- **Note**: The INSTALLED gate must stay at-most-once per JVM: every entry point (premain, agentmain, selfAttach) races on the same compareAndSet, and a second transformer would double-weave field accessors and double-count every access. premain installs without retransformation because classes are woven as they load; agentmain must keep RETRANSFORMATION + disableClassFormatChanges(), which is only safe while the Advice stays a method-entry prologue that adds no fields, methods or interfaces. Nothing may throw out of premain — an exception there aborts JVM startup. The Premain-Class / Agent-Class manifest entries live in this module's jar, which is why attaching uses -javaagent:async-test-agent.jar.

## Contract-Frozen Signature

### se.deversity.asynctest.agent.AgentOptions
- **Constraint**: You may change internal logic, but MUST NOT modify the method name, parameters, return type, or checked exceptions.
- **Reason**: The class is package-private but the agentArgs grammar it parses is public surface: users type it on the -javaagent: command line. Key names (includes/excludes/debug), the comma-or-semicolon separator, the bare-token continuation that lets one key carry several values, and case-insensitive key matching are all part of that contract — changing any of them breaks existing launch scripts silently. Parsing must stay total: it is called from premain, where a thrown exception aborts JVM startup, so unknown keys are ignored and malformed input degrades to the default instrument-everything behaviour rather than failing.
<!-- VIBETAGS-END -->
Loading
Loading