Four modules, three contracts, one pipeline
Each module has one job. Each contract has one shape. Failure anywhere falls back gracefully.
1. Observer — reads the world
A vanilla-JS bundle (under 8 KB gzipped, zero dependencies) loaded into your host app as a single <script> tag. It does four things passively:
- Route — tracks
popstate, hashchange, wrappedhistory.pushState, and title changes (SSR fallback). - Identity — reads the active token from MSAL / Keycloak / Auth0 / generic JWT / cookie session.
userIdis SHA-256 hashed before leaving the module. - Focus + DOM — captures the focused field's id, label, type, and
data-*attributes. - Interaction signals — rage clicks (3 in 1s), idle on form (20s), visible validation errors, last submit attempt.
Output: a HelpContext object. The Observer never modifies the host page's DOM. It only reads.
2. Knowledge Engine — understands the world
A backend service (Postgres + Fastify). Receives a HelpContext, returns a KnowledgePacket.
The resolution algorithm has five steps (glossary):
- Validate the context.
- Lookup concept by field-alias cascade:
data-concept-id→field-id→aria-label→ fuzzy label (Jaccard ≥ 0.5). - Apply role variant if
roleAwareContentis on and the identity has roles. - Apply jurisdiction variant: identity.jurisdiction → app.jurisdictions[0] → base concept.
- Resolve workflow hint + interaction context, assemble the packet.
Only concepts with status: published are visible to resolution. Draft entries are loadable but invisible — this is the gate that prevents demo content from leaking into production.
3. AI Content Generator — articulates the world
A backend service that turns each KnowledgePacket into a ContentPayload using Claude Sonnet 4 (claude-sonnet-4-20250514).
Three-block prompt structured for Anthropic prompt caching:
- System prompt (cached — never changes).
- Stable concept context (cached — changes when the concept changes).
- Variable context (not cached — role, jurisdiction, interaction signals).
Two guarantees that make this production-safe for compliance work:
- Grounding validation — every citation in the output must appear in the packet's source list. Regulatory tokens (AMLD5, GDPR, CSSF, etc.) outside the packet are rejected.
- Degraded mode — if Claude is unreachable or grounding fails twice, the service falls back to a payload assembled directly from the KnowledgePacket. Less polished, but accurate by construction. Users always get help.
4. UI Injector — delivers the world
Another vanilla-JS bundle deployed alongside the Observer. Listens for help:context events, calls the Content API, and renders one of six component types in a shadow DOM root attached to the host's body:
- Tooltip — short definition + sources.
- Risk callout — high-severity consequences.
- Role guidance — what your role specifically should do.
- Workflow nudge — current step + next step + deadlines.
- Correction guidance — shown when validation errors are visible.
- Fallback message — shown when no concept matched.
Shadow DOM isolates styles from the host app, accessibility is preserved (focus trap on modal-like components, ARIA live regions, aria-describedby append-not-replace).
Why this pipeline is hard to copy
The UI injection is the easy part — anyone can do that. The moat is the knowledge base: a curated, versioned, jurisdiction-aware, role-aware map from compliance fields to regulatory consequences. That requires domain experts, not just engineers.
The grounding constraint is the legal protection. If this product ever generated a false regulatory citation that a compliance officer acted on, that would be a serious problem. The grounding validator is what prevents it.
HelpContext flow through all three backends end-to-end.