Component
Core Engine
Description
Actual behavior:
13 fields across the engine layer (io.naftiko core + io.naftiko.engine.*) are declared volatile on non-thread-safe types — volatile NaftikoSpec, volatile List<ServerAdapter>, volatile Map<String, Object>, volatile StdioJsonRpcHandler, volatile Thread, etc. These are flagged by SonarQube rule java:S3077 — "Non-thread-safe fields should not be volatile".
volatile only publishes the reference, not the contents: if thread A reads volatile List<ServerAdapter> and thread B mutates the list elements, behavior is undefined. Engine classes have stronger concurrency requirements than spec POJOs because they are touched on the request path:
Capability.java collects adapters during start() and exposes them to request threads.
- A future "hot reload via Control port" feature will mutate these fields at runtime.
TelemetryBootstrap holds a process-wide singleton handed off across threads.
Expected behavior:
Apply the same AtomicReference<T> + immutable snapshot pattern established in Phase 2 (#422 / PR #423). For the singleton holder in TelemetryBootstrap, prefer the initialization-on-demand holder idiom when a true singleton — fall back to AtomicReference only if reset at runtime is needed.
Steps to Reproduce
- Run the full SonarQube scan (Quality Gate workflow).
- Filter issues by rule =
java:S3077 AND component path under io.naftiko.engine.* or io.naftiko.Capability.
- Observe 9 occurrences flagged across the engine layer (run #1516,
main @ dcfd01c).
By file:
| File |
Volatile fields |
S3077 hits |
io/naftiko/Capability.java |
7 |
7 |
io/naftiko/engine/consumes/ClientAdapter.java |
2 |
varies |
io/naftiko/engine/exposes/mcp/McpServerAdapter.java |
2 |
varies |
io/naftiko/engine/scripting/ScriptStepExecutor.java |
1 |
1 |
io/naftiko/engine/observability/TelemetryBootstrap.java |
1 (static) |
1 |
Capability File (if relevant)
Not capability-specific.
Logs & Stacktrace
Not a runtime failure (latent thread-safety issue). Sonar evidence: Quality Gate run #1516 — main @ dcfd01c.
Version
1.0.0-alpha3-SNAPSHOT (current main)
Runtime
JVM
Agent Context (optional)
agent_name: GitHub Copilot
llm: claude-opus-4.7
tool: copilot-chat
confidence: high
source_event: SonarQube quality gate run #1516 (main @ dcfd01c)
discovery_method: code_review
files_suspected:
- src/main/java/io/naftiko/Capability.java
- src/main/java/io/naftiko/engine/consumes/ClientAdapter.java
- src/main/java/io/naftiko/engine/exposes/mcp/McpServerAdapter.java
- src/main/java/io/naftiko/engine/observability/TelemetryBootstrap.java
- src/main/java/io/naftiko/engine/scripting/ScriptStepExecutor.java
Apply the same Pattern A/B/C established in Phase 2:
| File |
Field |
Recommendation |
Capability.java |
spec |
AtomicReference<NaftikoSpec> |
Capability.java |
serverAdapters |
AtomicReference<List<ServerAdapter>> with List.copyOf |
Capability.java |
clientAdapters |
AtomicReference<List<ClientAdapter>> with List.copyOf |
Capability.java |
aggregates |
AtomicReference<List<Aggregate>> with List.copyOf |
Capability.java |
bindings |
AtomicReference<Map<String, Object>> with Map.copyOf |
Capability.java |
scriptingSpec |
AtomicReference<ScriptingManagementSpec> |
Capability.java |
stepHandlerRegistry |
AtomicReference<StepHandlerRegistry> |
ClientAdapter.java |
capability, spec |
AtomicReference<T> |
McpServerAdapter.java |
stdioHandler, stdioThread |
AtomicReference<T> |
ScriptStepExecutor.java |
scriptingSpec |
AtomicReference<ScriptingManagementSpec> |
TelemetryBootstrap.java |
instance (static) |
Initialization-on-demand holder idiom or AtomicReference<TelemetryBootstrap> if reset is needed |
Public API contract preserved: getters return plain types. Setters take plain types and store immutable snapshots for collections.
Scope of this issue: 5 engine files. Spec POJO layer (Phase 2, #422) and char[] auth fields (Phase 4) are tracked separately.
Out of scope: fluent builder methods on engine classes — they will follow once the spec layer builders land.
Component
Core Engine
Description
Actual behavior:
13 fields across the engine layer (
io.naftikocore +io.naftiko.engine.*) are declaredvolatileon non-thread-safe types —volatile NaftikoSpec,volatile List<ServerAdapter>,volatile Map<String, Object>,volatile StdioJsonRpcHandler,volatile Thread, etc. These are flagged by SonarQube rulejava:S3077— "Non-thread-safe fields should not bevolatile".volatileonly publishes the reference, not the contents: if thread A readsvolatile List<ServerAdapter>and thread B mutates the list elements, behavior is undefined. Engine classes have stronger concurrency requirements than spec POJOs because they are touched on the request path:Capability.javacollects adapters duringstart()and exposes them to request threads.TelemetryBootstrapholds a process-wide singleton handed off across threads.Expected behavior:
Apply the same
AtomicReference<T>+ immutable snapshot pattern established in Phase 2 (#422 / PR #423). For the singleton holder inTelemetryBootstrap, prefer the initialization-on-demand holder idiom when a true singleton — fall back toAtomicReferenceonly if reset at runtime is needed.Steps to Reproduce
java:S3077AND component path underio.naftiko.engine.*orio.naftiko.Capability.main@dcfd01c).By file:
io/naftiko/Capability.javaio/naftiko/engine/consumes/ClientAdapter.javaio/naftiko/engine/exposes/mcp/McpServerAdapter.javaio/naftiko/engine/scripting/ScriptStepExecutor.javaio/naftiko/engine/observability/TelemetryBootstrap.javastatic)Capability File (if relevant)
Not capability-specific.
Logs & Stacktrace
Not a runtime failure (latent thread-safety issue). Sonar evidence: Quality Gate run #1516 —
main@dcfd01c.Version
1.0.0-alpha3-SNAPSHOT(currentmain)Runtime
JVM
Agent Context (optional)
Proposed Fix (Phase 3 of
sonar-bug-remediation)Apply the same Pattern A/B/C established in Phase 2:
Capability.javaspecAtomicReference<NaftikoSpec>Capability.javaserverAdaptersAtomicReference<List<ServerAdapter>>withList.copyOfCapability.javaclientAdaptersAtomicReference<List<ClientAdapter>>withList.copyOfCapability.javaaggregatesAtomicReference<List<Aggregate>>withList.copyOfCapability.javabindingsAtomicReference<Map<String, Object>>withMap.copyOfCapability.javascriptingSpecAtomicReference<ScriptingManagementSpec>Capability.javastepHandlerRegistryAtomicReference<StepHandlerRegistry>ClientAdapter.javacapability,specAtomicReference<T>McpServerAdapter.javastdioHandler,stdioThreadAtomicReference<T>ScriptStepExecutor.javascriptingSpecAtomicReference<ScriptingManagementSpec>TelemetryBootstrap.javainstance(static)AtomicReference<TelemetryBootstrap>if reset is neededPublic API contract preserved: getters return plain types. Setters take plain types and store immutable snapshots for collections.
Scope of this issue: 5 engine files. Spec POJO layer (Phase 2, #422) and
char[]auth fields (Phase 4) are tracked separately.Out of scope: fluent builder methods on engine classes — they will follow once the spec layer builders land.