MemberJunction Developer Guides
This folder is the home for cross-cutting, “read this before you build that” reference docs. Package-specific docs live in packages/<Pkg>/CLAUDE.md or packages/<Pkg>/README.md and are linked from here when relevant.
If you’re about to start work in one of the areas below, read the guide first — these documents capture patterns that have already been litigated.
This file is the complete index of MJ development guides. The root
CLAUDE.mdpoints here rather than duplicating the list, so this is the one place to look — and the one place to update when you add a guide.
Start here
Section titled “Start here”- Building Applications on MemberJunction — The hub guide for using MJ as a first-class application development platform. Explains the metadata-driven, schema-to-app model, the unified-TypeScript / isomorphic object model, AI-native app patterns, and links out to the authoritative README/guide for every layer (data modeling, CodeGen, entities, API, UI, Actions, AI, deployment). Start here if you’ve got data in MJ and want to build on it.
- Framework Comparison — Objective comparison of MJ against Next.js/Vercel, Supabase, Rails, Django, and a hand-rolled Node+ORM+SPA stack: where each shines, where MJ differs, and how to choose. Companion to the app-building guide.
- Agent Framework Comparison — Objective comparison of MJ’s AI agent framework and infrastructure against LangGraph, CrewAI, AG2/AutoGen, the vendor agent SDKs, Semantic Kernel, LlamaIndex, and the TypeScript-native frameworks: orchestration paradigms, payload/state governance, HITL, model gateway, observability, memory, permissions, and deployment (self-host or managed via MJ Central). Companion to the app-stack comparison above.
Framework fundamentals
Section titled “Framework fundamentals”- BaseEntity Server-Side Patterns — Use before writing a new server-side entity subclass under
MJCoreEntitiesServer. Covers the persisted-embedding pattern (Save()+EmbedTextLocal+ engine cache sync), cross-record invariants viaValidateAsync(NOT DB triggers), and FK cleanup before delete. Reference implementations:MJAIAgentNoteEntityServer,MJTagEntityServer,MJTagScopeEntityServer. Lift the recipes from there — don’t reinvent. - Soft Deletes Guide — How
DeleteType='Soft'works end to end: CodeGen-managed__mj_DeletedAtcolumn, filtered base views, soft-deletespDelete, interaction withAllowRecordMerge. - UUID Comparison Guide — Critical patterns for comparing UUIDs across SQL Server (uppercase) and PostgreSQL (lowercase):
- Always use
UUIDsEqual()instead of===for UUID comparisons - Use
NormalizeUUID()for Set/Map key operations - Angular template binding patterns
- Automated enforcement tests
- Always use
- Caching & Real-Time Synchronization Guide — Multi-tier server caching, RunView cache behavior,
BypassCache, BaseEntity event-driven invalidation, Redis pub/sub, real-time browser sync. Read before touching caching or write-after-write data flow. Also documents theBaseEngineintegration (ObserveProperty,DataChange$) that gives reactive UIs over entity caches for free. - Keyset Pagination Guide — Deep pagination via
RunViewParams.AfterKey(keyset/seek) instead ofStartRow, for background jobs and bulk processing that iterate all records of a large entity. Keyset stays O(log N) per page regardless of depth. Single-column PK only — throwsAfterKeyNotSupportedErrorfor composite-PK entities. Reference implementations:ScheduledGeocodingAction,VectorBase,EntityVectorSyncer. - Remote Operations Guide — The typed, provider-routed Remote Operations primitive —
BaseRemotableOperation<TInput,TOutput>(in@memberjunction/core) invoked from one call site on both client (marshalled over GraphQL) and server (in-process). Framed as MJ’s 4th data primitive alongsideBaseEntity(CRUD),RunView(dynamic set reads), andRunQuery(stored queries). Covers: when to use it vs. an Action vs. a bespoke resolver; the three authoring modes driven by anMJ: Remote Operationsrow’sGenerationType— Manual (CodeGen emits the typed base, you write theInternalExecutesubclass), AI (RO-4:MJRemoteOperationEntityServerhas an LLM author the body fromDescriptionagainst the ambientinput/provider/user/contextcontract + a JSONTypeLibrariesdeclaration, gated byCodeApprovalStatus), and Default; theRemoteOperationGeneratorBaseCodeGen emitter (@memberjunction/codegen-lib) →remote_operations.ts; theRouteOperationpower-tool seam (IRemoteOperationProvideronProviderBase); the auth chain (API-key scope ∥ user permissions + theRemoteOperationEngineBaseActive/Approved metadata gate + per-opAuthorize); andLongRunningprogress — attachedonProgressworks both in-process AND over the wire (a per-callRemoteOperationProgresssubscription channel published from the resolver); only detached fire-and-forget remains partial. Read before hand-rolling a resolver+client for a typed capability the browser and server both invoke, OR before adding a new operation (declare a metadata row, don’t hand-write the base). Complements (does not replace) the Transport-Layer guide and the Actions boundary. For a visual before/after with mermaid diagrams (the layers Remote Operations removes, from two real migrations), see the companion Remote Operations Showcase (lives with the@memberjunction/corepackage that defines the primitive). - Transport-Layer Architecture Guide — The canonical engine → resolver → GraphQL client → thin UI layering (plus the optional Action layer for agentic/workflow/low-code invocation) for any custom server-side capability the browser or an agent invokes — clustering, search, classify, LLM calls, “run this pipeline” buttons. Covers:
- Why business logic lives in the framework-agnostic engine exactly once, and what each adapter layer must NOT do
- Step-by-step: build the engine → thin TypeGraphQL resolver (
ResolverBase+ per-request user) → typedGraphQL<Feature>Clientin@memberjunction/graphql-dataprovider→ thinnest Angular wrapper (never inlinegql) → optional Action that calls the engine directly - A decision table for which layers you actually need (and when to just use the generated entity CRUD layer instead)
- JSON-string-field pattern for complex payloads, client/engine type decoupling, and reference implementations (
GraphQLClusterClient,SearchKnowledgeResolver, etc.) - Read this before hand-writing any new resolver or GraphQL client. Not for plain entity CRUD — that’s already generated.
- Record Set Processing & Record Processes Guide — MJ’s single hardened substrate for “do X to a set of an entity’s records” — and the saved, metadata-defined Record Process layer on top of it. Two layers: the substrate (
@memberjunction/record-set-processorengine +-baseseams) is a composition of three pluggable seams — Source (ArraySource/ViewSource/ListSource/FilterSource/KeysetSource, cursor-paginated + resumable) × Processor (per-record work →RecordResult) × Tracker (GenericProcessRunTracker→MJ: Process Runs/Process Run Details, orNoOpTracker/custom) — wrapped by an engine that owns batching, bounded concurrency, token-bucket rate-limiting, an error-rate circuit breaker, a budget gate, progress, a pause/cancel handshake, resume, and per-record isolation; and the Record Process (MJ: Record Processesrow → theRecordProcessExecutorfacade) with four work types (FieldRules — declarative field rules with a dry-run preview, self-writing; Action / Agent / Infer — wrapped byWriteBackProcessorwhen anOutputMappingis set), scopes (storedScopeTypeView/List/Filter/SingleRecord + a runtimeRecordProcessScopeOverriderecords/view/list/filter for “run against the current grid selection”), dry-run (the first-classProcessRun.DryRunflag), and three triggers (OnDemand / OnChange / Schedule). The UI lives in@memberjunction/ng-record-process-studio(the Bulk Operations studio — editor + visual FieldRules builder + reactive history) and@memberjunction/ng-entity-action-ux(the in-gridRecordProcessRunnerUXrunner); the typed API surface is theRecordProcess.RunNow+ control Remote Operations. Read before building any bulk/set-iterating operation, adding a work type, or touching the substrate — don’t re-implement batching/resume/audit; compose the seams or declare a Record Process. Folder overview:packages/RecordSetProcessor/README.md.
Database, migrations, and CodeGen
Section titled “Database, migrations, and CodeGen”- Migration → CodeGen End-to-End Workflow — The workflow every schema change follows, whatever its type (new tables, new columns, constraint changes, column modifications, extended properties): write DDL → apply it → run CodeGen → consolidate the output into one replayable migration file. Explains why a single consolidated file matters (guaranteed replay order, atomic review, consistent state on fresh installs) and the separator convention for appending CodeGen output. Companion to
migrations/CLAUDE.md, which holds the authoring rules themselves. - PG Migration Glossary — The vocabulary of the dual-platform (SQL Server → PostgreSQL) migration release process: curated metadata, metadata-sync migrations, the PG ledger and why it is immutable, marker files, migrate-only deployments, gapped databases, baselines, reseed migrations, and superseded updates. Read before discussing or debugging a PG release gap — issue #3253 (196 lost record deletions, a 126-byte marker, and a baseline dumped from a gapped database) is a lot easier to follow once these terms are pinned down.
- PostgreSQL Schema Casing Guide — Why entity
ClassName/CodeNameand GraphQL type names go lowercase-broken on PostgreSQL (unquoted DDL folds schema names to lowercase, so__mj_BizAppsCommon→mjbizappscommon…Entityinstead of the publishedmjBizAppsCommon…Entity→ TS2724 build break), and how MJ fixes it with a case-stableSchemaInfo.CanonicalSchemaName(sourced frommj-app.jsonschema.namevia theOpenApprecord, backfilled by CodeGen’s metadata-sync proc) preferred viaCOALESCE/??in BOTH thevwEntitiesSQL view and the runtime GraphQL prefix. Net-zero on SQL Server. Read before touching schema-prefixed identifiers, the OpenApp install path, orspUpdateSchemaInfoFromDatabase— and note that proc lives in TWO synced copies (SS baseline/migration + PGmetadataSupportObjects.ts). Includes the remediation runbook for existing PG installs (seed theOpenApprow).
Permissions and access
Section titled “Permissions and access”- Unified Permissions Guide — How MJ answers “can this user do this?” across every resource type (agents, artifacts, dashboards, queries, collections, entity rows, and anything you add) through one normalized model. Covers the three-concerns mental model — Authorizations (named capability/feature gates via
AuthorizationEvaluator), Entity Permissions / RLS (row-level CRUD filtering viagetRowLevelSecurityWhereClause), and the unifiedPermissionEngine(per-record sharing/access) — and how to pick the right layer; thePermissionProviderBasecontract + normalized vocabulary (PermissionAction= Read/Create/Update/Delete/Share/Execute/Admin,GranteeType,NormalizedPermission); thePermissionEngineaggregator that reads theMJ: Permission Domainscatalog and ClassFactory-instantiates each@RegisterClass(PermissionProviderBase, …)provider (adding a domain is data + a class, never an engine edit); the 9 shipping domains + backing storage; the two access paths (cached runtime helper likeAIAgentPermissionHelper, open-by-default, hot path — vs. the unified provider, closed-by-default, Sharing Center/audit) and why they differ; a recipe to add permissions to a new resource type (worked example); and mermaid diagrams (decision flow, provider fan-out, add-a-domain). Read before gating any action, building a sharing UI, auditing access, or adding a new permissioned resource. - Magic Link Access Guide — How to share an app-scoped, passwordless session with external users (MJ-issued RS256 magic links). Covers enabling the feature, the two-layer model (framework mechanism vs. per-deployment scenario config vs. runtime-provisioned users), and the recipe for defining an external-access scenario via metadata (restricted role + entity permissions + application role). Read before wiring up external/guest access — external user accounts are runtime-provisioned, but the role + permissions that scope them are version-controlled metadata.
Search, RAG, and tagging
Section titled “Search, RAG, and tagging”- Search Overview Guide — Decision tree across MJ’s search/lookup APIs —
EntityByName/EntityByID(definition lookup),SearchEntity/SearchEntities(per-entity ranked hybrid search, see ENTITY_SEARCH_GUIDE),FullTextSearch(multi-entity DB-level FTS, see FULL_TEXT_SEARCH_GUIDE), andSearchEngine.Search(cross-source unified search with scopes, see SEARCH_SCOPES_AND_RAG_GUIDE). Read this first when you need to find records / definitions / cross-source matches — picking the wrong API can mean wasted round-trips or missed semantic matches. - Entity Search Guide — Per-entity ranked hybrid search via
SearchEntity/SearchEntities. Reached from the Search Overview decision tree above. - Search Scopes & RAG+ Guide — Implementation guide for Search Scopes + agent RAG+ architecture: cross-source unified search with scopes via
SearchEngine.Search. Companion toplans/search-scopes-rag-plus.md. - Content Segmentation Guide — Segmentation decides what gets embedded; the embedding model only decides how. How MJ chunks content before vectorization via the pluggable
BaseSegmenterstrategies (StructuralText,SemanticText,Transcript,FixedWindow) in@memberjunction/ai-segmentation. Covers strategy selection, why the embedding and tagging chunk sites keep separate token budgets, writing a new strategy, and the multimodal model — one native vector per media chunk plusDescription/Transcriptas readable text. Read before changing how any pipeline chunks content, or before ingesting audio/video/images. - Content Autotagging Guide — The Knowledge Hub pluggable autotagging pipeline: providers, keyword extraction, taxonomy bridging.
- Taxonomy & Tagging Guide — How the tag taxonomy itself is shaped, scoped, governed, grown, embedded, reviewed, and pruned. Companion to the autotagging guide.
- Duplicate Detection & Merge Guide — Configuring and running intelligent duplicate detection on an entity, reviewing the AI’s verdicts, and merging. The principle: vectors filter, reasoning validates — a fast embedding/vector pass finds candidate near-duplicates, then an optional gated LLM pass judges the high-probability ones (Merge / NotDuplicate / Uncertain). The LLM never originates matches. Covers the cost/quality lever (
ReasoningThreshold) and how to swap the reasoning model. Written for both users configuring it and PS teams implementing it for a client.
External data
Section titled “External data”- External Data Sources Guide — Backing an MJ Entity or Query with a remote system (Snowflake / MongoDB / external PostgreSQL / …) read live through a pluggable driver — no replication into the MJ DB (Linked-Server-style). Covers the engine → provider-dispatch → router → driver layering, configuring an
ExternalDataSourceType+ExternalDataSource+ credentials, read-only enforcement (ReadOnlyExternalBaseEntity), TTL caching, the RLS-refusal / injection-safe-filter / unsupported-param (AfterKey/aggregates/search) guards, connection auth self-heal, how to add a driver, and known limitations. Read before touching anything underpackages/ExternalDataSources/or wiring an entity/query to a remote source. Distinct from Integrations (scheduled pull-sync into MJ) and Materialization (plans/query-entity-materialization.md, persist hot results into MJ tables).
AI and agents
Section titled “AI and agents”- Agent Memory Guide — The complete agent-memory architecture — note lifecycle (Provisional → Active → Archived), injection (strategies, recency-wins precedence, scoping), in-flight
memoryWriteswith its framework guard pipeline, and the Memory Manager’s hardening/consolidation/decay phases. Includes a configuration reference and troubleshooting queries. Read before touching anything under agent notes/examples or thememoryWritescapability. - Agent Skills & Plan Mode Guide — Two
BaseAgent-framework capabilities that ship together (one migration). Skills (MJ: AI Skills) = reusable capability bundles (Instructions + bundled Actions + bundled sub-agents) an agent activates mid-run via a progressive-disclosure catalog (only name+description in the prompt until activation); resolved byAIEngineBase.GetSkillsForAgentthrough a three-layer gate (AIAgent.AcceptsSkillsNone/All/Limited ×AISkill.Status× per-grantMJ: AI Agent Skills.Status); activation appends Instructions + widens the tool surface via a'specific'-scopedActionChange/SubAgentChangetargeting the activating agent (applies at any depth, never cascades — a'root'scope would be a bug for sub-agents). Permissions use full agent parity: a dedicatedMJ: AI Skill Permissionstable (User xor Role grantee × View/Run/Edit/Delete) with two access paths over one table — the cached, open-by-defaultAISkillPermissionHelper(@memberjunction/ai-engine-base, the runtime gate) and the closed-by-defaultAISkillPermissionProvider(@RegisterClass(PermissionProviderBase,'MJAISkillPermissionProvider'), the unifiedPermissionEngine/Sharing-Center view) — grantee-exclusivity enforced byMJAISkillPermissionEntityServer.Validate(); sharing gated by theCan Share Skillsauthorization (the oldAI SkillsResource-Type sharing was retired).AIEngineBase.GetSkillsForAgent(agent, user?)takes an optional user to intersect the agent gate with the user’s Run permission, so the model’s skill catalog is permission-filtered. Users invoke a skill by typing/skill-namein the composer (mirrors@agent/#entity; picker filtered by the helper, chips useAISkill.IconClass/Color); selected IDs thread asExecuteAgentParams.requestedSkillIDs(same client→resolver→runtime chain asplanMode) andBaseAgent.preActivateRequestedSkillsactivates them at run start only if they survive the guard (agent-accepted ∩ user-permitted). Portable via SKILL.md (SkillMarkdownConverter+SkillImportExportService+ theAISkill.ExportMarkdown/ImportMarkdownRemote Operations — names not IDs, unresolved bundle members become non-fatal warnings); see the Unified Permissions Guide for the two-access-path pattern. Plan Mode = a per-request HITL gate (AIAgent.SupportsPlanModecapability default-ON/opt-out ×ExecuteAgentParams.planModeper-request default-OFF, root-agent-only) that blocks Actions/Sub-Agent until the agent presents a'Plan'step and a human approves it via the existingMJ: AI Agent Requestspause/resume flow; rejection forces a re-plan becauseresumeAgentre-enablesplanModeonly forPlan-step-originated resumes. Both'Skill'and'Plan'are non-terminal steps — deliberately NOT in the DB-CHECK-constrainedAIAgentRun.FinalStepunion (mirror the existing'ClientTools'cast), but they ARE inAIAgentRunStep.StepType. v5.45 governance & observability: self-activation additionally requires the double activation gate —AISkill.ActivationMode×AIAgent.SkillActivationMode, both'Auto'/'RequestedOnly'defaulting to'RequestedOnly'(resolved byGetAutoActivatableSkillsForAgent; the/skillrequested path ignores ActivationMode but honors all availability gates);AIAgent.RequirePlanModeforces plan mode on every root run (SupportsPlanMode moot); runs stampAIAgentRun.PlanMode; in plan mode, skill activations are legal only before approval (post-approval → Retry demanding a re-plan); every step touched by a skill recordsAIAgentRunStep.Skills(JSONArray<AgentSkillInvocation>— activation type, provenance-of-authority gate values, agent-statedreasonfromskills:[{name, reason}]; Actions/Sub-Agent steps carry skill attribution with native-grant precedence = NULL). Read before touching anything under Skills,AcceptsSkills/SupportsPlanMode/ActivationMode/RequirePlanMode, the skill-step/plan-step loop wiring, skill observability, or SKILL.md. - User Routines Guide — The user-owned “agents on my schedule” layer (P1.5, 5.45) —
MJ: User Routines/Recipients/Runs, the single 1-minute dispatcher scheduled job (UserRoutineDispatcherDriver: claim-by-advancing-NextRunAt before running,ConcurrencyMode=Skip, bounded concurrency, per-routine isolation, runs AS the owner), pure schedule math inUserRoutineProcessor(shared withMJUserRoutineEntityServerso save-path and dispatcher agree),ScheduledvsMonitoring(OnChange via result hash), activation windows (StartAt/EndAt), Template-driven notifications (metadata-seeded default), linkage-only telemetry (AgentRunID/PromptRunID/ActionExecutionLogID— no duplicated token/cost),RequestedSkillIDspre-arming, the non-startupUserRoutineEngine, theng-user-routineswidget set + conversations bottom-sidebar entry (gated byShowRoutinesprop AND entity-Read permission). Read before touching anything under routines, the dispatcher, or the conversations routines section. - Real-Time Co-Agents Guide — The live, low-latency agent stack — the
Realtimeagent type and Voice Co-Agent (one co-agent voices any target agent via the stableinvoke-target-agenttool), the triple-registry plugin architecture (server/client realtime-model drivers + interactive-channel plugins, all ClassFactory + metadata resolved), client-direct vs server-bridged topologies,AIAgentSessionlifecycle/janitor, interactive channels (the live Whiteboard), progress narration, observability, and the security model. Read before touching anything realtime / voice / agent-session / channel. Companion toplans/ai-agent-sessions.md. - Realtime Bridges Guide — The pluggable media-transport seam that connects the one realtime agent engine to external endpoints — Zoom/Teams/Slack/Meet/Webex/Discord meetings and Twilio/Vonage/RingCentral telephony — carrying bidirectional, media-agnostic tracks (audio/video/screen, full duplex). Covers: the
AIBridgeEngineBase(@memberjunction/ai-bridge-base, metadata cache) /AIBridgeEngine(@memberjunction/ai-bridge-server, composition-not-inheritance coordination + thebridge.OnMedia → session.SendInput/session.OnOutput → bridge.SendMediatransport seam) pair; theBaseRealtimeBridgedriver family (sibling toBaseRealtimeModel) and how to add a new bridge driver (subclass +@RegisterClass(BaseRealtimeBridge, '<X>Bridge')+ capability-gated virtuals,LoopbackBridgeas the worked example); theIBridgeProviderFeaturescapability model (engine gates the flag,RequireFeaturethrows as defense-in-depth viaBridgeCapabilityNotSupportedError); the platform-agnosticTurnTakingPolicy(passive/active/hybrid); the 5 entities + their*EntityServerinvariants; and the roadmap (Phase 0/1 shipped, 2+ planned). Read before touching anything bridge / meeting / telephony / media-transport, or before adding a bridge driver. - Remote Browser Channel Guide — The in-house realtime channel where an agent drives a real, live browser while it talks (sales demo, support walkthrough, trainer agent — demonstrate then “your turn, you try”). Built on the principle that every backend exposes the same primitive (a CDP endpoint), so the browser work lives once, generically, in
@memberjunction/computer-use(enriched additively with selector-aware actions, screencast,MouseMove, accessibility/element perception) and the Remote Browser layer just maps vocabulary + manages session lifecycle. Covers: the layer cake (computer-use→remote-browser-baseuniversal contracts +RemoteBrowserEngineBaseregistry →remote-browser-cdpsharedCdpRemoteBrowserSessionkit + losslessmapRemoteBrowserAction→ 5 thin backends →remote-browser-serverRemoteBrowserEngine/RemoteBrowserChannel); theAIRemoteBrowserProviderregistry +IRemoteBrowserProviderFeaturescapability gating (two-layer, like bridges); control modes (AgentOnly/ViewOnly/Collaborative) vs control strategies (ComputerUsedefault vsNativeAI/Stagehand, capability-gated); goal-driven control (§9 — set a high-level goal instead of granular clicks:browser_AchieveGoal→ExecuteRemoteBrowserGoal→RemoteBrowserEngine.AchieveGoal→ puredispatchRemoteBrowserGoalstrategy switch →RunComputerUseGoalon the session’s OWN adapter; model-blind credentials via{{label}}context injection resolved at the CDP keystroke boundary; Collaborative pause-on-takeover; vision-model auto-selection + theMJProgressComputerUseEnginestartup binding); and how to add a backend (subclassBaseCdpRemoteBrowserProvider, implementAcquireSession+ a 3-methodICdpSessionBackend,@RegisterClass(BaseRemoteBrowserProvider, '<X>RemoteBrowser'), seed a row). Read before touching anything underpackages/AI/RemoteBrowser/or before adding a browser backend. - Realtime Session Capture & Recording Guide — How a voice session is captured: per-turn transcript with start/end timing + speaker identity, and the optional audio recording. Covers the create-on-start/update-on-complete turn lifecycle, recording on both topologies (server-bridged mixer and client-direct browser capture via
RealtimeAudioRecorder), the seekable 16-bit PCM WAV + capture-timepeaks.jsonwaveform sidecar, agent-audio mixing (AttachRemoteStream/OnRemoteMediaStream), and streaming playback viamj-storage-media-player→CreateMediaAccessToken→ theGET /media/:fileIdRange route. Read before touchingpersistRealtimeTranscript, the recording capture/store, or playback. - Conversations UX Stack Guide — The 3-layer architecture for every chat surface in MJ —
@memberjunction/conversations-runtime(pure-TS engine: agent dispatch, default-agent resolution, mentions, bridge, streaming, client tools, sessions observability) ↔ adapters (INotificationAdapter/IActiveTaskTracker/ISessionsAdapter) ↔@memberjunction/ng-conversations(Angular widget) ↔ your app. Covers: when to use each layer, the slot system (6 slots:header/agentPresence/emptyState/messageRenderer/messageExtra/demonstrationSurfacewith project / wrap / subclass modes), Before/After cancelable events (beforeAgentTurn,beforeToolInvoked,beforeResponseFormSubmittedwithevent.Cancel = trueenforced;sessionStarted/sessionChannelStateChanged/sessionEndedinformational), persona inputs ([showAgentCharacter]+agentCharacterConfig),--mj-chat-*design tokens, default-agent resolution chain (explicit → app-scoped → global → code-const Sage fallback), sessions adapter bridging to PR #2787’sVoiceSessionService, multi-provider scoping, runtime pre-warming via@RegisterForStartup. Read before building any chat surface (overlay, full workspace, embedded panel) OR before forking the widget — slots + events almost certainly cover the use case. - Predictive Studio Guide — How MJ trains predictive models on a client’s own data (member retention/renewal, lapse/lead scoring) and scores records with them — core MJ, not an OpenApp, composed onto existing substrates. Covers the 4-layer architecture (data → feature → model → inference); the self-managing Python sidecar (
MLSidecarin@memberjunction/predictive-studio-sidecar— the sqlglot-ts bundled-microservice pattern: managed child-process spawn on an ephemeral port is the default, Docker-free; remote-URL mode for scaled deployments;npm run setup:python; the/train+/predict+/healthcontract defined once in@memberjunction/predictive-studio-core); theFeatureAssemblyExecutorcorrectness backbone (one code path × three contexts; the raw-vs-preprocessing fit-once/apply-everywhere anti-skew split withfitted_preprocessingtravelling with the model; first-class point-in-time as-of assembly; theLeakageGuardEnforcerdeny-list + post-train single-feature-dominance flag → plain-language warning + blocked promotion); training (TrainingEngine→ immutable, versionedMJ: ML Modelsdistinct fromMJ: AI Models, with a locked holdout for honest metrics + full lineage); scoring (MLModelInferenceProcessor— a new'ML Model'Record Set Processing work type registered via@RegisterClasswithout forking the substrate; ephemeral by default, write-back viaOutputMapping; on-demand + scheduled); the genericExperiment→ExperimentSession→ExperimentSessionIterationprimitive + theExperimentOrchestratorwave loop (leaderboard / pruning / budget gate, run through RSP waves — reusable beyond ML); theMJ: ML Algorithms/Use Cases/Use Case Rankings6×7 guidance matrix; the (planned) Remote Operations + Actions + Model Development Agent; the lazy-loaded Studio dashboard (PredictiveStudioDashboardComponent+PredictiveStudioEngine+ 6 panels + embeddedmj-conversation-chat-areacopilot); a train+score walkthrough; and the live integration test (PS_INTEGRATION=1). Read before touching anything underpackages/AI/PredictiveStudio/**, theMJ: ML */MJ: Experiment*entities, the Predictive Studio dashboard, or before adding a trained-model / feature-assembly / experiment-search capability.
Angular / MJExplorer
Section titled “Angular / MJExplorer”- Dashboard Best Practices — Comprehensive patterns for building MJ dashboards including:
- Architecture and naming conventions
- State management with getter/setters
- Engine class patterns (no Angular services for data)
- User preferences and local caching
- Page Chrome — the shared
<mj-page-layout>+<mj-page-header>+<mj-page-body>trio that every Explorer dashboard uses, with slot rules ([meta]/[actions]/[toolbar]) and documented exceptions - Layout patterns, permission checking, and more
- Explorer Chrome Conventions — The full rulebook for MJ Explorer’s shared chrome — slot rules (
[meta]is state,[actions]is verbs,[toolbar]is secondary controls), filter UI decision tree, and the canonical exception list. Sub-pages of left-nav shells use<mj-page-header-interior>(Section 10) — a two-row card with[Title]+[Subtitle]inputs and the same slot conventions as<mj-page-header>— NOT their own<mj-page-header>(which would produce a doubled-header). Read before doing chrome work or deciding to deviate. (Lives inplans/, not this folder.) - Forms Architecture Guide — How MJ renders/edits entity records across all surfaces from one set of forms — full-page tabs, modal dialogs, and slide-in panels. Covers:
- The 4-layer architecture (
MjEntityFormHostComponent→ presentation shells →MJFormPresenterService), all in@memberjunction/ng-base-formswith zero Explorer/Router coupling - How generated, custom (
*Extended), and interactive (EntityFormOverride) forms coexist, plus the variant picker EntityFormConfig— per-instance control over toolbar / related-entity sections / collapsibility / width / in-form navigation, applied without regenerating any form- Imperative (
forms.open({...})) and declarative (<mj-form-dialog>/<mj-form-slide-in>) usage - Read this before building any bespoke “edit a record in a dialog/slide-in” component — the generic capability almost certainly covers it.
- The 4-layer architecture (
- Lazy Loading Guide — How MJExplorer’s code-split lazy loading works:
- Adding new dashboard components (zero config — just
@RegisterClass+ feature module) - Making a package lazy-loadable (add subpath exports to
package.json) - Adding new feature modules with subpath exports
- How the auto-generated lazy config is produced by
mj codegen manifest --lazy-config - Troubleshooting lazy loading issues
- Adding new dashboard components (zero config — just
- Navigation and Routing Guide — How the shell owns URL state, back/forward navigation, adding URL-synced sub-navigation to a component.
- Optimistic-UI Save Pattern — ⚠️ Status: proposal for review, not yet an adopted convention. Documents a pattern several Angular surfaces adopted independently (render the user’s action immediately, reconcile after
Save()resolves) plus a proposed framework hook (EntitySaveOptions.OnValidated) that generalizes it. Includes two competing implementations — generic vs. inline — for reviewers to compare before any codebase-wide sweep. Read for context on perceived-latency work on chat/list/settings surfaces; do not treat as settled guidance until the proposal is resolved. - Media Player package — The generic
mj-media-player(zero-MJ-dep audio/video player: transport, real waveform scrubber, playback speed, ±skip, time-synced transcript, multi-track video grid) and the MJStorage-boundmj-storage-media-playerwrapper (resolves anMJ: Filesid to a permission-gated, Range-streamed/mediaURL with server-supplied waveform peaks). Used by the artifact audio/video viewers + previews and the realtime session-review overlay.
Testing
Section titled “Testing”- Integration Testing Quickstart — The integration-test tier — real provider stack (live DB + GraphQL, real cache managers/engines), headless, sitting between unit tests (mocked) and browser/computer-use regression (“mock the top layer, keep everything else real”). Covers: the one-library-two-front-ends architecture (
@memberjunction/testing-integrationcheck bundles on oneIntegrationCheckRegistry, consumed identically by thetsxsuite scripts /run-all.tsaggregator AND the metadata-driven “Integration Test”TestTypedispatched byIntegrationTestDriverthroughmj test); the dedicated-process rule (the instrumented cache MUST beLocalCacheManager’s first caller —MJ_INTEGRATION_TEST=1on the CLI; server-transport suites refuse to run inside a live MJAPI); the two proof techniques (UniqueFiltercold-cache determinism with zero mutation +InstrumentedLocalStorageProviderper-category counters); tiers/gating (deterministic / mutation / live-model viaRUN_MUTATION_TESTS/RUN_AGENT_TESTS, plusPS_INTEGRATIONflows); every way to run it (npm run test:integration, per-suitetsx,mj test run/suite, the smoke test, the cross-server rig, the golden diff, the CI PR gate) and all four authoring methods (add a check to a bundle · new bundle + Test row + suite membership · metadata-only composition · standalone script). Read before running or extending the integration suites, or touching anything underpackages/TestingFramework/testing-integration/. - Angular Testing Guide — How to test MJ Angular components. MJ runs Vitest everywhere (Jest is deprecated). Two complementary styles: class-level tests (instantiate with
new, exercise pure logic — getters, navigation methods,@Outputemission — fast, no DOM; these already exist and stay) and DOM-level tests (render into a headless jsdom DOM withTestBed+ComponentFixture, set@Inputs, dispatch DOM events, assert on rendered output — the half of a component’s contract that lives in the template:@ifgating, bindings,(click)wiring, conditional classes, a11y attributes, which class-level tests cannot see). Includes a decision tree for which style to write, and when to reach for live Playwright/e2e instead (WebRTC and real-media paths). Rollout plan:plans/testing/angular-dom-testing-rollout.md.
Theming and visual design
Section titled “Theming and visual design”- Theming — Pointer page. Authoritative theming guide is co-located with
ThemeServiceatpackages/Angular/Generic/shared/THEMING.md. - App Color Architecture — Why dashboards must not hardcode hex values; how to migrate to design tokens.
Completed / archived
Section titled “Completed / archived”The complete/ subdirectory holds guides whose subject matter has been fully implemented and absorbed into the codebase. Kept for historical context — newer guides should go in the top-level folder.
Adding a new guide
Section titled “Adding a new guide”A topic earns a guide in this folder when:
- It spans multiple packages or touches the framework as a whole — package-internal guidance belongs in that package’s
CLAUDE.md. - It captures non-obvious patterns a developer would not derive by reading the code alone (gotchas, conventions, “why we do it this way”).
- The patterns have been validated in production — speculative designs belong in
plans/, not here.
When adding a guide, also:
- Add an entry to this README under the appropriate section. This README is the single index — root
CLAUDE.mdlinks here rather than maintaining a parallel list, so there is nowhere else to update. - If the guide is a hard requirement for a specific subtree, add a pointer from that subtree’s
CLAUDE.md(e.g.packages/Angular/CLAUDE.md) so it loads when someone works there.