LangGraph Agents
Production-grade LangGraph.js (Node 24+, TypeScript strict,
).
Owns
runtime doctrine — placement, prompt/capability injection, graph-spec sync. Diffs via
or
. Framework choice:
.
Applicability
| Context | Doctrine strength |
|---|
| Greenfield agent-api (new LangGraph runtime) | MUST follow build workflow gates — dev-chat, budgets, normalize-before-truncate, separate skill cap. Topology: suggest for most MCP/tool-heavy cases — lock in ; not a hard rule |
| Brownfield existing agent | RECOMMENDED migration toward same controls; orphan recovery does not Critical-fail missing topology |
| Intentional MCP redesign | Sync + refs in same delivery — treat greenfield MUST for topology/budget/evidence sections touched |
Brownfield open ReAct valid until deliberate topology change. Greenfield MUST = new agent-api + intentional MCP redesign only.
Routing (read first)
| Signal | Action |
|---|
| No framework lock / CrewAI requested | Stop → |
| Orphan / lost structure / layout unclear | Run orphan checklist before features (references/orphan-recovery-checklist.md
) |
| GitLab or SDD version scope | Defer to harness ../../ns-harness/references/code-skill-routing.md
— do not absorb |
| Approved placement/inject plan ready for diff | Hand off to for implementation — except greenfield bootstrap copy (references/bootstrap-agent-runtime.md
) |
Boot (mandatory)
See
../../ns-harness/references/session-boot.md
—
complete Session boot (blocking) there, then:
- Confirm and when touching runtime
- Load placement/inject refs before path decisions (
references/placement-and-domains.md
, references/prompt-and-capability-injection.md
)
- Continue this skill
Success: placement + inject doctrine + project rules. Failure: invented folders or external frameworks.
When to use
| Situation | Action |
|---|
Greenfield agent-api (no {agent_api_root}/package.json
) | Build workflow Phase 0 then Phase 1 bootstrap — references/bootstrap-agent-runtime.md
|
| Brownfield / orphaned runtime | Run Orphan recovery (references/orphan-recovery-checklist.md
) before features |
| New file / unclear folder | Placement Decision Block + references/placement-and-domains.md
|
| System prompt / skill inject / bind | Prompt/Capability plan + references/prompt-and-capability-injection.md
|
| Topology / state / capabilities change | Spec Sync Gate — update in the same delivery |
| MCP with many servers/tools | Read references/mcp-complex-access.md
+ references/capability-governance.md
|
| Token blow-up / slow turns | Read references/context-window-and-tokens.md
|
| Provider message/reasoning quirks | Read references/message-content-blocks.md
|
| HITL / streaming UX | Read references/streaming-and-hitl.md
|
| JSON planner / analyst chooses tools | Operator-progress channel — templates/contracts/planner-contract.md
+ references/streaming-and-hitl.md
|
| Evals before merge | Read references/evals-and-gates.md
|
Core doctrine
LangGraph = control flow. MCP/local tools = capabilities under graph. Small graph state (refs, summaries). Checkpointer = full state; context window = LLM view — separate.
System prompt: compose
(motor) +
(product persona)
per LLM invoke. Never persist composed system/persona text in graph state, checkpointer, or durable
. Summary
at index 0 ≠ full system —
references/prompt-and-capability-injection.md
,
references/message-content-blocks.md
.
Locale: conversation-observed
(detection-first from human messages ± intent slots);
weak hint only; Intl formatters in code — not fixed bootstrap locale.
references/evidence-and-fidelity.md
,
templates/snippets/conversation-locale.ts.snippet
.
Operator progress (JSON planner hops): greenfield
with a planner/analyst that emits structured
(no
on that hop)
MUST persist
(or
analysis.userFacingIntent
) +
on
.
language MUST match the current user message (last
) — not English unless that message is English; not product default locale. Machine
stays English for audit. Emit SSE
from that field at
node entry of the next hop — not
, not in durable
. Hop 0 uses generic copy from
conversation/presentation/
(or locale) in the
same operator language. Open ReAct +
uses
/
only. Details:
templates/contracts/planner-contract.md
,
references/streaming-and-hitl.md
.
Three capability kinds bind to the model:
| Kind | LLM wire name | Internal id |
|---|
| Local tool | | |
| MCP tool | | |
| Skill procedure | | |
Wire names must match
(use
separators; colons only in internal ids). Colon in a
new wire name is Critical on review.
Pre-change gates
Before new file or inject/bind change: complete all three. No code until posted.
1. Placement Decision Block
markdown
### Placement Decision Block
- Artifact: …
- Type: …
- Target path: …
- Layer: …
- Refs: placement-and-domains.md
- do_not_create_under: […]
Full matrix:
references/placement-and-domains.md
.
2. Prompt / Capability plan
markdown
### Prompt / Capability plan
- Compose: base_invariant + injected (rebuild per invoke; not in state/checkpointer/durable messages)
- Motor (`base_invariant`): [gather-no-Markdown / sole-writer / tool discipline / JSON planner userFacingIntent is SSE not Markdown / …]
- Product (`injected`): canonical path + persona/tone notes; mode-resolved: yes/no; modes: [...]; resolver: ...
- System layers touched: […]
- Canonical prompt path: …
- Session overlay: yes/no
- Bind list: […]
- Auto-inject skills: […] (exclusive of bind for same id)
- Truncate caps: tool vs skill body
- Bind parity: …
- Spec paths to sync: […]
Full doctrine:
references/prompt-and-capability-injection.md
.
3. Spec Sync Gate
Nodes, edges, state, capabilities, recursion_limit, or wire names change: update
same delivery. Stale archive ≠ SoT — sync spec to intended runtime; do not force-fit live code to stale archive.
Reference map
Load on demand — do not memorize whole files.
| Reference | Read when |
|---|
references/bootstrap-agent-runtime.md
| Greenfield copy of |
references/orphan-recovery-checklist.md
| Project structure unclear or agent "lost" |
references/runtime-layout.md
| Scaffolding, refactors, layer violations |
references/placement-and-domains.md
| Where to put files; domain vs graph vs config |
references/prompt-and-capability-injection.md
| System prompt layers, bind vs inject, bind parity |
references/message-content-blocks.md
| AIMessage/HumanMessage/ToolMessage across providers |
references/context-window-and-tokens.md
| trim, summarize, tool vs skill body caps, |
references/mcp-complex-access.md
| Multi-server MCP, discovery, transport, lifecycle |
references/capability-governance.md
| Allowlist, classification, rate limits, tool budgets |
references/evidence-and-fidelity.md
| State-backed evidence, fidelity gate, conversation-observed locale |
templates/snippets/conversation-locale.ts.snippet
| resolveConversationLocale
+ Intl |
templates/snippets/tool-budget.ts.snippet
| Per-turn tool/MCP caps, arg fingerprint duplicate-skip |
templates/snippets/prepare-llm-messages.ts.snippet
| helper |
references/error-and-reliability.md
| Tool errors, circuit breaker, retries |
references/observability.md
| Postgres audit, LangSmith, OTel, run context |
references/architectures.md
| ReAct, plan_execute (suggested start for most MCP), other topologies; node id ≠ state channel |
references/streaming-and-hitl.md
| SSE envelopes, operator from planner state, , resume |
templates/contracts/planner-contract.md
| JSON planner hops: + |
references/evals-and-gates.md
| Architecture, tool-selection, memory evals |
references/anti-patterns.md
| Review gate before marking done |
Templates:
(greenfield tree),
,
,
(brownfield patches).
Session inputs
| Variable | Required |
|---|
| Default |
| What to build, fix, or review |
Orphan recovery (brownfield first)
Runtime disorganized or team blocked:
- Read
references/orphan-recovery-checklist.md
; score project.
- Gap report: structure, placement, context window, inject/bind parity, MCP governance, HTTP, spec sync.
- Ordered fix plan (one phase per message if large).
- Implement via .
No new graph nodes or MCP servers until layout + governance baselines pass.
Build workflow (greenfield or post-recovery)
Phase 0 — Spec gate
If
is missing, create it from
. Minimum sections: locked header (
,
,
), domain ownership, prompt composition, state schema, nodes table, edges, interrupts, memory, capability bind/inject table, recursion_limit, HTTP routes.
If the user has no architecture decision yet, stop and invoke
first.
Phase 1 — Skeleton
If
{agent_api_root}/package.json
is missing, run
scripts/bootstrap-agent-runtime.mjs
(
references/bootstrap-agent-runtime.md
). Then
in
.
Do
not assemble the tree from
. Do
not copy another product. After copy: align
with
; put domain in
+
via later phases /
.
Brownfield existing tree: align per
references/runtime-layout.md
— snippets for patches only.
Phase 2 — State and persistence
After bootstrap,
verify these exist; do not re-scaffold. Fill only gaps vs
.
- with reducer ( or Zod + ).
- in dev/prod; only in .
- Every invoke/stream: via .
- JSON planner/analyst (no on that hop): declare + (or nested on ) in state schema —
templates/snippets/state.ts.snippet
.
Phase 3 — LLM and messages
- Provider config in (infra only — no domain prompts).
- Prefer JSON mode + Zod parse for structured turns; avoid on OpenAI-compatible local servers.
- Normalize provider output via / — see
references/message-content-blocks.md
.
Phase 4 — Context window (mandatory)
Implement per
references/context-window-and-tokens.md
:
- before every LLM call.
- then before enters state.
- Separate
CONTEXT_SKILL_BODY_MAX_CHARS
for skill bodies (snippet ).
- Optional with persisted compaction ( + rewrite) in the same agent-node return.
Never pass raw
to the model.
Phase 5 — Capabilities
- Local s in .
- MCP: governed client — discovery → local allowlist → wire names → singleton client lifecycle (
references/mcp-complex-access.md
).
- Skills: auto-discovered → or auto-inject (exclusive per id).
Apply
references/capability-governance.md
and
references/prompt-and-capability-injection.md
before
. Enforce
bind parity. Wire per-turn tool/MCP budgets from
templates/snippets/tool-budget.ts.snippet
when MCP or external tools are bound.
Phase 6 — HTTP and interaction mode
| Mode | Requirements |
|---|
| , POST /threads/:id/message
|
| SSE envelope per references/streaming-and-hitl.md
; greenfield MUST ship gated by (local-only); JSON planner hops MUST emit operator from state |
| HITL | + with |
Brownfield missing dev-chat: recommend add — not Critical. Postman synced with live routes.
Phase 7 — Observability
Wire
references/observability.md
:
,
,
,
, persist
. LangSmith and OTel are opt-in.
Phase 8 — Evals and review
- Add suites per
references/evals-and-gates.md
.
- Run
npm run build && npm test
in .
- Invoke on the diff; ask it to verify placement, inject, wire-name, and bind-parity anti-patterns when the diff touches .
Maintenance workflow
Ongoing work (not greenfield):
- Three Pre-change gates
- matches intended graph after change
- Layer: graph node, conversation, MCP, memory, HTTP
- Read matching reference before edit
- Minimal diff via with placement/inject handoff
- Postman on HTTP route change
- Re-run orphan checklist items touched
MCP complex access (quick rules)
Multiple MCP servers, overlapping or large catalogs:
- Discovery filter — allowlisted before bind
- Local classify —
read | write | destructive | admin
; never trust server read-only flags
- Singleton client — one per process; no per-request stdio in prod
- Transport — Streamable HTTP deployed; stdio local single-user dev only
- Errors — MCP = recoverable ; protocol fail may abort with HTTP/SSE
- Secrets — env or / request payload only; never state or checkpointer
Full:
references/mcp-complex-access.md
.
Handoff to ns-coder
When implementation is approved, delegate with:
markdown
## LangGraph implementation task
- Root: {agent_api_root}
- Spec: path/to/graph-spec.md
- Phase: [number and name from this skill]
- target_paths: […]
- layer: […]
- do_not_create_under: […]
- injection_notes: [layers / bind vs auto-inject / caps]
- spec_paths_to_sync: […]
- References to apply: [list]
- Acceptance: build + test pass; orphan checklist items [n] resolved; placement + inject + bind parity verified
- Review: ns-reviewer after tests — must check placement, inject, wire names (`:`), bind parity (load ns-langgraph-agents anti-patterns when diff touches agent-api)
Stay here for diagnosis, spec, placement, governance design, and
greenfield bootstrap copy.
for feature diffs. Version-scoped features:
.
Stop conditions
| Condition | Action |
|---|
| No and user wants code now | Create spec or invoke architect |
| Greenfield assembled from snippets / another product tree | Stop; run scripts/bootstrap-agent-runtime.mjs
|
| Path outside placement matrix / inventing folders | Stop; propose legal path |
| Domain / locale / copy landing in or | Stop; reroute to conversation/config |
| Bind without parity (dispatchable but unbound) | Stop; fix bind or document unbound + test |
| in a new wire name | Stop; use separators |
| Skill auto-inject + bind same id without explicit decision | Stop; choose one mode |
| CrewAI requested | Redirect to appropriate skill |
| Change spans >3 layers without plan | One-line phased plan, wait for approval |
| Critical security gap (secrets in state, ungoverned MCP) | Block feature work; fix governance first |
Related skills (ownership)
| Skill | Owns |
|---|
| Doctrine, placement, inject plan, graph-spec, greenfield scaffold |
| Feature diffs + review loop (not initial scaffold copy) |
| Verdict; when diff touches , apply placement + inject + wire-name + bind-parity anti-patterns from this skill |
| Framework choice before Phase 0 when unlocked |
| Version features after scaffold exists (or first task = bootstrap) |
| Runtime debug |
Forbidden
- Emitting planner as or as Markdown in (SSE only; composer remains sole Markdown writer)
- Writing in a language other than the current user message (e.g. English progress when the operator wrote Portuguese)
- Persisting composed system/persona prompt (
base_invariant + injected
) — or secrets/API keys — in graph state, checkpointer, or durable (rebuild system text per invoke)
- Treating bootstrap / / as primary locale SoT, or persisting sticky thread locale (use conversation-observed + Intl)
- Passing unbounded tool/MCP output into
- Applying tool/MCP truncate caps to skill bodies (use
CONTEXT_SKILL_BODY_MAX_CHARS
)
- Trusting MCP tool metadata for security classification
- Spawning stdio MCP subprocesses per HTTP request in production
- compiling
- Domain qualify/conversation prompts in
- Locale/presentation under
- Nudge as fake
- Tests under
- Greenfield tree from snippets or another product — use only