Skip to main content
Status: Accepted (directional) Date: 2026-06-11 Decision Makers: Jeremy Bloom (project owner)

Context

Following a review of the AI automation surface (Claude Code skills/agents, the eos-spec navigator) and the in-product AI runtime, the question was raised: should Encore OS adopt LangChain, LangGraph, LangSmith, or Agent OS? The review found that the problems these tools solve are already solved in our stack, and that adopting them would duplicate working infrastructure while widening a HIPAA-regulated dependency surface.

What already exists (the reason most of this is redundant)

ADR-023 already dispositioned the broader agent-harness and eval-tooling question (deferring Mastra and any product-agent harness; recording that Promptfoo + self-hosted Langfuse already shipped). It did not name the LangChain family or Agent OS. This ADR closes that gap with an explicit, durable disposition so the question does not recur.

Options Considered

Option A: Adopt LangChain / LangGraph as the orchestration framework

  • How it works: wrap LLM calls and agent loops in LangChain abstractions; use LangGraph for stateful graph orchestration.
  • Pros: large ecosystem; off-the-shelf agent/graph primitives.
  • Cons: duplicates the AIProvider seam; heavy, fast-churning abstraction; Python-first with JS as the second-class citizen (our edge runtime is Deno, see ADR-011); adds a large new dependency inside the HIPAA boundary.
  • Why not chosen: negative ROI — it replaces a small abstraction we fully control with a large one we do not.

