Skip to content

Commit 19eebf3

Browse files
committed
docs: fix 29 doc hallucinations caught by adversarial audit (incl. one from this session)
Correct broken Spring Boot YAML config keys, fabricated sandbox API, non-existent durable-hitl sample link, wrong rag dep versions, capability-matrix prose (TOOL_CALL_DELTA/CANCELLATION/Alibaba/EmbeddingRuntime), and overclaiming javadoc.
1 parent 8a976d2 commit 19eebf3

13 files changed

Lines changed: 107 additions & 68 deletions

File tree

MIGRATION.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -397,7 +397,7 @@ atmosphere:
397397
packages: com.yourapp.atmosphere
398398
servlet-path: /atmosphere/*
399399
session-support: false
400-
heartbeat-interval-in-seconds: 60
400+
heartbeat-interval: 60s
401401
init-params:
402402
org.atmosphere.websocket.maxIdleTime: "300000"
403403
```
@@ -615,7 +615,7 @@ Configuration is done via init-params:
615615
## 6. Client Library Migration (atmosphere.js)
616616

617617
This is a **breaking change**. The old `atmosphere.js` (jQuery-based, 2.x/3.x)
618-
has been replaced with a modern TypeScript library (5.0.0).
618+
has been replaced with a modern TypeScript library (5.x).
619619

620620
### Installation
621621

@@ -929,8 +929,8 @@ Spring Boot configuration:
929929
atmosphere:
930930
durable-sessions:
931931
enabled: true
932-
session-ttl-minutes: 1440
933-
cleanup-interval-seconds: 60
932+
session-ttl: 1440m
933+
cleanup-interval: 60s
934934
```
935935
936936
### AI/LLM Streaming

README.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ Atmosphere is built for teams that need AI agents to behave like production serv
2727
| Stream to real clients | WebSocket, SSE, long-polling, and gRPC run through one broadcaster pipeline as always-on defaults; WebTransport over HTTP/3 is optional (needs `jetty-http3-server` or `reactor-netty-http` on the classpath plus a dev cert) |
2828
| Swap AI integrations | One `AgentRuntime` SPI with twelve runtime adapters and contract-tested capability flags |
2929
| Govern execution | Policy admission, `@AgentScope`, human approval, plan-and-verify, cost ceilings, PII rewriting, and admin kill switches |
30-
| Pause for humans | Durable HITL approvals park virtual threads, persist workflow state, and resume through REST approval surfaces |
30+
| Pause for humans | Durable HITL approvals hibernate without holding a thread, persist workflow state, and resume through REST approval surfaces |
3131
| Resume long runs | Durable sessions, run IDs, replay buffers, checkpoints, and reconnect-safe continuation |
3232
| Expose the same agent everywhere | Browser endpoints plus MCP (stateless **2026-07-28** RC + sessions back to 2024-11-05), A2A, AG-UI, Slack, Telegram, Discord, WhatsApp, and Messenger modules |
3333

@@ -242,7 +242,6 @@ For Java/Kotlin clients, use [wAsync](modules/wasync/) for async WebSocket, SSE,
242242
| [startup team](samples/spring-boot-multi-agent-startup-team/) | `@Coordinator` with A2A specialists, governance, checkpoints, skills, admin control plane |
243243
| [ai-chat](samples/spring-boot-ai-chat/) | Streaming AI chat with auth, caching, and runtime adapter portability |
244244
| [ai-tools](samples/spring-boot-ai-tools/) | Framework-agnostic `@AiTool` methods and approval gates |
245-
| [durable-hitl](samples/spring-boot-durable-hitl/) | Human approval gates that persist, resume, and replay across reconnects |
246245
| [checkpoint-agent](samples/spring-boot-checkpoint-agent/) | Checkpointed `@Coordinator` workflow with REST approval/resume |
247246
| [ai-classroom](samples/spring-boot-ai-classroom/) | Multi-room collaborative AI with React Native / Expo client |
248247
| [guarded-email-agent](samples/spring-boot-guarded-email-agent/) | Plan-and-verify taint protection before any email tool fires |

cli/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ Every template sparse-clones the matching sample from `cli/samples.json` into th
190190
| `guarded-agent`| `spring-boot-guarded-email-agent` | Plan-and-Verify (Meijer) — refuses unsafe LLM-emitted plans before any tool fires |
191191
| `ms-governance`| `spring-boot-ms-governance-chat` | Governance demo: policy admission, decision viewer, kill switch, write-gated admin endpoints |
192192
| `assistant` | `spring-boot-personal-assistant` | Long-lived memory-bearing assistant: AgentState + AgentWorkspace + AgentIdentity + ProtocolBridge |
193-
| `multi-agent` | `spring-boot-multi-agent-startup-team` | Fleet of 5 independent `@Agent` classes collaborating over A2A |
193+
| `multi-agent` | `spring-boot-multi-agent-startup-team` | A `@Coordinator` (CEO) dispatching to 4 `@Agent` specialists over A2A |
194194
| `classroom` | `spring-boot-ai-classroom` | Shared streaming AI responses across web + Expo React Native clients |
195195

196196
⭐ marks the five **flagship enterprise templates** — the canonical agent

docs/governance-policy-plane.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -87,10 +87,10 @@ Implementations must be thread-safe, side-effect-free (except for metrics/loggin
8787
`org.atmosphere.ai.governance.PolicyParser` — parse a declarative artifact into `List<GovernancePolicy>`. The SPI contract supports `java.util.ServiceLoader` discovery; three implementations ship in-tree:
8888

8989
- **`YamlPolicyParser`** (`format() = "yaml"`, `modules/ai`) — SnakeYAML `SafeConstructor` (no arbitrary class instantiation). Auto-detects Atmosphere-native vs Microsoft Agent Governance Toolkit schema by inspecting the root keys. Registered via `META-INF/services/org.atmosphere.ai.governance.PolicyParser`, so adding `atmosphere-ai` to the classpath is enough to wire it up.
90-
- **`RegoPolicyParser`** (`modules/ai-policy-rego`) — wraps an external OPA process via `RegoEvaluator`. **Wired programmatically**: instantiate with `new RegoPolicyParser(registry)` and pass it where you'd otherwise consume a `YamlPolicyParser`. No `META-INF/services` entry ships today, so adding the dependency alone does not auto-discover it.
91-
- **`CedarPolicyParser`** (`modules/ai-policy-cedar`) — Cedar policy text via `CedarAuthorizer` / `CedarCliAuthorizer`. Same posture as Rego: programmatic wiring, no `META-INF/services` entry.
90+
- **`RegoPolicyParser`** (`modules/ai-policy-rego`) — wraps an external OPA process via `RegoEvaluator`. Registered via `META-INF/services/org.atmosphere.ai.governance.PolicyParser` (same as `YamlPolicyParser`), so adding `atmosphere-ai-policy-rego` to the classpath is enough to auto-discover it. Programmatic wiring (`new RegoPolicyParser(registry)`) remains available for callers who want explicit control.
91+
- **`CedarPolicyParser`** (`modules/ai-policy-cedar`) — Cedar policy text via `CedarAuthorizer` / `CedarCliAuthorizer`. Same posture as Rego: ships a `META-INF/services/org.atmosphere.ai.governance.PolicyParser` entry, so adding `atmosphere-ai-policy-cedar` to the classpath auto-discovers it.
9292

93-
Third-party parsers can register either path. ServiceLoader auto-discovery is the SPI-level recipe (ship a `PolicyParser` impl plus a `META-INF/services/org.atmosphere.ai.governance.PolicyParser` entry); programmatic wiring is the recipe the in-tree Rego/Cedar adapters use today.
93+
Third-party parsers can register either path. ServiceLoader auto-discovery is the SPI-level recipe (ship a `PolicyParser` impl plus a `META-INF/services/org.atmosphere.ai.governance.PolicyParser` entry); all three in-tree adapters (Yaml/Rego/Cedar) ship such an entry, so they auto-discover from the classpath. Programmatic wiring remains available for callers who want explicit control.
9494

9595
The audit-sink family follows the same posture: `AsyncAuditSink` ships in `modules/ai`; `KafkaAuditSink` (`modules/ai-audit-kafka`) and `JdbcAuditSink` (`modules/ai-audit-postgres`) are wired programmatically as well — no `META-INF/services` entries.
9696

docs/runtime-selection.md

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ Walk these questions in order; stop at the first match.
1818

1919
1. **Are you new to Atmosphere AI and just need it to work?**
2020
**Built-in (`atmosphere-ai`)**. Zero extra deps, OpenAI-compatible
21-
client out of the box, one of three runtimes (with Anthropic and Cohere)
21+
client out of the box, one of two runtimes (with Cohere)
2222
that emit `TOOL_CALL_DELTA` for token-streaming tool-call argument deltas. Use
2323
`--model` / `LLM_API_KEY` to point at OpenAI / Gemini / Ollama / any
2424
compatible endpoint.
@@ -109,9 +109,9 @@ ordered from "most-general / most-portable" at the top to
109109
| LangChain4j | Vendor-neutral JVM (Quarkus, Micronaut, Vert.x) | You want OpenAI-only | Token-by-token |||
110110
| Google ADK | Gemini-native + sub-agent orchestration | You're not using Gemini | Token-by-token | ✅ + `AGENT_ORCHESTRATION` ||
111111
| Embabel | Goal-graph planner workflows | You want imperative dispatch | Token-by-token | ✅ + `AGENT_ORCHESTRATION` ||
112-
| Koog | Kotlin coroutine + native `CANCELLATION` | You're not on Kotlin | Token-by-token | ✅ + `AGENT_ORCHESTRATION` + `CANCELLATION` ||
112+
| Koog | Kotlin coroutine + native cancellation mechanism | You're not on Kotlin | Token-by-token | ✅ + `AGENT_ORCHESTRATION` ||
113113
| Semantic Kernel | Microsoft / .NET ecosystem parity on JVM | You don't need SK plugins/planners | Token-by-token |||
114-
| AgentScope | Qwen-native ReAct, Alibaba Cloud AI Studio | You want anything other than Qwen | Token-by-token |+ `CANCELLATION` ||
114+
| AgentScope | Qwen-native ReAct, Alibaba Cloud AI Studio | You want anything other than Qwen | Token-by-token |||
115115
| Spring AI Alibaba | DashScope / `ReactAgent` graph on Spring Boot 3 | You're on Spring Boot 4 | **Buffered** (one chunk + complete) |||
116116
| Anthropic | Native Anthropic Messages API without a third-party SDK | You need audio input | Token-by-token |||
117117
| Cohere | Native Cohere v2 Chat API | You need audio input | Token-by-token | ✅ + `TOOL_CALL_DELTA` ||
@@ -124,6 +124,8 @@ them once in the pipeline layer or in `AbstractAgentRuntime` — the adapter
124124
only plugs in where it differs:
125125

126126
- **`TEXT_STREAMING`** (every runtime; Spring AI Alibaba is buffered)
127+
- **`CANCELLATION`** (every runtime — the mechanism differs per adapter, e.g.
128+
native coroutine cancel on Koog, Reactor subscription cancel on AgentScope)
127129
- **`SYSTEM_PROMPT`** (every runtime)
128130
- **`STRUCTURED_OUTPUT`** (every runtime — pipeline wraps the session in
129131
`StructuredOutputCapturingSession` with system-prompt schema injection)
@@ -144,8 +146,7 @@ only plugs in where it differs:
144146
snapshots `context.history()` into a `CheckpointStore` for the other eleven)
145147

146148
Use the table at the top to decide on the specialized capabilities
147-
(`AGENT_ORCHESTRATION`, `CANCELLATION`, multi-modal vision/audio,
148-
`PROMPT_CACHING`).
149+
(`AGENT_ORCHESTRATION`, multi-modal vision/audio, `PROMPT_CACHING`).
149150

150151
## Swapping runtimes
151152

@@ -161,7 +162,9 @@ atmosphere new my-app --template ai-chat --runtime spring-ai --force
161162
```
162163

163164
`--runtime` accepts `builtin`, `spring-ai`, `langchain4j`, `adk`, `embabel`,
164-
`koog`, `semantic-kernel`, `agentscope`, `spring-ai-alibaba`. The `--force`
165+
`koog`, `semantic-kernel`, `agentscope`, `spring-ai-alibaba`, `anthropic`,
166+
`cohere`, `crewai` — any key defined in `cli/runtime-overlays.json` (12 today).
167+
The `--force`
165168
flag (only valid with `--runtime`) wipes any existing adapter dependency
166169
declared in `cli/runtime-overlays.json` from the scaffolded `pom.xml`
167170
*before* injecting the chosen overlay — required for samples that already

modules/ai/README.md

Lines changed: 27 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ The `AgentRuntime` interface is the AI-layer equivalent of `AsyncSupport`. Imple
4949
| `atmosphere-spring-ai-alibaba` | `SpringAiAlibabaAgentRuntime` | 100 | TEXT_STREAMING (buffered), SYSTEM_PROMPT, STRUCTURED_OUTPUT, CONVERSATION_MEMORY, TOOL_CALLING, TOOL_APPROVAL, TOKEN_USAGE, PER_REQUEST_RETRY, BUDGET_ENFORCEMENT, CONFIDENCE_SCORES, PASSIVATION, VISION, AUDIO, MULTI_MODAL, CANCELLATION (cooperative) *(see runtime caveats below)* |
5050
| `atmosphere-semantic-kernel` | `SemanticKernelAgentRuntime` | 100 | TEXT_STREAMING, SYSTEM_PROMPT, STRUCTURED_OUTPUT, CONVERSATION_MEMORY, TOKEN_USAGE, TOOL_CALLING, TOOL_APPROVAL, PER_REQUEST_RETRY, BUDGET_ENFORCEMENT, CONFIDENCE_SCORES, PASSIVATION, VISION, MULTI_MODAL, CANCELLATION |
5151

52-
Every runtime emits `TokenUsage` via `StreamingSession.usage()` when the underlying API provides token counts, feeding `ai.tokens.*` metadata into `MetricsCapturingSession` and `MicrometerAiMetrics`. Capability declarations are pinned in each runtime's contract test (`AbstractAgentRuntimeContractTest.expectedCapabilities()`), so the table above cannot drift from the running code without breaking the build. The aggregate counts ("12 runtimes") and the per-row capability lists are additionally pinned against `.harness/capabilities.snapshot.json` by `CapabilitySnapshotTest` and `scripts/validate-capability-claims.sh` (run from pre-push), so prose claims about the matrix break the build alongside code drift.
52+
Every runtime emits `TokenUsage` via `StreamingSession.usage()` when the underlying API provides token counts, feeding `ai.tokens.*` metadata into `MetricsCapturingSession` and `MicrometerAiMetrics`. Capability declarations are pinned in each runtime's contract test (`AbstractAgentRuntimeContractTest.expectedCapabilities()`), so the table above cannot drift from the running code without breaking the build. The aggregate counts ("12 runtimes") and the per-row capability lists are additionally pinned against `.harness/capabilities.snapshot.json` by `CapabilitySnapshotTest` and `scripts/validate-capability-claims.sh` (run from pre-push). That enforcement covers the structured table rows and the tight count claims (`All N runtimes`, `N AiCapability`/`N capabilities total`) only; free-form per-runtime narrative below is **not** machine-checked, so keep that prose in sync with the table by hand.
5353

5454
Each runtime additionally ships a portable signed manifest at `modules/<X>/SKILLCARD.yaml` (and `SKILLCARD.yaml.sig` after a tagged release). `scripts/regen-skillcards.sh` emits the YAML from the snapshot + module `pom.xml`; `.github/workflows/sign-skillcards.yml` signs every card on tag push via OpenSSF Model Signing (Sigstore keyless OIDC — short-lived Fulcio cert + Rekor transparency-log entry, OIDC identity bound to the workflow path). Both the card and its `.sig` bundle are packaged into each runtime jar at `META-INF/atmosphere/` so a downstream consumer can verify integrity without unpacking the source tree. `SkillCardSnapshotTest` enforces drift detection, shape conformance, and signature verification when a `.sig` is present; verify locally with `./scripts/verify-skillcards.sh --identity https://github.com/Atmosphere/atmosphere/.github/workflows/sign-skillcards.yml@refs/tags/<TAG> --identity-provider https://token.actions.githubusercontent.com`. Cards on `main` between releases are unsigned by design — the workflow runs at tag time.
5555

@@ -1090,26 +1090,34 @@ prevention, dynamic routing, and long-pause human-in-the-loop:
10901090
is called per request, and `assembleMessages` also threads a
10911091
`SystemMessage` into the `List<Message>` dispatched to `call(...)`.
10921092
`CONVERSATION_MEMORY` is honored because the same message list carries
1093-
`context.history()`. `TOKEN_USAGE` is **not** declared because
1094-
`ReactAgent.call()` returns `org.springframework.ai.chat.messages.AssistantMessage`
1095-
which has no surface for the `ChatResponse` usage metadata as of v1.1.2.0;
1096-
the agent framework's `CompiledGraph` captures usage internally but does not
1097-
return it through the `call(...)` API. `TOOL_CALLING` is not declared because
1098-
Spring AI Alibaba's tool surface bridges Spring AI `FunctionCallback`s, which
1099-
would need a separate `SpringAiAlibabaToolBridge` to satisfy
1100-
`TOOL_APPROVAL`. **Spring Boot 3.5 only**: Spring AI Alibaba 1.1.2.0
1093+
`context.history()`. `TOKEN_USAGE` is declared because
1094+
`AtmosphereSpringAiAlibabaAutoConfiguration` wraps the Spring AI `ChatModel`
1095+
bean in the `UsageCapturingChatModel` decorator (see footnote ² above), which
1096+
accumulates `ChatResponseMetadata.getUsage()` across every step of the ReAct
1097+
graph into a per-thread collector the runtime emits via `session.usage(...)`
1098+
after each dispatch — `ReactAgent.call()` returns an
1099+
`org.springframework.ai.chat.messages.AssistantMessage` with no usage surface,
1100+
so the decorator is what closes the gap. `TOOL_CALLING` and `TOOL_APPROVAL`
1101+
are declared because `doExecute` builds a per-request `ReactAgent` with
1102+
`SpringAiAlibabaToolBridge` attached when `context.tools()` is non-empty; the
1103+
bridge routes every tool invocation through
1104+
`ToolExecutionHelper.executeWithApproval` so `@RequiresApproval` gates fire
1105+
uniformly. **Spring Boot 3.5 only**: Spring AI Alibaba 1.1.2.0
11011106
transitively pulls Spring AI 1.1.2 which references Spring Boot 3.x
11021107
autoconfigure classes (e.g. `RestClientAutoConfiguration`) that don't exist
11031108
in Spring Boot 4 — the CLI overlay must be applied with `-Pspring-boot3`,
11041109
same situation as Embabel.
1105-
- **`TOOL_CALL_DELTA`** is declared only by `BuiltInAgentRuntime`. Built-in's
1106-
`OpenAiCompatibleClient` forwards every `delta.tool_calls[].function.arguments`
1107-
fragment through `session.toolCallDelta(acc.id(), argChunk)` on both the
1108-
chat-completions and responses-API streaming paths (see
1109-
`OpenAiCompatibleClient.java` lines ~530 and ~892), so browser UIs receive
1110-
`ai.toolCall.delta.*` metadata frames before the consolidated `AiEvent.ToolStart`
1111-
fires. The tool-capable framework bridges (Spring AI, LangChain4j, ADK,
1112-
Embabel, Koog, Semantic Kernel) honor the default `StreamingSession.toolCallDelta()` no-op
1110+
- **`TOOL_CALL_DELTA`** is declared by `BuiltInAgentRuntime` and
1111+
`CohereAgentRuntime`. Built-in's `OpenAiCompatibleClient` forwards every
1112+
`delta.tool_calls[].function.arguments` fragment through
1113+
`session.toolCallDelta(acc.id(), argChunk)` on both the chat-completions and
1114+
responses-API streaming paths (see `OpenAiCompatibleClient.java` lines ~530
1115+
and ~892), and Cohere's `CohereChatClient` emits the same frames via
1116+
`session.toolCallDelta(acc.id, chunk)` from `handleToolCallDelta`, so browser
1117+
UIs receive `ai.toolCall.delta.*` metadata frames before the consolidated
1118+
`AiEvent.ToolStart` fires. The remaining tool-capable framework bridges
1119+
(Spring AI, LangChain4j, ADK, Embabel, Koog, Semantic Kernel) honor the
1120+
default `StreamingSession.toolCallDelta()` no-op
11131121
contract but do not emit chunks from their streaming loops — their high-level
11141122
APIs surface only consolidated tool calls, and the negative assertion in
11151123
`modules/integration-tests/e2e/ai-tool-call-delta.spec.ts` pins the gap
@@ -1123,9 +1131,11 @@ Seven `EmbeddingRuntime` implementations are registered via `ServiceLoader`. The
11231131
| Runtime | Module | Priority | Notes |
11241132
|---------|--------|----------|-------|
11251133
| `SpringAiEmbeddingRuntime` | `atmosphere-spring-ai` | 200 | Wraps Spring AI `EmbeddingModel` |
1134+
| `SpringAiAlibabaEmbeddingRuntime` | `atmosphere-spring-ai-alibaba` | 200 | Wraps Spring AI Alibaba `EmbeddingModel` |
11261135
| `LangChain4jEmbeddingRuntime` | `atmosphere-langchain4j` | 190 | Wraps LC4j `EmbeddingModel`; unwraps `Response<Embedding>` |
11271136
| `SemanticKernelEmbeddingRuntime` | `atmosphere-semantic-kernel` | 180 | Wraps SK `TextEmbeddingGenerationService`; `Mono.block()` sync boundary |
11281137
| `EmbabelEmbeddingRuntime` | `atmosphere-embabel` | 170 | Wraps Embabel `EmbeddingService` (1:1 SPI map) |
1138+
| `KoogEmbeddingRuntime` | `atmosphere-koog` | 100 (default) | Wraps Koog `LLMEmbeddingProvider` |
11291139
| `BuiltInEmbeddingRuntime` | `atmosphere-ai` | 50 | HTTP POST to `/v1/embeddings`; zero-dep fallback |
11301140

11311141
See <https://atmosphere.github.io/docs/reference/ai/> for the Astro reference page (maintained in the `atmosphere.github.io` repo).

modules/ai/src/main/java/org/atmosphere/ai/governance/owasp/OwaspAgenticMatrix.java

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,13 @@
2020
/**
2121
* Atmosphere's self-assessment against the <a
2222
* href="https://genai.owasp.org/resource/agentic-ai-top-10/">OWASP Agentic AI
23-
* Top 10</a> (December 2025). Every row points at (a) the shipped feature
24-
* that defends against the threat, (b) a regression test that fires on the
25-
* evidence path, and (c) a production-consumer grep pattern so reviewers
26-
* can confirm the primitive is reached on a real turn — per CLAUDE.md
27-
* "SPI presence ≠ runtime presence."
23+
* Top 10</a> (December 2025). Every addressed (COVERED / PARTIAL / DESIGN)
24+
* row points at (a) the shipped feature that defends against the threat,
25+
* (b) a regression test that fires on the evidence path, and (c) a
26+
* production-consumer grep pattern so reviewers can confirm the primitive
27+
* is reached on a real turn — per CLAUDE.md "SPI presence ≠ runtime presence."
28+
* {@link Coverage#NOT_ADDRESSED} rows carry no evidence and state the gap in
29+
* their notes column.
2830
*
2931
* <h2>Coverage vocabulary</h2>
3032
* <ul>

0 commit comments

Comments
 (0)