4+1 Process View¶
The Process View captures what FCC does at runtime. Where the Logical View focuses on static class structure, this page traces control and message flow: how a workflow step becomes an LLM call, how external tools invoke FCC via MCP, how two agents negotiate A2A capabilities, and how the compliance auditor runs as an out-of-band job. Every diagram on this page is a live sequence — reading them top- to-bottom shows a single execution trace.
Four diagrams follow: the Find to Create to Critique cycle, the MCP inbound call, an A2A agent card exchange, and a compliance audit run.
Find, Create, Critique cycle¶
The spine of the framework. SimulationEngine.run(start_node, payload)
at src/fcc/simulation/engine.py:50 walks a WorkflowGraph one node at
a time. For each node it resolves the persona and action, builds a
prompt via the ActionEngine, calls the configured AIClient,
validates the output, records a turn, and publishes one
workflow.step event on the EventBus. A single workflow.completed
event fires at the end.
Figure 1 traces a single end-to-end workflow run from the caller's perspective.
sequenceDiagram
participant Caller
participant SimulationEngine
participant PersonaRegistry
participant ActionEngine
participant AIClient
participant EventBus
participant Observability
Caller->>SimulationEngine: run(start_node, payload)
loop for each node in graph
SimulationEngine->>PersonaRegistry: by_id(persona_id)
PersonaRegistry-->>SimulationEngine: PersonaSpec
SimulationEngine->>ActionEngine: get_action_prompt(persona, action)
ActionEngine-->>SimulationEngine: {system, user}
SimulationEngine->>AIClient: complete(system, user)
AIClient-->>SimulationEngine: raw_output
SimulationEngine->>EventBus: publish(workflow.step)
EventBus-->>Observability: span emit
end
SimulationEngine->>EventBus: publish(workflow.completed)
SimulationEngine-->>Caller: MessageHistory
MCP inbound persona lookup¶
When an external tool (a CrewAI agent, an IDE extension, etc.) invokes
FCC over MCP, the MCPServer at src/fcc/protocols/mcp/server.py
receives a JSON-RPC request, resolves the tool name to a handler, and
returns a structured result. The stream_manager multiplexes streaming
responses for long-running tool calls.
Figure 2 traces an MCP persona_lookup call end-to-end.
@startuml
actor "External Tool" as ET
participant "MCP Server" as MCP
participant "ToolRegistry" as TR
participant "PersonaRegistry" as PR
participant "StreamManager" as SM
ET -> MCP : JSON-RPC persona_lookup(id)
MCP -> TR : resolve("persona_lookup")
TR --> MCP : handler
MCP -> PR : by_id(id)
PR --> MCP : PersonaSpec
MCP -> SM : open_stream(request_id)
MCP --> ET : initial ack
MCP -> SM : send(persona_card)
SM --> ET : chunk
MCP -> SM : close(request_id)
SM --> ET : end
@enduml
The MCP bridge is thin by design: handlers call the same Python APIs the CLI would, so there is no duplicate business logic.
A2A agent card exchange¶
Two FCC personas running in different processes can negotiate
capabilities via the A2A protocol. The A2AServer at
src/fcc/protocols/a2a/server.py exposes an agent card that declares
skills; the client persona reads the card, finds a compatible skill,
and invokes it.
Figure 3 shows a capability negotiation between a Research Crafter (RC) and a Blueprint Crafter Champion (BCHM).
sequenceDiagram
participant RC as Research Crafter (client)
participant BCHM as Blueprint Crafter Champion (server)
participant CardStore as AgentCardStore
RC->>BCHM: GET /.well-known/agent.json
BCHM->>CardStore: load_card()
CardStore-->>BCHM: AgentCard(skills)
BCHM-->>RC: AgentCard
RC->>RC: match(needed_skill, card.skills)
RC->>BCHM: POST /skill/blueprint_draft
BCHM->>BCHM: validate_against_riscear()
BCHM-->>RC: SkillResult
RC->>RC: record_collaboration_edge()
The collaboration edge that RC records feeds back into the
cross-reference matrix at src/fcc/personas/cross_reference.py.
Compliance audit run¶
ComplianceAuditor.audit() is a batch job: given a PersonaRegistry,
it enumerates every persona, classifies each against the EU AI Act risk
tiers, runs all applicable quality gates, and assembles a
ComplianceReport. The CompliancePipeline wraps the audit with event
emission so dashboards and subscribers can react.
Figure 4 is a PlantUML activity diagram of one audit pass.
@startuml
start
:Load PersonaRegistry;
:Load RequirementRegistry (256+ EU AI Act, 29 NIST);
:Load 58 QualityGates;
while (more personas?) is (yes)
:Pick next persona;
:AIActClassifier.classify(persona);
:Select applicable requirements;
while (more gates?) is (yes)
:Evaluate gate;
if (pass?) then (yes)
:record pass;
else (no)
:record AuditFinding;
endif
endwhile
:emit compliance.persona_audited;
endwhile (no)
:Assemble ComplianceReport;
:Build EvidenceGraph;
:emit compliance.audit_completed;
stop
@enduml
How the async story fits together¶
The simulation engine is synchronous inside its loop; the
concurrency seam is the event bus. EventBus.publish at
src/fcc/messaging/bus.py:102 enqueues onto a bounded
queue.Queue and returns immediately. Subscribers drain the queue on a
worker thread pool; each subscriber has its own dead-letter queue
(src/fcc/messaging/dlq.py) so a slow or failing subscriber cannot
back-pressure the publisher. The MCP and A2A servers run on separate
event loops and interact with the engine only through published events
and direct Python calls — they never reach into engine state. The
compliance pipeline runs detached from any live simulation, but its
events land on the same bus so the dashboard can surface audit
progress alongside live workflow steps.
That separation is the process-view invariant: the workflow critical path is synchronous and deterministic, while everything else (observability, protocol bridges, compliance reactions) rides the bus asynchronously.
See also¶
src/fcc/simulation/engine.py:50—SimulationEngine.runsrc/fcc/workflow/action_engine.py:51—ActionEngine.runsrc/fcc/messaging/bus.py:102—EventBus.publishsrc/fcc/protocols/mcp/server.py— MCP handler dispatchsrc/fcc/protocols/a2a/server.py— A2A agent card serversrc/fcc/compliance/pipeline.py—CompliancePipeline- Sequence diagrams deep-dive
v1.5.0 Process Impact¶
v1.5.0 introduced three new runtime flows that extend the Process View's async spine. The workflow critical path is still synchronous and deterministic; the new flows ride the lane-routed event bus as additional asynchronous rails.
GraphRAG retrieval pipeline (Pillar B). GraphRAG.query at
src/fcc/rag/graphrag.py:166 sequences six steps — seed retrieval, BFS
expansion, Zachman filter, persona-aware scoring, LYRA augmentation,
context assembly — into one deterministic pipeline. Each step is
synchronous; LYRA augmentation tolerates a raised exception and
degrades to KG-only. Full trace: ../sequence-diagrams/graphrag-zachman-filter.md.
CRDT convergence loop (Pillar D). The MultiUserSession edit path
is non-blocking: local edits fan out through a broadcaster callback,
remote edits arrive via on_remote_edit(update), and the CRDT backend
converges by Lamport + replica-id LWW (in-memory) or full Yjs semantics
(YPy). No coordination primitive gates the merge. Presence and session
events ride the UX lane; turn recording rides the PAOM lane. Full trace:
../sequence-diagrams/crdt-multi-user-merge.md.
Dual-bus event routing (ADR-006). The LaneRouter at
src/fcc/messaging/lanes.py:132 replaces the single EventBus entry
point: publish(event) classifies the event against
event_lane_map.yaml (85 classifications) and dispatches to PAOMBus
(orchestration), UXBus (live-view), or both. Subscribers register per
lane; unknown events fall back to PAOM with a RuntimeWarning. Full
flow: ../data-flow-diagrams/dual-bus-lane-routing.md.
Bidirectional federation sync (Pillar E). BidirectionalSync runs
as a batch job against a NamespaceRegistry; conflicts reconcile per
the target's ConflictPolicy and manual merges queue until a human
resolves them. Full trace: ../sequence-diagrams/bidirectional-federation-sync.md.
The process-view invariant holds: workflow on the critical path, everything else on the bus. The new flows are additive.
v1.6.0 Impact¶
v1.6.0 executes three of the pillars that v1.5.0 introduced. Each pillar adds a new runtime flow; all three preserve the v1.5.0 invariant (workflow synchronous, everything else either offline batch or async-bus).
Pillar A fetch + manifest-build workflow. The owner guards the
canonical-seed fetch behind the FCC_ACCEPT_EXTERNAL_LICENSES=1 env
flag. publications/scripts/fetch_canonical_seeds.py is a one-shot
offline job: it iterates the per-vertical CanonicalSource entries
from seed_canonical.py, issues an HTTPS request with the bootstrap
User-Agent + 30-second timeout, writes each file under
publications/_output/seed_canonical/<vertical>/, computes SHA-256 + a
25 MB cap per file, and appends a FileResult entry to
seed_canonical_manifest.json. When the endpoint refuses scrape or
redirects to an auth wall, the fetcher writes a smoke subset and
flags substituted: true in the manifest. The job emits no events —
it is a build-time artifact producer, not a runtime flow — and its
output is committed into the tree as the authoritative corpus
manifest. Full trace: ../pillar-a-seed-lifecycle-v160.md.
Pillar C translation + glossary-preservation workflow. The
translation flow is LLM-assisted with a deterministic post-check. A
translator sub-agent reads the English baseline
(frontend/src/i18n/locales/en/common.json plus the companion
docs/i18n/ tree), emits a locale JSON, and hands off to the glossary
enforcer. scripts/verify_glossary_preservation.py loads
docs/i18n/glossary.csv, collects every row with
do_not_translate=true (R.I.S.C.E.A.R., Zachman row/column names,
persona IDs, canonical acronyms), and asserts that every
do-not-translate term that appears in the English baseline also appears
verbatim in the target locale. A single mismatch fails the release
gate. Full trace: ../pillar-c-translation-flow-v160.md.
LYRA live bridge probe + fallback sequence. get_lyra_bridge() at
src/fcc/knowledge/lyra_bridge.py:346 calls lyra_available() (which
caches the result of import lyra.api), and on True constructs
lyra.api.LyraAdapter(). On construction failure the accessor falls
back to LyraMockBridge(_build_seed_graph()) and logs at DEBUG; on
False it returns LyraMockBridge directly. The probe is synchronous
and happens per-call, so a v1.6.x environment where the operator
enables lyra.api partway through a process lifecycle picks up the
live adapter on the next call without a restart (the sentinel can be
reset via _reset_lyra_sentinel() for tests). Full trace:
../lyra-live-integration-v160.md.
Preview-namespace removal. The fcc.plugins.v1_5_preview.* import
paths are removed in v1.6.0 — any runtime that still imports them
raises ModuleNotFoundError at interpreter startup rather than a
DeprecationWarning. This is a process-view observation because it
is the only v1.6.0 change that can break a running process; operators
must migrate their shims to fcc.knowledge.lyra_bridge /
fcc.archive.polaris_bridge / the flat fcc.api namespace before
upgrading.
The three flows together validate the v1.5.0 process-view invariant: the workflow critical path is untouched, Pillar A is an offline build-time job, Pillar C is a CI-gate + translator sub-agent loop, and the LYRA probe is a pure per-call resolution that degrades safely when the backend is absent.