> ## Documentation Index
> Fetch the complete documentation index at: https://docs.encoreos.io/llms.txt
> Use this file to discover all available pages before exploring further.

# ADR-026: LangChain Ecosystem & Agent OS — Not Adopted

> 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: sho…

**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)

| Capability the Lang\*/Agent OS tools sell         | Encore already has it                                                                                                                                         |
| ------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Provider-agnostic LLM abstraction / model routing | `AIProvider` seam over the Vercel AI Gateway (**PF-111**), `supabase/functions/_shared/ai/` — module→model lanes, retry, error sanitization, PHI-lane routing |
| Streaming / tool-calling client layer             | Vercel AI SDK (`ai@6`, `@ai-sdk/react`) + custom SSE transport (`src/platform/ai/`)                                                                           |
| Durable / branching agent orchestration           | pgmq durable executor (`supabase/functions/workflow-executor-worker`) + the **ADR-024** `@/platform/workflow` execution seam                                  |
| Tracing / datasets / experiments / scores         | **Self-hosted Langfuse** (L2) + Promptfoo gate (L3) + JSONL floor (L1) — see [EVAL\_OBSERVABILITY.md](/development/EVAL_OBSERVABILITY) and **ADR-023**        |
| Skill-regression evals                            | Shipped: `npm run eval:skills` (`eval-skills.yml`)                                                                                                            |
| Spec-driven dev workflow for coding agents        | `eos-spec` CLI + skills/commands/hooks with regulated-core gates                                                                                              |
| Prompt management                                 | DB-driven (`pf_ai_skills` + `pf_ai_skill_overrides`, org overrides)                                                                                           |

[ADR-023](/architecture/decisions/ADR-023-agent-harness-and-eval-tooling-direction)
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](/architecture/decisions/ADR-011-edge-functions-deno)); 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)

| Criterion (weighted toward a HIPAA / 42-CFR-Part-2 ERP)                | Opt 1 — Extend in-house (bespoke) | Opt 2 — Build on backbone (chosen) | Opt 3 — Adopt external |
| ---------------------------------------------------------------------- | :-------------------------------: | :--------------------------------: | :--------------------: |
| (a) PHI-lane enforceability — redaction/lane structurally in-perimeter |                 5                 |                **5**               |            2           |
| (b) Checkpoint/resume fidelity for long-horizon runs                   |                 3                 |                **4**               |            4           |
| (c) BAA posture / data-residency of any dependency                     |                 5                 |                **5**               |            2           |
| (d) Maintenance surface & team familiarity                             |                 2                 |                **4**               |            2           |
| (e) Subagent fan-out support                                           |                 3                 |                **3**               |            4           |
| **Total**                                                              |               **18**              |               **21**               |         **14**         |

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

## Related Documents

* [ADR-023 Agent Harness, Skills & Eval-Tooling Direction](/architecture/decisions/ADR-023-agent-harness-and-eval-tooling-direction) — broader harness/eval disposition
* [ADR-024 `@/platform/workflow` Execution Seam](/architecture/decisions/ADR-024-platform-workflow-execution-seam) — durable cross-core execution
* [ADR-010 Core–PF Dependency Boundary](/architecture/decisions/ADR-010-core-pf-dependency-boundary)
* [ADR-011 Edge Functions on Deno](/architecture/decisions/ADR-011-edge-functions-deno)
* [Eval & Observability](/development/EVAL_OBSERVABILITY)
* `src/platform/ai/README.md` — PF-111 AI integration layer & model routing
