colectica-forsta-routing-toolkit
Extracts and cross-validates questionnaire routing logic from Colectica DDI and Forsta+ (Confirmit Horizons) exports — schema extraction, routing graphs, structural routing-diff, interview simulation, and a read-only MCP server over the results.
Documentation
Flowise Questionnaire System
A Django app for analyzing and simulating Colectica-format survey
questionnaire JSON files (e.g. Understanding Society Mainstage waves). It lets designers upload a
questionnaire module, extract its question schema and routing logic, build a visual flow graph,
run an AI-assisted advisory review via Flowise, and interactively
simulate walking through the questionnaire as a respondent.
It can also ingest a Forsta+ (Confirmit Horizons) XML export of the same questionnaire wave and
structurally compare its routing against the Colectica-derived routing, surfacing any
discrepancies — missing branches, unmatched conditions, Forsta+-only "else" branches — in a
dedicated routing-diff GUI, with a side-by-side graph view per discrepancy.
Core architectural principle
Django owns all data, routing logic, and validation. Flowise is advisory only.
Flowise (an external LLM agent platform) is used for two narrow purposes, and its output is
always validated/post-processed by Django before being trusted:
1. Module Review agentflow — reviews routing/coverage for design issues. Django sends a
compact payload and post-validates the response against known facts, rejecting anything that
invents question names or modifies routing.
2. Interview Wording agentflow — reformats respondent-facing question text/options during the
interview simulator. Django validates the response and falls back to a deterministic,
locally-built message if Flowise is unavailable or returns something invalid — the simulator
always works even if Flowise is down.
Processing pipeline
Enforced in the UI/views as a strict order per uploaded module:
1. Upload JSON → `QuestionnaireModule`
2. Extract schema (`schema_extractor.ColecticaSchemaExtractor`) → `NormalizedQuestion` rows
3. Extract routing (`routing_extractor.ColecticaRoutingExtractor`) → `RoutingEdge` rows
(conditional / sequential / loop)
4. Build graph (`graph_builder.py` + `graph_enrichment.py`) → `QuestionnaireGraph`
(nodes/edges JSON + Mermaid text)
5. Run Flowise review (optional) → `ModuleAIReview`
Everything runs synchronously in the request/response cycle — there is no Celery/async task
queue.
Colectica vs Forsta+ routing diff
A second, independent pipeline runs against a second `QuestionnaireModule`
(`source_format` auto-detected as `forsta_xml` from the `.xml` extension at upload — same upload
form as Colectica), cross-validating a fieldwork agency's Forsta+ (Confirmit Horizons) XML export
against the Colectica-derived routing for the same wave:
1. Upload Forsta+ XML → `QuestionnaireModule`
2. Extract schema (`forsta_xml_schema_extractor.ForstaXmlSchemaExtractor`) → `NormalizedQuestion` rows
3. Extract routing (`forsta_xml_routing_extractor.ForstaXmlRoutingExtractor`) → `RoutingEdge` rows
4. Match questions (`question_matcher.build_question_matches`) → `QuestionMatch` rows, pairing
each Colectica question with its best Forsta+ counterpart in three passes: exact
normalized-text match, then fuzzy fallback (`difflib`, 0.75 threshold), then a name-tiebreak
reconciliation step — if a question's current match has a different name than itself, and an
unused same-named question exists on the other side whose own wording *also* clears the fuzzy
threshold (or is a clean prefix/substring match — Forsta+ source text sometimes folds
interviewer instructions inline where Colectica keeps them separate), the same-named one takes
over. Name is a tiebreak, never an override: a same-named-but-unrelated-content "false friend"
is left alone.
5. Compare routing (`routing_comparator.compare_routing_for_modules`) → `RoutingDiscrepancy`
rows — a *structural* diff (edge target presence only, not condition semantics). Both the
source and target question of each edge are resolved through `QuestionMatch` before comparing,
not compared as raw name strings, so a target present on both sides under a different name
(casing, a Forsta+ suffix, etc.) isn't wrongly reported as missing.
Browsable at `/questionnaires/routing-diff///`, with a
per-discrepancy detail page rendering both systems' routing graphs side by side.
MCP tool server
Alongside the main Django app, `mcp_server` exposes a curated, read-only subset of the same data
as MCP (Model Context Protocol) tools over streamable-HTTP,
for MCP clients like Claude Desktop — its own Django app, its own standalone process, its own
port, never the main `runserver`.
python manage.py runmcp # own process, port 8765 by defaultEvery request must carry a per-person access token in its URL path
(`https:///t//mcp`) rather than a header, since MCP client connector UIs generally
only take a URL. Staff users generate/revoke tokens at `/questionnaires/mcp-tokens/` — no shared
secret, no terminal command needed to onboard a new person, and revoking one person's token
doesn't affect anyone else's.
Requesting access: there's no self-serve signup by design. Open an issue on this repository
or contact the maintainer to request a token.
Tools (`mcp_server/tools.py`), each with a matching MCP prompt (`mcp_server/prompts.py`)
that MCP clients can surface as a slash-command-style shortcut:
| Tool | Prompt | Notes |
|---|---|---|
| `list_modules` | `listModules` | Colectica-only |
| `get_module_summary` | `showModuleSummary` | Colectica-only |
| `list_questions` | `listQuestions` | Colectica-only |
| `get_question` | `showQuestion` | Colectica-only |
| `get_routing_edges` | `listRoutingEdges` | Colectica-only |
| `trace_variable` | `traceVariable` | Colectica-only |
| `get_module_graph` | `showModuleGraph` | Colectica + Forsta+; also returns Mermaid flowchart syntax so an MCP client can render an actual diagram; large modules are auto-summarized instead of returning a huge graph |
| `evaluate_edge_condition` | `evaluateCondition` | Colectica + Forsta+; evaluate any condition string against hypothetical answers |
| `get_routing_diff_report` | `showRoutingDiffReport` | Colectica + Forsta+ pair; mirrors the routing-diff report page |
| `get_routing_discrepancy_detail` | `showColecticaForstaDiscrepancy` | Colectica + Forsta+ pair; mirrors the per-discrepancy detail page, both systems' graphs included |
| `get_routing_simulation` | `showRoutingSimulation` | Colectica-only |
Never writes to the database and never triggers a compute-heavy pipeline step (extraction, graph
building, matching/comparison, AI review) itself — every tool reads data some other part of the
app already computed and persisted.
Tech stack
- Django 6.0 (`config/` project; two apps, `flowise_questionnaire` and `mcp_server`)
- PostgreSQL (`flowise_questionnaire_db`)
- Flowise (external, self-hosted or cloud) for advisory AI review/wording
- No frontend framework — server-rendered Django templates (routing graphs rendered client-side
via vis-network, loaded from a CDN)
Getting started
Prerequisites
- Python 3.12+
- PostgreSQL, with a `flowise_questionnaire_db` database available
- A running Flowise instance (optional — only needed for the AI review / interview wording
features; the rest of the app works without it)
Setup
git clone https://github.com/amiravarzamani/colectica-forsta-routing-toolkit.git
cd flowise-questionnaire-system
python3 -m venv venv
source venv/bin/activate # venv\Scripts\activate on Windows
pip install -r requirements.txtCopy `.env.example` to `.env` and fill in `SECRET_KEY`/`DB_PASSWORD`/`DB_HOST` — `config/settings.py`
has no defaults for these and will fail loudly at startup if they're missing. Adjust the
`DATABASES` and `FLOWISE_*` settings in `config/settings.py` to match your environment before
running migrations.
python manage.py migrate
python manage.py createsuperuser # first user, since login is required app-wide
python manage.py runserverThe app is mounted at `/questionnaires/` and requires login (`LOGIN_URL = /questionnaires/login/`).
Running tests
python manage.py testProject structure
config/ Django project settings, URLs, WSGI/ASGI
flowise_questionnaire/
models.py QuestionnaireModule, NormalizedQuestion, RoutingEdge,
QuestionnaireGraph, ModuleAIReview, InterviewSimulatorSession/Turn,
QuestionMatch, RoutingDiscrepancy
services/ Pipeline logic, in order:
schema_extractor.py parse questions out of the Colectica JSON
routing_extractor.py parse conditional/sequential/loop routing (Colectica)
forsta_xml_schema_extractor.py parse questions out of the Forsta+ XML
forsta_xml_routing_extractor.py parse conditional/sequential/loop routing (Forsta+)
graph_builder.py build the routing graph
graph_enrichment.py annotate the graph
condition_evaluator.py evaluate Colectica-syntax routing conditions against answers
forsta_condition_evaluator.py evaluate Forsta+-syntax routing conditions against answers
coverage_intent_builder.py generate deterministic test-case seed inputs
routing_simulator.py check routing coverage
question_matcher.py pair Colectica and Forsta+ questions (exact + fuzzy + name-tiebreak)
routing_comparator.py structural diff of matched questions' routing edges (source + target resolved via QuestionMatch)
routing_diff_explainer.py plain-language explanation text for the routing-diff GUI
agentflow_payload_builder.py build the Module Review Flowise payload
flowise_client.py send/receive the Module Review agentflow
interview_router.py deterministic routing engine for the simulator
interview_simulator_service.py orchestrate simulator sessions
answer_validation.py validate respondent A/B/C input
question_presentation.py convert questions to respondent-facing text
flowise_interview_wording.py Interview Wording agentflow client + caching/fallback
interview_simulator_contracts.py shared dataclasses
views/
module_views.py upload / extract / build-graph / review / graph
interview_simulator_views.py start / state / answer / abandon
routing_simulation_views.py
routing_diff_views.py Colectica-vs-Forsta+ report / run / discrepancy-detail
auth_views.py
mcp_server/
models.py McpAccessToken (per-person access token)
auth_middleware.py TokenAuthMiddleware -- validates /t//mcp on every request
tools.py the MCP tools (see "MCP tool server" above)
prompts.py matching MCP prompts (slash-command shortcuts)
server.py MCPServer instance, tool/prompt registration
views.py / urls.py staff-only token management UI (/questionnaires/mcp-tokens/)
management/commands/runmcp.py standalone streamable-HTTP server commandKnowledge graph (graphify)
The codebase can be explored via graphify, a tool that
turns the repo into a queryable knowledge graph (god nodes, community structure, cross-file
relationships) instead of relying on raw grep/browse. Output is written to `graphify-out/`
(gitignored — it's a regenerable local artifact, not committed source).
pip install graphifyy
graphify . # build the graph (AST + semantic extraction)
graphify query "" # BFS/DFS traversal, answers from the graph
graphify path "" "" # shortest path between two concepts/symbols
graphify explain "" # plain-language explanation of a node
graphify update . # incremental re-extract after code changes`graphify-out/graph.html` opens as a standalone interactive visualization; `GRAPH_REPORT.md`
is a plain-language audit of god nodes, surprising connections, and suggested questions.
Status / in-progress work
- `AgentRun`, `SyntheticProfile`, `SimulationRun`, `SimulationCase`, `ValidationIssue` models
are defined but not yet wired into any view.
- The Forsta+ (Confirmit Horizons) XML import and Colectica-vs-Forsta+ routing-diff pipeline
(see above) is implemented and in active use. See
`forsta_xml_routing_validation_plan.md` for the
original research doc and design rationale (it now also carries a "post-build" notes section
documenting where the real implementation diverged from the initial design).
- `RoutingDiscrepancy.DiscrepancyType.CONDITION_MISMATCH` is defined on the model (for a future
semantic/condition-evaluation diff, as opposed to the current structural diff) but not
currently produced by `routing_comparator.py` — reserved, not a bug.
- An `mcp_server` tool for the latest `ModuleAIReview` result is designed but not yet built —
intentionally on hold pending a separate go-ahead, not an oversight.
- `question_matcher.py` known limitation: two Colectica questions with byte-identical, generic
reused wording (e.g. a form-letter follow-up like "And in which town is that?" asked in more
than one routing context) can't be disambiguated by text similarity alone, so the wrong one can
win a match. The name-tiebreak doesn't help here since the questions' *names* don't collide,
only their text does. Not currently fixed — would need a different signal (e.g. routing-graph
position) than text similarity.
License
No license file yet — all rights reserved by default until one is added.
Frequently asked questions
What is colectica-forsta-routing-toolkit?
colectica-forsta-routing-toolkit is Extracts and cross-validates questionnaire routing logic from Colectica DDI and Forsta+ (Confirmit Horizons) exports — schema extraction, routing graphs, structural routing-diff, interview simulation, and a read-only MCP server over the results.
How do I install colectica-forsta-routing-toolkit?
Open the GitHub repository and follow its README. Most MCP servers are added to your client's MCP config, then called by your agent.
Is colectica-forsta-routing-toolkit open source?
Yes — it is hosted on GitHub at https://github.com/amiravarzamani/colectica-forsta-routing-toolkit and has 1 stars.
Related MCP tools
Cognee is the open-source AI memory platform for agents. Give your AI agents persistent long-term memory across sessions with a self-hosted knowledge graph engine.
Automate browser based workflows with AI
Hindsight: Agent Memory That Learns
A privacy-first app that strips AI watermarks from content you own.
Agent framework and applications built upon Qwen>=3.0, featuring Function Calling, MCP, Code Interpreter, RAG, Chrome extension, etc.
The power of Claude Code / GeminiCLI / CodexCLI + [Gemini / OpenAI / OpenRouter / Azure / Grok / Ollama / Custom Model / All Of The Above] working as one.
Run your own MCP server? See who uses it and what to fix.
Measure it with TrackMCP