Notes | CaseHub - Claudony
the session that wasn't about sessions
What brought us here was a closed issue — engine#231 finally threading triggerChannelId and triggerCorrelationId through to ProvisionContext. The provision path in Claudony has carried a TODO for w...
Claudony — When the Ledger Enforces Its Own Rules
Claudony — When the Ledger Enforces Its Own Rules
Claudony — Running the Signal Chain Live
Claudony — Running the Signal Chain Live
The Signal That Writes
The question I couldn’t answer going into this: how does a Claude Code session tell the case engine it’s done?
The Shell That Outlived the Worker
The problem was simple to state: the engine never found out when a worker finished.
Claude CLI would run, complete, exit — and the case would stay frozen in WAITING
indefinitely. The engine had cal...
A Denial Is Not a Restriction
The oversight channel in Claudony’s three-channel layout was supposed to be the governance channel — the place where agents pause and ask humans for approval before doing something consequential. T...
Three layers to one line
I came in expecting to wait the engine round-trip test out. The previous session left a note: CaseEngineRoundTripTest fails because the engine SNAPSHOT has an in-flight PlanExecutionContext constru...
The handler that never fired
A listener that registers without complaint, data flowing from server to browser, an open connection — and yet the panel never updates. That’s how SseMeshStrategy had been sitting in the codebase.
When the browser is on the other node
The problem is simple to state: a worker posts a message on Node B. The browser watching that channel is connected to Node A. It gets nothing.
One way to register a backend
I started this branch expecting a small fleet plumbing task and found out immediately that the plumbing was already broken — even on a single node.
The profile that wasn't %test
The core change was two lines. ClaudonyReactiveCaseChannelProvider.listChannels() was calling listAll() — every channel in the database, then filter client-side for the ones belonging to the case. ...
Turning a poll into a push
The dashboard channel panel had been polling Qhorus every 500ms for new messages. Not because that was a deliberate choice — it was a workaround. An earlier attempt at event-driven delivery had fai...
The dependency that didn't exist
CI had been red since last session. The error looked straightforward: Could not find artifact io.casehub:casehub-testing:jar:0.2-SNAPSHOT — not in Maven Central, not in GitHub Packages. Engine CI w...
The feed that couldn't show new messages
I asked Claude to add cursor support to the mesh feed endpoint. The issue was straightforward: GET /api/mesh/feed returned the same messages on every poll — no ?after=<id> parameter, so clien...
Three fixes and a phantom class
Three test fixes and an unexpected build failure.
Dead config and live redirects
Short session today — a cleanup branch and one unexpected discovery.
What the SSE Stream Was Hiding
The global mesh dashboard SSE stream had been silently delivering nothing.
When the Rule Is Right but the Fix Is Wrong
PLATFORM.md had a boundary rule: don’t call ReactiveQhorusMcpTools from internal service code. ReactiveQhorusMcpTools is the MCP protocol dispatch layer for Claude Code — calling it from MeshResour...
What the mock was hiding
The @InjectMock in CaseEngineRoundTripTest was technically correct. It was also hiding a production bug. Removing it required an architecture session that went further than expected.
Five problems before the first assertion
Closing the agent mesh epic required working through five separate infrastructure problems before the real integration test could be written. What they were and why they were non-obvious.
The silent annotation and SSE causality
Two Quarkus findings from SSE test coverage: @TestSecurity that silently does nothing without an HTTP call, and why SSE causality in Claudony can't be tested through Event.fireAsync().
Server-sent events and two silent failures
Both SSE failures return HTTP 200 with valid-looking data — one because Quarkus adds the data: prefix Claudony was also adding, and one because a static ObjectMapper has no JavaTimeModule and silen...
Shipped: the case panel. Found: a fifty-tool cliff.
The case context panel ships after three upstream API migrations, but partway through a silent tool-count cap of 50 causes send_input to disappear without any warning from the MCP server.
What Polling Was Hiding
Switching from polling to server-initiated push exposes three guarantees polling provided for free — restart resilience, catch-up, and identity — that each require deliberate design in a push model.
Phase 15: Auditing the Audit Trail
An external audit finds two production bugs in ClaudonyLedgerEventCapture: a swallowed persistence exception that silently drops ledger entries, and a MAX() sequence query with no UNIQUE constraint...
Phase 14: The Panel Knows Its Case
Adding caseId and roleName to the immutable Session record touches 20+ construction sites, and the left panel auto-expands with clickable worker rows that reconnect the WebSocket without reloading ...
The name problem hiding an identity problem
CaseHub uses worker roles while Claudony tracks session UUIDs — WorkerSessionMapping bridges the two identity systems, with a documented MVP limitation when concurrent same-role workers share a case.
The config mapping trap
The four CaseHub SPIs wire up cleanly, but @ConfigMapping strict validation fires before library roots register and CaseLedgerEntryRepository turns out not to be a CDI bean despite looking exactly ...
A rule, not a preference
Migrating Qhorus to a named datasource hardens into an ecosystem-wide rule — every library owns a named datasource matching its artifact ID — and exposes two direct EntityManager calls hiding behin...
Idle is not one thing
Three CDI-pluggable expiry policies distinguish an idle shell from an autonomously working agent, collected at startup via @Any Instance so new policies are just new beans.
The observer becomes a participant
A fixed send dock in all three mesh views lets humans post into any active channel without switching panels, turning the read-only mesh observer into an active participant.
Fourteen weeks of foundation, three hours to embed
Qhorus embeds into Claudony after a major Quarkus upgrade rewrites the WebAuthn layer and exposes that old credential files stored aaguid without dashes, silently locking users out.
Fleet Phase 2: the fleet you can see
Fleet Phase 2 adds the fleet sidebar and WebSocket proxy bridge, and the first Playwright test immediately finds a four-month-old bug hiding in displayName() that no one had noticed.
The fleet was inevitable
Fleet Phase 1 extends Claudony to manage sessions across multiple instances — and clears three deferred embarrassments: a hardcoded encryption key committed to the repo, a 30-minute session timeout...
Claudony — The Name That Fits
RemoteCC was always a placeholder — Claudony emerged from the colony metaphor and brought a bioluminescent visual language that makes the architecture literal.