An ISO 27001 Assessor That Writes Its Own Audit Trail
Prompts are one node in the graph. The compliance decision is a governed process — evidence in, a scored judgment, a deterministic route, an auditable record out.
This generation of AI platforms is built around the prompt. Prompt engineering, prompt templates, prompt libraries, chat windows — the prompt has become the primary way most teams touch a large language model, and for a lot of work that's fine.
FloMorphic doesn't argue with any of that. Prompts are first-class here: named, versioned, and reusable assets you can reference from anywhere in a process rather than text buried inside a workflow. What FloMorphic argues is that the prompt is one node in the graph — not the architecture. Around it sits persistent context, intelligent routing, reusable composition, native MCP, durable execution, and a distributed runtime. That's the context-oriented shape this whole blog keeps returning to: the prompt expresses behavior, but the process is what runs.
The fastest way to feel the difference is to build something a chat window structurally cannot do. So let's assess an ISO 27001 control — end to end — and end with an auditable record instead of a paragraph of advice.
Prompts as assets, not embedded strings
Before the process, the seam that makes it maintainable.
As AI systems grow, prompts sprawl — copies drift, nobody can say which version shipped, and governance is impossible. FloMorphic pulls the prompt out of the flow and treats it as a referenced template, which separates three concerns that usually tangle:
- Process design — the shape of the assessment
- Prompt design — how a control gets evaluated
- Runtime execution — what actually happens on a given run
A prompt becomes an organizational asset you can review and version on its own, the same way credentials live off the graph in a node-settings profile rather than on the canvas. You can export the flow, hand it to an auditor, or paste it into a blog post, and the sensitive parts stay behind.
Context is the operational backbone
Prompts define behavior; context defines state. Every FloMorphic process carries one structured context that evolves as it runs — and on this runtime that context isn't a payload passed between steps, it's a single live document the flow reads and mutates.
For an ISO 27001 assessment, the seed looks like this:
{
"assessment": {
"framework": "ISO27001",
"control": "A.5.15 Access Control",
"organization": "Acme Corp",
"assessment_date": "2026-08-17"
},
"evidence": {},
"result": { "status": "pending", "coverage_score": null, "decision": null }
}
That document travels through the whole process. MCP nodes deposit evidence into it. A model reads it and writes findings back. A js node computes a score onto it. A store node persists it. Unlike a chat session where context is transient and gone when the tab closes, here it's a durable execution artifact — the thing every node reads from and writes to, and the thing that survives the run.
Gather the evidence over MCP
A real control assessment pulls evidence from several systems. Using MCP Client nodes, the flow reaches out to whatever holds the truth:
A.5.15 Access Control
↓
MCP Client(s) ──► identity provider (MFA, privileged accounts)
↓ security platform (access-review cadence)
↓ documentation store (the written policy)
Updated Context
Each response lands under evidence on the context, available to everything downstream. If a source you need isn't an MCP server, it's a plugin node you write once — your identity provider or GRC tool becomes a native palette node, configured in a drawer and governed like everything else. The evidence-gathering is drawn, not hand-integrated.
After this stage the context is carrying something like:
"evidence": {
"access_review_process": true,
"mfa_enabled": true,
"privileged_account_monitoring": true,
"documented_policy": true
}
The model judges. It does not route, and it does not score.
Here's the part where a chat-first design and a process design diverge hardest, and it's the same discipline the claims walkthrough calls "money is not a model's job."
The tempting design is to let the model pick the outcome directly — a Covered / Partially Covered / Not Covered port, chosen by the LLM. You can do that on FloMorphic by binding functions to the LLM node, which gives it named output ports it routes on. But for a compliance decision that will be audited, a cleaner shape keeps the model's discretion narrow: the LLM node emits structured findings, and nothing downstream takes them on trust.
The node references a reusable prompt:
Evaluate the ISO 27001 control against the evidence in context. For each sub-requirement of the control, state whether the evidence satisfies it, your confidence, and any gap. Return structured findings only — do not decide the overall outcome.
And it writes back data, not a verdict:
"findings": {
"requirements": [
{ "id": "access_review", "met": true, "confidence": 0.95 },
{ "id": "mfa", "met": true, "confidence": 0.98 },
{ "id": "priv_monitoring", "met": true, "confidence": 0.9 },
{ "id": "documented", "met": true, "confidence": 0.88 }
],
"gaps": []
}
The coverage_score doesn't come from the model. It's computed in a js node scoped to $, where the scoped context arrives as input, there's no return, and the value of the last expression is the output:
let reqs = input.findings.requirements || []
let met = reqs.filter(r => r.met).length
let score = reqs.length ? Math.round((met / reqs.length) * 100) : 0
let uncertain = reqs.some(r => r.confidence < 0.8)
let result = { coverage_score: score, has_gaps: input.findings.gaps.length > 0, uncertain }
result
Now 95 is reproducible, explainable to a regulator, and identical on every run — because a human wrote the arithmetic, not a language model. The model contributed judgment about the evidence; the number is code.
The gate: a governed decision, not a generated one
A Rule node routes on the code-derived signals — never on the model's prose. Its handlers are the branches, each firing the port whose name is a truthy key:
let r = input.result || {}
let decision = {}
if (r.has_gaps || r.coverage_score < 60) {
decision = { not_covered: true }
} else if (r.coverage_score < 90 || r.uncertain) {
decision = { partially_covered: true }
} else {
decision = { covered: true }
}
decision
┌─────────────── covered ──────────► Approve → Persist record
Rule node ───┼──────── partially_covered ───────► Human review (park)
└─────────────── not_covered ──────► Remediation → Create action plan
Three named ports, three routes, all of them drawn before the flow ran. The 90 and 60 are business thresholds a compliance lead can change in a drawer — no pull request, no deploy. If you'd rather express the policy formally, switch the Rule node to Rego and get a policy language with a proof story instead of conditionals. Either way, the decision is governed: you can see every path it could take, and audit which one it took.
From assessment to action
Because the outcome is a route rather than a sentence, execution simply continues:
- Covered → an approval step, then the record is persisted.
- Partially Covered → the run parks on a human (below), then resumes.
- Not Covered → a remediation sub-flow that opens an action plan.
That last point is the whole difference between AI-assisted advice and a production process. The assessment doesn't end in a report someone has to act on. It is the action.
Persist the decision to a store
After a covered control clears approval, the result is written to a store node. The built-in Document store declares a table and lets you write structured, SQL-queryable records:
assessment_results(
control_id, organization, coverage_score,
decision, assessment_date, approved
)
{
"control_id": "A.5.15",
"organization": "Acme Corp",
"coverage_score": 95,
"decision": "Covered",
"assessment_date": "2026-08-17",
"approved": true
}
If your system of record is your own PostgreSQL, it's the same shape through a plugin node — your database becomes a palette node and the write is a step in the flow. The process is no longer generating a document; it's updating operational systems and leaving an auditable record. That record wasn't a compliance feature anyone bolted on — it's what an engine that persists context naturally produces.
Durable execution, because compliance takes weeks
Control assessments don't finish in one sitting. Evidence arrives late. An auditor has to sign off. A related control needs validating first. A chat session can't survive any of that; a context-oriented process is built for it.
FloMorphic snapshots the full execution state — current context, progress, node position, traversal history — and resumption is an execution primitive, not something you engineer:
Collect Evidence
↓
Await Auditor Review ── run parks, whole state persisted
↓
… three days …
↓
Restore + Continue ── resumes exactly where it stopped
That park on the Partially Covered branch is a Human-in-the-Loop node: the auditor arrives with the findings, the score, and the gaps already assembled on the context, supplies a disposition and a rationale, and closing the session resumes the parked run — which then writes the reviewed record. It's the same move as pausing a flow to consult a person only when their judgment actually adds value: approval is a state of the process, not a notification sent beside it.
Fractal: the runtime underneath
All of this runs on Fractal, a Go-based runtime that handles execution and orchestration. Deploy more Fractal instances and the platform distributes work across them, which is how it holds large numbers of concurrent, long-running assessments at once. That separation — process design, context, decision-making, and runtime execution as distinct layers — is exactly why a new unit of execution needs a runtime built for it. The web needed web servers; context-driven processes need Fractal.
What you actually built
Step back and look at what's now true of this assessor.
Evidence is gathered from real systems over MCP. The model's discretion is bounded to judging evidence per requirement — and nothing downstream trusts its prose: the coverage score is re-derived in code you can read, and the outcome is chosen by a deterministic rule on those code-derived signals. The thresholds are numbers a compliance owner can change without a deploy. The human step is a state of the process. Every run leaves a context document and a written record that answers why.
FloMorphic isn't a prompt-orchestration platform with extra features. It's prompt management, persistent context, intelligent routing, MCP, durable execution, reusable composition, and distributed runtime — one environment where the prompt is a node and the process is the product. The ISO 27001 example just makes it concrete: not compliance advice, but a compliance process that collects, decides, routes, persists, and endures.
Repo: github.com/FloMorphic/getting-started
Concepts and docs: inflowenger.com/flomorphic
New here? Start with The Model Proposes, the Graph Decides, see the mechanics in full in Build a Claims Adjudicator You Can Actually Audit, and the architecture behind all of it in Context-Oriented Software Development.