Option B: Adopt LangSmith for observability/evals

  • How it works: route traces, datasets, and scores to LangSmith.
  • Pros: integrated eval tooling.
  • Cons: redundant with the self-hosted Langfuse stack already shipped (PR #815); hosted-by-default, which is the wrong posture for PHI/residency.
  • Why not chosen: Langfuse already covers tracing/datasets/experiments/scores, and self-hosting keeps trace storage on our box.

Option C: Adopt Agent OS

  • How it works: either (a) LangChain’s hosted agent-deployment runtime, or (b) buildermethods’ spec-driven dev-workflow system for coding agents.
  • Pros: turnkey agent deployment (a); ready-made spec workflow (b).
  • Cons: (a) is a Python-centric hosted runtime that mismatches our Supabase/Deno/Vercel stack and has no product agent to host; (b) duplicates eos-spec + skills/commands and lacks our regulated-core gates (compliance-reviewer, RLS, PHI lanes).
  • Why not chosen: architectural mismatch or duplication in both readings.

Option D: Keep extending the provider-agnostic seam ✓ (Chosen)

  • How it works: treat the AIProvider seam (PF-111) as the platform AI substrate; add new AI capability by extending the seam and the existing eval/observability layers, not by adopting an orchestration framework.
  • Pros: no new HIPAA dependency surface; full control of the abstraction; preserves per-module/per-lane model routing and BAA boundaries; consistent with ADR-023/ADR-024.
  • Cons: we own the orchestration code (e.g. the agentic tool-loop) rather than outsourcing it.
  • Why chosen: lowest risk, no duplication, and the “cons” are small and already mitigated by keeping that code small and tested.

Decision

We do not adopt LangChain, LangGraph, LangSmith, or Agent OS.
  • LangChain — not adopted. Redundant with the AIProvider seam (PF-111); heavy, fast-churning abstraction; Python-first / Deno-second; widens the HIPAA dependency surface.
  • LangGraph — not adopted as a framework. Durable / branching orchestration is already served by pgmq (workflow-executor-worker) plus the ADR-024 execution seam. If a concrete, funded product agent ever needs in-process stateful orchestration, prefer a lightweight in-stack TS state machine over the framework — seeded by the now-extracted, unit-tested agentic tool-loop at supabase/functions/_shared/ai/agentic-loop.ts.
  • LangSmith — not adopted. Redundant with self-hosted Langfuse (L2, PR #815); self-hosting is a PHI/residency advantage.
  • Agent OS — not adopted (both meanings). (a) LangChain’s hosted agent-deployment runtime: architectural mismatch and no product agent to host (ties to the ADR-023 harness deferral); (b) buildermethods’ spec-driven dev workflow: duplicates eos-spec
    • skills/commands and lacks the regulated-core gates.

Principle enshrined

The provider-agnostic AIProvider seam is the platform’s AI substrate. New AI capabilities extend the seam (and the existing L1/L2/L3 eval-observability layers) rather than adopt an orchestration framework. Prompts stay in the DB-driven system (pf_ai_skills), not in a third-party prompt-management product, to keep the seam — and the BAA/PHI boundary — authoritative. Revisit trigger: a concrete, funded product-agent feature (the same gate ADR-023 sets for Mastra/Claude Agent SDK). At that point, re-evaluate LangGraph specifically against a hand-rolled state machine; the rest of this disposition stands regardless.

Consequences

Positive

  • No new always-on dependency inside the HIPAA boundary; the model/gateway/streaming and eval/observability layers already in place keep their single owner.
  • The decision is durable and discoverable, so the “should we use LangChain?” question stops recurring.
  • The agentic tool-loop is now isolated and tested, making any future state-machine evolution cheap without a framework.

Negative

  • We own the orchestration code (the agentic loop) rather than outsourcing it to a framework.

Mitigations

  • The orchestration surface is deliberately small and now unit-tested (agentic-loop.test.ts).
  • The revisit trigger above keeps the door open if a real product agent ever justifies reopening LangGraph specifically.

Revision 2026-06-24 — Orchestration backbone for the Agentic Workspace (revisit trigger fired)

Status of this revision: Recommendation (directional), pending a time-boxed PF-125 Phase-0 prototype — see “Prototype gate” below. Date: 2026-06-24 Decision Makers: Jeremy Bloom (project owner), PF Supersedes: nothing — this section is additive. The original disposition above (LangChain / LangSmith / Agent OS — not adopted) stands unchanged.

Context — the revisit trigger fired

The original ADR-026 set a single revisit trigger: “a concrete, funded product-agent feature” (line 115-117), at which point LangGraph specifically would be re-evaluated against a hand-rolled state machine. That trigger has now fired: the Encore Agentic Workspace program (docs/superpowers/specs/2026-06-24-encore-agentic-workspace-roadmap-design.md) is the concrete, funded product-agent feature, and its Phase-0 spike (roadmap §4) requires a revised ADR-026 that scores the orchestration build-vs-adopt question against a five-criterion rubric: (a) PHI-lane enforceability, (b) checkpoint/resume fidelity for long runs, (c) BAA posture / data-residency of any adopted dependency, (d) maintenance surface & team familiarity, (e) subagent fan-out support. This revision re-scores the question honestly under that trigger. Scoring an option is not adopting it. The capability the external options sell — durable, branching, checkpointed orchestration — already exists in-perimeter and was verified on disk during this review:
  • supabase/functions/workflow-executor-worker/index.ts (1311 lines) consumes workflow_execution_queue via pgmq_read, runs batches under a per-org worker_running optimistic-lock semaphore, classifies errors, writes pre/post/failure/waiting checkpoints, routes permanent failures to workflow_dlq, and resumes via the cron-driven fw_claim_queued_executions fallback when the worker is down.
  • supabase/functions/_shared/checkpointHelpers.ts provides writePreStepCheckpoint / writePostStepCheckpoint / writeFailureCheckpoint / writeWaitingCheckpoint / writeSkippedCheckpoint (for nodes bypassed during resume) + classifyError (transient/permanent/timeout).
  • 24 migrations already wire pgmq / cron.schedule.
  • The ADR-024 @/platform/workflow seam is more built than previously assumed. enqueueWorkflowExecution and getThrottleStatus (src/platform/workflow/execution.ts), the fw-enqueue-execution edge function, and useWorkflowEnqueue.ts all exist on disk and route through pf_has_org_access with idempotency. Only ADR-024’s frontmatter still reads Status: Proposed — that is documentation drift to reconcile, not a missing seam.
  • The reasoning spine is in-house and unit-tested: supabase/functions/_shared/ai/agentic-loop.ts (MAX_LOOP_ITERATIONS = 5) already routes every call through the fail-closed PF-111 PHI lane (PHI_MODELS = cl+pm, BAA/ZDR) and already neutralizes tool results (neutralizeToolResultFn) and guards tool names (assertToolCallIsAllowedFn) before re-prompting.
The genuinely-missing pieces are agent-conversation-level run-state serialization (the loop holds message/tool-call state in memory today) and governed subagent fan-out + join (the existing cascade machinery is built to limit / DLQ fan-out past max_cascade_depth, not aggregate it). Both sit on the existing seams — this is additive, not a rewrite.

Options considered (re-scored under the fired trigger)

  • Option 1 — Extend in-house, bespoke: evolve agentic-loop.ts into a persisted run-state machine on a new, parallel pgmq + pg_cron queue/worker/checkpoint table — the literal “lightweight in-stack TS state machine” of the original disposition, taken to a fully bespoke extent off the ADR-024 seam.
  • Option 2 — Build on the in-perimeter backbone (chosen, gated): reuse the ADR-024 @/platform/workflow seam + workflow-executor-worker + FW-48 checkpoints for durable run-state / checkpoint / resume / scheduling, with agentic-loop.ts as the in-house reasoning/tool layer on top. PF-125 serializes agent Run/Task state into the FW-48 checkpoint model — net-new schema on a proven substrate, no new external dependency in the HIPAA boundary.
  • Option 3 — Adopt external: a durable-execution vendor (Temporal / Inngest, self-hosted in-perimeter) and/or LangGraph (TS) as the PF-125 backbone.

Rubric scores (all three options; 1-5 per criterion, /25 total)

Reading the table. Options 1 and 2 are both fully in-perimeter and tie on the two axes that dominate this regulated context (a, c). They diverge on (d): Option 1 hand-builds a second durable executor + checkpoint table + DLQ/retry/cascade stack parallel to workflow-executor-worker / fw_execution_steps, off the sanctioned ADR-024 seam — the largest owned maintenance surface and widest regression frontier of the set, and a direct contradiction of this ADR’s Decision D (“extend the seam”). Option 2 extends the seam, inheriting the worker’s already-tested retry/DLQ/checkpoint/cascade machinery, so it dominates Option 1 on (d) at equal (a)/(c). Option 3’s two real wins (b, e) are each ~70% already covered in-perimeter, while its costs are structural — BAA/residency (hosted egresses PHI run-state; self-hosted adds a heavy always-on stateful PHI datastore inside the HIPAA boundary — the exact dependency-surface widening this ADR rejects at lines 25/52/92), a Deno-runtime mismatch (ADR-011; Temporal/Inngest SDKs are Node-centric, LangGraph Python-first; today there is zero temporal/inngest/langgraph dependency in package.json or supabase/), and PHI-chokepoint erosion (LangGraph owns the model-call layer, duplicating/bypassing the AIProvider seam — Option A’s named harm).

Decision

The orchestration backbone for PF-125 is built on the existing in-perimeter backbone (Option 2): extend the ADR-024 @/platform/workflow execution seam + the pgmq workflow-executor-worker + FW-48 checkpoints for durable run-state / checkpoint / resume / scheduling, with agentic-loop.ts as the in-house reasoning/tool layer. This is a recommendation pending the PF-125 Phase-0 prototype below.
  • LangGraph / LangChain / external durable-execution vendors — not adopted as the backbone. The original disposition (lines 89-105) stands, reaffirmed under the now-fired trigger. Option 3’s marginal, partially-redundant gains do not justify a second run-state owner or a new always-on stateful dependency inside the HIPAA boundary.
  • Option 2 over Option 1 because the durable substrate already exists and is tested: extending the seam beats hand-rolling a parallel executor on (d) at equal perimeter posture. Option 1 is the documented fallback — adopt it only if the prototype proves agent run-state genuinely cannot fit the FW execution/checkpoint model.
  • Narrowed revisit trigger (replaces the broad “any product agent” door for orchestration): re-open Temporal (self-hosted, model-agnostic) specifically only if proven, multi-day human-in-the-loop runs with complex durable-join semantics become a measured bottleneck that pgmq + checkpointHelpers demonstrably cannot carry. LangGraph remains rejected regardless (it owns the model call and erodes the PHI chokepoint).

Consequences

Positive
  • Zero new dependency inside the HIPAA / 42-CFR-Part-2 boundary; the PF-111 AIProvider/PHI-lane and the pgmq durable executor keep their single owner.
  • Reuses genuinely mature durable infra (per-org semaphore, FW-48 step checkpoints, retry-policy merge, circuit breakers, DLQ, fw_claim_queued_executions cron resume) that a framework would otherwise re-implement.
  • Boundary-clean: the ADR-024 seam lets CE/PM/HR enqueue durable agent runs without core-to-core imports (Constitution §1 / ADR-010).
  • The §6.1 two-path PHI rule, tool-result neutralization, and fail-closed routing already live in agentic-loop.ts and do not have to be ported into a foreign runtime.
Negative (named honestly, per the original ADR’s Option D con)
  • We own the orchestration code (PF-125 run-state, fan-out/join, resume) rather than outsourcing it.
  • Agent-loop iteration/message state is in-memory today — durable resume requires net-new PF-125 Run/Task serialization into the FW-48 model.
  • Subagent fan-out is greenfield: handleCascadeBlock / max_cascade_depth are built to block deep fan-out, so governed parallel fan-out + join/aggregation must be built, and the cascade guard re-configured to permit (not DLQ) governed fan-out.
  • The FW queue is workflow-rule-shaped (fw_workflow_executions, rule_id, trigger_condition); mapping free-form agent runs onto it needs a deliberate schema bridge (or a PF-125 sibling table), not a drop-in.
  • Ordering constraint (load-bearing): per roadmap §6, this orchestration layer (Layer 3) must land behind the Phase-1 guardrail spine (PF-27-EN-01 unified redaction, runtime injection screening, PF-30-EN-01 scoped principals off service_role, PF-126 governance-in-the-loop, fail-closed budgets). Building durable run-state ahead of the spine would strip protective friction (roadmap §2 #1 risk: indirect injection via tool-result / inter-agent re-prompts is unaddressed today).
Doc-drift to reconcile (Phase-0): ADR-024 frontmatter Status: Proposed is stale relative to the shipped enqueueWorkflowExecution / fw-enqueue-execution seam — reconcile alongside the PF-111/PF-27 frontmatter fixes.

Prototype gate (this decision is provisional until PF-125 proves it)

This is a recommendation, not a final commit, because three rubric criteria are unproven on the agent path: (b) is “high once adapted, not free,” (e) is greenfield against a guard that actively blocks fan-out, and the schema bridge is undemonstrated. Per roadmap §4 the Phase-0 spike is time-boxed to ~5 engineer-days and must clear the checklist below before the backbone is committed. If the spike fails the run-state-fit or fan-out items, fall back to Option 1 (bespoke in-house) and record that result; under no outcome does this revision adopt an external framework. Prototype checklist (all must pass):
  1. Run-state serialization fidelity — agent Run/Task message + tool-call state round-trips through the FW-48 checkpoint model and resumes after worker crash without re-executing committed steps.
  2. Governed subagent fan-out — re-configure the cascade guard to permit (not DLQ) governed parallel fan-out; child tasks inherit ≤ parent scope and draw budget from the parent run.
  3. FW-queue schema bridge — free-form agent runs map cleanly onto fw_workflow_executions / PF-125 sibling tables without breaking idempotency or org scoping.
  4. Claim-check payload offloading — large PHI message/tool blobs are stored out-of-line (object store or dedicated table); checkpoints reference by id only — workflow history does not saturate on LLM payloads.
  5. Run/version pinning — in-flight runs pin the skill prompt version, tool registry snapshot, and risk-rule set at run start; mid-run deploys cannot alter behavior for active runs.

Revision 2026-06-25 — PF-125 Phase-1 foundation: prototype gate resolved (extend-in-house, PF-125-owned run-state on the in-perimeter pgmq/pg_cron substrate)

Status of this revision: Accepted (for the PF-125 Phase-1 flag-OFF foundation). Resolves the Revision-2 “Prototype gate” for Phase 1. Date: 2026-06-25 Decision Makers: Jeremy Bloom (project owner), PF Supersedes: nothing — additive. The original disposition (LangChain / LangSmith / Agent OS — not adopted) and Revision 2’s rejection of an external durable-execution backbone both stand, reaffirmed. This revision only resolves which in-perimeter shape PF-125 takes.

What the Phase-1 build determined

Revision 2 recommended Option 2 (build on the in-perimeter backbone) but flagged it provisional behind a five-item prototype checklist, naming Option 1 (extend in-house) as “the documented fallback — adopt it only if the prototype proves agent run-state genuinely cannot fit the FW execution/checkpoint model.” Authoring PF-125 against the FW model answered exactly that question:
  • Run/Task state does not fit fw_workflow_executions. That table is workflow-rule-shaped (rule_id, trigger_condition) — built for declarative FW rules, not free-form, message/tool-call-bearing agent runs. Revision 2 itself listed this as a load-bearing Negative (“mapping free-form agent runs onto it needs a deliberate schema bridge or a PF-125 sibling table”). PF-125’s spec landscape scan confirmed pf_agent_runs / pf_agent_tasks / pf_agent_run_checkpoints / pf_agent_run_events are net-new with no table collision. So PF-125 owns its run-state schema (the sibling-table mechanism of Option 1) rather than bending agent runs onto the FW execution rows.
  • But the durable substrate is reused, not re-invented. This is not Option 1’s “second bespoke durable executor parallel to workflow-executor-worker.” The Phase 3-4 background/scheduled-run worker (spec FR-17) runs on the same proven pgmq + pg_cron pattern and the checkpointHelpers discipline; agentic-loop.ts remains the in-house reasoning/tool layer (PF-72), wrapped — not replaced — by the run-state machine. Zero new dependency enters the HIPAA boundary.
The net shape is extend-in-house with PF-125-owned run-state tables on the in-perimeter pgmq/pg_cron substrate — Option 1’s sibling-schema honesty about agent runs, kept on Option 2’s reuse-the-proven-substrate posture, with Option 3 (external framework / vendor) rejected as in Revision 2.

Decision

The PF-125 orchestration backbone is built in-house: PF-125 owns the Run/Task run-state, checkpoint, and event tables and three governed SECURITY DEFINER transition RPCs (pf_agent_run_advance / pf_agent_run_checkpoint / pf_agent_run_resume); it wraps runAgenticLoop as a durable, resumable, checkpointed run-state machine; and the Phase 3-4 background worker reuses the existing pgmq + pg_cron substrate. No external orchestration framework or durable-execution vendor is adopted.
  • Scope of this acceptance = the PF-125 Phase-1 flag-OFF foundation. Everything ships additive and behind pf.agents.interactive_tools_enabled (PF-45, default false) with the spine check unconditional. The write/PHI/budget seams (PF-30-EN-01 / PF-27-EN-01 / PF-111-EN-01) are integrated via documented fail-closed shims until those specs land; the production flag-flip is a separate human gate (PF-125 compliance signoff C-01..C-08).
  • Prototype-checklist disposition (Rev-2 items 1–5): item 1 (run-state serialization round-trip) and item 5 (run/version pinning) are delivered by the Phase-1 checkpoint model + run-pinned snapshot; items 2 (governed subagent fan-out), 3 (FW-queue bridge — now moot: PF-125 owns sibling tables), and 4 (claim-check payload offloading) are Phase 3-4 work behind the production gate, not the flag-OFF foundation.
  • Narrowed revisit trigger stands unchanged: re-open Temporal (self-hosted, model-agnostic) only if proven multi-day human-in-the-loop runs with complex durable-join semantics become a measured bottleneck pgmq + checkpointHelpers demonstrably cannot carry. LangGraph remains rejected regardless (it owns the model call and erodes the PF-111 PHI chokepoint).

Consequences

  • Positive: zero new HIPAA-boundary dependency (reaffirms the whole ADR); the run-state schema fits agent runs natively instead of straining the FW rule model; the proven pgmq/pg_cron/checkpointHelpers substrate is reused for Phase 3-4; agentic-loop.ts PHI-lane/neutralizer/allowlist guards are preserved, not ported into a foreign runtime.
  • Negative (named honestly): PF-125 owns its run-state machine, fan-out/join, and resume code (the original Option-D con). Run-state, fan-out, and the background worker are net-new owned surface — mitigated by reusing the substrate and keeping the reasoning layer unchanged.