Date: 2026-06-28 Type: phase-update
casehub-clinical has ten completed foundation layers, twenty REST endpoints, and a showcase test that proves the full trial lifecycle works. What it doesn’t have is a way to show someone who isn’t a developer. The goal: build a demo UI where an investor or CTO clones the repo, runs mvn quarkus:dev, opens a browser, and understands what AI Fusion actually means — trust-weighted agent routing, oversight gates, Merkle audit trails, and the qhorus accountability model.
I assumed the existing REST endpoints would feed the demo directly. They don’t. The existing API is entity-lookup oriented — get one trial, one patient, one AE by ID with full ownership chain validation. A dashboard needs flattened lists, cross-site aggregations, and governance context that doesn’t exist on any single entity. The data for “what the AI decided vs how the platform governed it” lives across three different sources: the AE entity, the WorkerDecisionEntry in the qhorus datasource, and the ActorTrustScore cache.
The spec went through six rounds of adversarial review before implementation started. Each round caught something the previous one missed — and three of them would have caused build or runtime failures.
Round 1 identified that the spec described what the UI shows but never specified how it’s built with the casehub-pages DSL primitives. The DSL code examples in the spec used the wrong function signatures — columns({span: 3}, metric(...)) when the actual API is columns([3, 3, 3, 3], [metric(...)], ...). Compile-time errors in the spec.
Round 2 caught that the 8-step guided narrative skipped the PI authorisation flow entirely. The SUSAR gate story shows “AI decided, human approved.” The PI COMMAND → Commitment → Resolution story shows “the platform created a formal obligation on a named person with a traceable deadline.” The second is the stronger sell. The narrative expanded from 6 to 8 steps.
Round 3 found that ActionGateService.approve() — the method I’d specified for SUSAR gate approval — doesn’t exist. Gates are approved by completing a WorkItem. The actual flow is: find the gate WorkItem by callerRef prefix, claim it, complete it, and ActionGateCompletionApplier publishes the event. The same round caught that channelService.receiveHumanMessage() doesn’t exist either — it’s channelGateway.receiveHumanMessage(ChannelRef, InboundHumanMessage).
Round 4 identified that %dev.quarkus.arc.selected-alternatives would have dropped three JPA alternatives from the non-profiled list — Quarkus profile properties replace, not merge. The fix: @IfBuildProfile("dev") + @Alternative + @Priority(150) on the bean itself, no config property needed.
Round 5 caught that the pre-seeded Grade 2 AE wouldn’t produce any trust score data. SusarCriteriaEvaluator gates on GRADE_4/GRADE_5. Grade 2 AEs route directly to safety officers with no engine case, no attestation, no trust score update. The seeder needed Grade 4 unexpected AEs with full SUSAR gate lifecycles.
Round 6 fixed getBytes() → getBytes(StandardCharsets.UTF_8) for the deterministic UUID, and turned the DSMB rollup side effect into a feature — the seeded Grade 4 AEs leave grade4Active flags set, so when the demo adds a new Grade 4 AE at Site B, the trial case detects two sites with simultaneous signals and fires the DSMB review automatically.
Seven dashboard endpoints on TrialDashboardResource: trial summary, patients, adverse events, deviations (all Panache queries), plus agents, governance context, and ledger entries (all cross-datasource aggregations joining the default and qhorus datasources). The governance endpoint powers the demo’s hero layout — “What the AI decided” beside “How the platform governed it” — by joining AE entity fields with WorkerDecisionEntry.trustScoreAtRouting and current ActorTrustScore.
Two demo-only endpoints on DemoActionResource, guarded by @IfBuildProfile("dev"): PI approval (calls channelGateway.receiveHumanMessage() to fire the real qhorus CDI chain) and SUSAR gate approval (finds the gate WorkItem via scanAll() + callerRef filter, completes it via WorkItemService.completeFromSystem(), then calls TrustScoreJob.runComputation() for immediate Bayesian Beta recomputation).
DemoDataSeeder replays a complete trial scenario through real service calls — not direct inserts. This matters because LedgerEntryRepository.save() computes Merkle digests and updates the frontier automatically. Hand-inserting ledger entries would break the verification endpoint, and Step 8 of the demo — “The Proof” — calls that endpoint. The seeder creates three sites, ten patients, resolved events at each site, and runs three Grade 4 SUSAR lifecycles to produce real attestations and materialised trust scores.
The Java backend is complete. The next piece is the TypeScript UI — fourteen casehub-pages DSL compositions that render the guided walkthrough and explore dashboard against these endpoints. The framework is casehub-pages (our own declarative dashboard runtime), embedded in clinical via Quinoa. mvn quarkus:dev will serve both the Java API and the TypeScript UI from a single process.
The spec review process surfaced something worth naming: every API the spec referenced that turned out to be wrong (ActionGateService, channelService.receiveHumanMessage, sidebar() nesting, selected-alternatives profile merging) would have been a runtime failure discovered during implementation. Catching them in the spec cost minutes of research each. Catching them in code would have cost debugging sessions.