LoopFlow adapter demo¶
Use this page as presenter notes for the LoopFlow owner. The short version: LoopFlow stays the control language; ctx becomes a permissioned recommendation sidecar that tells the loop which skills, agents, MCP tools, and user-owned LLM harnesses are worth loading before the next plan.
30-second pitch¶
LoopFlow already owns the loop: goal:, look at:, plan, act, observe,
reflect, and done when. ctx should not replace that. ctx should run before
planning, read the same goal, verification checks, and failure context, and
return a read-only JSON contract:
- what the loop is allowed to load;
- which skills, agents, MCP servers, and harnesses fit the current goal;
- which related recommendations fit after the loop accepts or rejects part of a previous bundle;
- the
ctx-mcp-servercommand and tool list for MCP-aware runners; - a dry-run harness install command only when the user declares their own local/API model.
Demo loop¶
loop "select ctx capabilities":
goal: mcp agent loop local ollama filesystem
look at: the repo plan, AGENTS.md, and the last failure
done when "pytest src/tests/test_loopflow_adapter.py -q" passes
ctx grants: skills, mcps, harnesses
each cycle: plan, then act, then observe
when it fails: reflect, then plan again
Demo command¶
Run ctx immediately before LoopFlow plans:
python -m ctx.adapters.loopflow \
--goal "mcp agent loop local ollama filesystem" \
--loop-name "ctx capability selection" \
--permissions skills,agents,mcps,harnesses \
--own-llm \
--model-provider ollama \
--model ollama/llama3.1 \
--selected local-ollama-file-operations \
--rejected legacy-reviewer \
--top-k 2
The same call can read a real .loop file:
python -m ctx.adapters.loopflow \
--loop-file select-capabilities.loop \
--permissions skills,agents,mcps,harnesses \
--own-llm \
--model-provider ollama \
--model ollama/llama3.1
Add --last-failure-file .loopflow/last-failure.txt after LoopFlow has written
that file.
Example payload¶
This excerpt is from the live adapter against the current ctx catalog. Exact recommendation names can change as the graph changes, but the contract shape is stable.
{
"version": "ctx.loop_adapter.v1",
"adapter": "loopflow",
"permissions": {
"skills": true,
"agents": true,
"mcps": true,
"harnesses": true
},
"loopflow": {
"before_plan": "Call python -m ctx.adapters.loopflow before planning and inject this JSON as read-only context.",
"use_tools": "use tools from the \"ctx\" server",
"use_skills": "use skills: oocx-tfplan2md-agent-model-selection",
"harness_rule": "Only load harnesses when the loop runs on a user-owned/API/local LLM."
},
"mcp_server": {
"name": "ctx",
"command": "ctx-mcp-server",
"args": [],
"tools": [
"ctx__recommend_bundle",
"ctx__graph_query",
"ctx__recommend_related",
"ctx__wiki_search",
"ctx__wiki_get",
"ctx__observe_dev_event",
"ctx__load_entity",
"ctx__mark_entity_used",
"ctx__record_validation",
"ctx__record_escalation",
"ctx__unload_entity",
"ctx__session_end",
"ctx__session_state"
]
},
"capabilities": {
"skills": [
{
"name": "oocx-tfplan2md-agent-model-selection",
"type": "skill",
"status": "installed",
"installable": true,
"load_status": "local-wiki",
"source_path": "converted/oocx-tfplan2md-agent-model-selection/SKILL.md"
},
{
"name": "nickcrew-claude-ctx-plugin-tool-selection",
"type": "skill",
"status": "available",
"source_catalog": "skill-index",
"install_command": "python -m ctx.adapters.claude_code.install.skill_install nickcrew-claude-ctx-plugin-tool-selection",
"installable": false,
"load_status": "external-install-required",
"source_path": "python -m ctx.adapters.claude_code.install.skill_install nickcrew-claude-ctx-plugin-tool-selection"
}
],
"agents": [
{"name": "oss-investigator-local-git-agent", "type": "agent"},
{"name": "loop-operator", "type": "agent"}
],
"mcps": [
{"name": "local-ollama-file-operations", "type": "mcp-server"},
{"name": "multi-model-advisor-ollama", "type": "mcp-server"}
],
"harnesses": [
{"name": "autogen", "type": "harness", "fit_score": 1.0},
{"name": "langfuse", "type": "harness", "fit_score": 1.0}
]
},
"related_recommendations": [
{
"id": "mcp-server:ollama",
"name": "ollama",
"type": "mcp-server",
"tldr": "mcp-server recommendation.",
"reason": "related via local-ollama-file-operations; normalized score 1.000",
"installable": true,
"load_status": "local-wiki",
"source_path": "entities/mcp-servers/ol/ollama.md",
"selected": false,
"selection_state": "suggested_related"
}
],
"agent_loop": {
"before_act": "Load only the granted capability groups from capabilities.*.",
"on_failure": "Pass the latest failure back as last_failure before the next plan.",
"harness_install": "python -m harness_install --dry-run '--goal=mcp agent loop local ollama filesystem' --model-provider=ollama --model=ollama/llama3.1 -- autogen"
},
"warnings": []
}
How LoopFlow would consume it¶
The smallest integration is a pre-plan hook:
from ctx.adapters.loopflow import recommend_for_loop
ctx_payload = recommend_for_loop(
goal=loop.goal,
loop_name=loop.name,
loop_kind="loopflow",
look_at=loop.look_at,
done_when=loop.done_when,
last_failure=loop.last_failure_text,
selected=loop.selected_ctx_ids,
rejected=loop.rejected_ctx_ids,
permissions={"skills", "agents", "mcps", "harnesses"},
own_llm=runner.uses_user_owned_model,
model_provider=runner.model_provider,
model=runner.model,
)
loop.add_readonly_context("ctx", ctx_payload)
if ctx_payload["mcp_server"]["command"]:
runner.register_mcp_server(
name=ctx_payload["mcp_server"]["name"],
command=ctx_payload["mcp_server"]["command"],
args=ctx_payload["mcp_server"]["args"],
)
if ctx_payload["loopflow"]["use_skills"]:
loop.add_planning_hint(ctx_payload["loopflow"]["use_skills"])
for row in ctx_payload["related_recommendations"]:
loop.add_planning_hint(f"consider related ctx recommendation: {row['id']}")
After a failed observe step, LoopFlow passes the new failure text back into the next adapter call. That is the agent-loop back edge: LoopFlow keeps the retry logic, and ctx refreshes recommendations based on what just failed.
For a long-lived runner that executes accepted recommendations, share one
ActivationLeaseRegistry across all nested or parallel loops:
from ctx.adapters.loopflow import ActivationLeaseRegistry
leases = ActivationLeaseRegistry()
def apply_ctx_actions(actions):
runner.load_all(actions.load)
runner.mark_used(actions.use)
runner.unload_all(actions.unload)
lease_id = f"{run.id}.{loop.id}.{loop.invocation_id}"
desired = loop.accepted_ctx_ids
with leases.lease(
lease_id,
desired=desired,
used=lambda: loop.used_ctx_ids,
permissions=loop.ctx_permissions,
apply=apply_ctx_actions,
):
loop.run()
The registry serializes transitions inside one host process. It commits
ownership only after apply_ctx_actions returns, so failed loads and unloads
remain retryable, and it unloads shared context only after the final owning
loop exits. Host load/unload operations must be idempotent because a host may
partially apply an action set before raising.
Each live context-manager lease ID must be unique; include the loop invocation
or attempt ID, not only the static loop name. Pass a used supplier so the
registry reads observed use after execution, including failure and cancellation
paths, rather than recording planned use before the loop starts.
The applier must be synchronous and must not call the registry or wait for a
worker that calls it. Such callback re-entry fails fast with
ActivationLeaseBusyError so it cannot deadlock all loop transitions. The
context manager waits for unrelated transitions automatically; direct
sync(...) or release(...) calls may either retry that error or explicitly
pass wait_for_transition=True when they are outside an applier.
The subprocess CLI is recommendation-only and does not coordinate activation
leases across processes. Recommendation feedback is stateless unless the runner
supplies --session-id; with that opt-in, canonical rejected IDs persist in the
local lifecycle store according to --rejection-mode (use, replace, or
ignore). This memory suppresses repeated recommendations only. It does not
create or share activation leases. A runner that needs live load/use/unload
behavior must use the direct Python integration above (or own a persistent
coordinator) and keep LoopFlow in control of physical tool changes.
Permission model¶
The adapter fails closed:
--permissions skillsonly returns skill recommendations.- Goals that imply local files,
feature_implementation, no API keys, or a clear programming language overfetch and then hide non-local, credentialed, generic-planning, or wrong-language capability and related rows. --permissions mcpsreturns MCP server recommendations and exposes only read-only ctx MCP tools: recommendation, graph query, and wiki lookup.- Lifecycle MCP tools such as load, mark-used, validation, escalation, unload,
and session tools are exposed only when all capability groups are granted:
skills,agents,mcps, andharnesses. --permissions harnessesreturns no harnesses unless--own-llmis present.--model-providerand--modelimprove ranking and dry-run command metadata; they are not user-owned model consent.agent_loop.harness_installis always a--dry-runcommand. It shows what would be installed; it does not mutate the host.
.loop files can request ctx grants with one small line:
CLI/API grants take precedence. If the command passes --permissions, those
grants replace the .loop line. If the API caller passes permissions=...,
that explicit set is authoritative. If neither is present, the .loop grants
are used; if none are present anywhere, the adapter returns no capabilities.
This lets a LoopFlow user decide whether a loop may use skills, agents, MCPs, or harnesses without giving ctx authority to bypass the loop's own gates.
Owner ask¶
The integration proposal for LoopFlow is intentionally small:
- Add an optional
ctxpre-plan hook in the LoopFlow runner. - Let
.loopusers grant capability groups:skills,agents,mcps, andharnesses. - Inject the returned JSON as read-only context before planning.
- Register the ctx MCP server only when the adapter returns an MCP command.
- Surface
agent_loop.harness_installas an explicit user action, not an automatic install.
That gives LoopFlow the ctx graph, llm-wiki, MCP server, skill recommender, and
user-owned model harness recommendations while preserving LoopFlow's language,
human gates, and done when verification model.