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...

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.

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...

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...

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.

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.