Vancetope — Wonko Think Engine
Wonko is the Review-Loop-Engine: It examines an item — one or more documents — for meaning, contradictions, gaps, and vague passages, and clarifies it with the user in rounds until nothing remains to be clarified or the user has had enough. Optionally, it measures the item against reference documents (regulations, existing specs). It decides what its mandate allows and discloses it.
The loop is code; the model is only called at narrow, schema-bound edges (LightLlm), each with a fixed result, escape route, and mandatory reason. Codename after H2G2: Wonko the Sane, who declares the world insane and questions its meaning.
Design plan, decisions, and discarded paths:
planning/wonko-engine.md. Implementation:readme/wonko-engine.md.
1. Role and Classification
| Engine | operates on … | State | End |
|---|---|---|---|
frankie |
completed work | Tool-Loop in the model | natural / terminate / safety-net |
benjy |
completed work, small models | Task-Queue | Reflection-Gate |
zaphod |
synthesis of multiple views | Brains + Consensus | one answer |
wonko |
clarity | Checklist + Register of runs | no open question left / user stops / convergence |
Wonko does not write the solution. Its result is clarified findings and decisions. A Worker (§7) can incorporate them upon request.
Why an Engine and not a Recipe: The state is a register of rounds and runs (§4) — structurally different from Benjy’s Task-Queue, Marvin’s Task-Tree, or Vogon’s Phase-Strategy. Variants (other lenses, custom Skills, different mandate) are Recipes over the single Engine.
| Who | What | |
|---|---|---|
| External | Code | Checklist, Register, Mandate rules, Question selection, Convergence, Budget, Quote verification |
| Internal | LightLlm calls | check, decompose, select passages, compare, coverage, assign answers, classify messages |
2. Item and Standard
subject— what is being reviewed: one or more document paths. If the parameter is missing, Wonko takes the item from the conversation: the document bound in the Cortex or a path/ID in the message. Without an item, a chat run waits for the next message instead of blocking.references— what it is measured against: a folder (ending with/) or document paths. References are not reviewed, only used for comparison. Only via param/Recipe — from free text, subject and standard cannot be reliably distinguished.
Reading is exclusively done via permission-checked document tools
(doc_read, doc_list_in_folder); process_spawn starts the Worker (§7).
3. The Checklist
The state of a run is a queue of typed checkpoints
(engineParams.wonkoState.queue). The end is “no point and no question
left open”, not “no tool call left”.
| Type | When | Result (Core) | Escape |
|---|---|---|---|
CHECK |
without references — entire subject, one call |
Findings + category changes | empty list + reason |
DECOMPOSE |
with references |
verifiable statements with search terms (maxClaims) |
not_decomposable |
LOCATE |
per statement | Top passages from the candidate list, cross-references | none / cannot_determine |
COMPARE |
per statement × its passages | contradicts / satisfies / not_applicable + quote |
cannot_determine |
COVERAGE |
per reference document (can be disabled: coverage: false) |
missing requirements with quote | complete / cannot_determine |
start/Message → Subject
without references: CHECK
with references: DECOMPOSE → per statement LOCATE → COMPARE (parallel) → COVERAGE per reference
Work loop: process points → findings into the question pool
Pool ≥ maxQuestionsPerRound → send questionnaire, continue independent points
Queue empty → ask remaining pool or report
maxCalls reached → Checkpoint question "continue / stop"
Answer → Register; affected statements → COMPARE recheck only for these
Mechanics that are code:
- LOCATE in two stages. Code searches for paragraphs in the references
(
WonkoPassageSearch: paragraph with heading path; phrase in text before phrase in path before word stem,maxCandidates); the model only selects from this list and names cross-references (maxLocateDepthjumps). - Parallel comparisons. COMPARE calls run simultaneously
(
parallelism, virtual threads). Prompts are built on the Lane thread; only the model call runs in parallel, incorporation is sequential again. - Quote verification. A finding against a reference only counts if its
quote actually appears there (
WonkoQuotes: tolerant of whitespace/case/quotes/ellipsis, at least 12 characters). Otherwise, it is discarded — with a reason. - Budget.
maxCallsmodel calls per run; after that, a checkpoint question. The model never decides its own limits.
3.1 Output Contract
For every model edge:
- Fixed result schema instead of a tool for noting — the code reads it and enters it.
- Always an escape route (
cannot_determine,none,not_applicable,unclear). The model never has to invent something to fulfill the format.cannot_determinein a comparison is never silently a finding: the topic becomesUNCLEARand appears in the report, not as a question. - Every decision with a reason — including those of the code (mandate, discard, duplicate, Settled-Ast). Escape exits, discards, and errors end up in the report under “Decisions along the way”.
An answer whose evidence is in the same reference triggers a recheck
of an UNCLEAR statement; if the recheck reaches a judgment, the point
is resolved.
4. The Register
The state across rounds and runs, so that a later run on the same item knows what has been clarified.
- Storage in Memory:
MemoryKind.REVIEW, project-wide, one register per item (metadata.subjectKeyfrom the normalized refs). Each save writes a new entry and supersedes the old one — the chain is the history. Access only viaWonkoRegisterService(data sovereignty); project delete/rename is covered by the Memory handler. - Typed:
WonkoRegisterwith mandate, mandate log, and topics. - Topic instead of question. The concern is recorded under a
hierarchical key (
retry-policy/backoff) with an anchor (finding location) and type (contradiction·gap·vague·implausible·intent), weight (BLOCKER·IMPORTANT·MINOR) and evidence (reference + quote). The register is in the review prompt so that the same concern gets the same key. - Duplicate-Gate over the evidence. The key is model wording, the evidence is not: a finding with the same reference and overlapping quote as an existing topic or a finding in the pool is the same concern and is discarded (reason “duplicate” in the report). This prevents asking twice, just because a recheck names the matter differently.
| Status | Meaning | Asked again? |
|---|---|---|
OPEN |
found, not yet asked | yes |
ASKED |
in the open questionnaire | — |
ANSWERED |
answered | only with a new contradiction |
SETTLED |
“it’s fine as is” | no, no sub-topics either (closes the branch) |
DECIDED |
decided within the mandate itself | no — unless the user contradicts |
LATER |
“later” | not in the same run, again in the next |
UNKNOWN |
“I don’t know” | no — open in the report |
CONFLICT |
reappeared as a contradiction after a new answer | never a third time — in the report as a conflict |
UNCLEAR |
model could not determine it | no — in the report with reason |
Open topics from previous runs (OPEN/ASKED/LATER, not MINOR, not
under a SETTLED branch) are added to the question pool at the start — a
model call that doesn’t find them this time does not close them.
5. Mandate — what Wonko is allowed to decide itself
One level per category: decide (decides itself, justifies,
reports) · confirm (proposes, asks for a yes) · ask (open
question). Without a category, default applies (Recipe default: confirm).
mandate:
default: confirm
categories:
naming: { level: decide }
data-model: { level: ask, locked: true }
cosmetics: { level: confirm, ceiling: decide }
notes: "Prototype — performance does not matter"
locked— Wonko never changes the category.ceiling— up to what level Wonko can loosen itself.notes— context for every review prompt.
Self-decided never means silently decided: Every decide
decision is recorded with a reason in the register and the report. A user’s
contradiction reopens the topic.
5.1 Wonko maintains its categories — under code rules
The review call may propose category changes
(categoryChanges: create, describe, rename, split,
merge, level, reassign topic) — each with a reason. Whether they apply
is decided by WonkoMandateRules:
| Change | Result |
|---|---|
| create, describe, rename, split, merge | applied |
| tighten level | applied |
loosen level up to ceiling |
applied |
loosen beyond ceiling |
proposal — confirmation question to the user |
change locked category |
rejected |
| reassign topic to a looser category | counts as loosening — same rule |
| split | child inherits level, ceiling, and locked |
| merge different levels | the stricter applies |
The reassignment and inheritance rules prevent the workaround of self-empowerment
(“create a decide category, move an ask topic into it”). Every change
is recorded in the mandate log and the report. Mandate changes expressed by the
user in chat apply without these rules.
6. Questions: the Questionnaire
Wonko asks multiple questions at once, each with a separate,
structured answer — via the child questions:
- Outbound: the code generates a ` ```questions
-Fence from the state (QuestionsCodec.toFence`) — no model writes the syntax. The web displays it as a wizard, read-only once a subsequent user message follows. - Inbound: typed via
ProcessSteerRequest.questionAnswers(QuestionSetAnswerDto);contentonly carries the readable short form. The answering message carriesmeta.questionsAnswered = <set-id>. - Standard exits for each question: “it’s fine as is” (
SETTLED), “later” (LATER), “I don’t know” (UNKNOWN) — fixed buttons, no text to interpret. - Selection (
WonkoQuestionPlanner):MINORis not asked; blockers before important; at mostmaxQuestionsPerRound(default 3 — deliberately strict: the biggest risk is an annoying Wonko); nothing under aSETTLEDbranch. - Free text instead of questionnaire →
wonko-interpretassigns the answer to the questions (escapeunclear→ question remains open); “stop” ends the round.
7. Free Messages: Input, Work Loop, Worker
A turn has two parts: the input (receives every message, assigns
it, changes the state) and the work loop (processes the
checklist). Every message outside a questionnaire answer goes through
the router wonko-route (LightLlm, with the last chat turns, the
current text of the item, and the report):
| Intent | Effect |
|---|---|
recheck |
Subject changed / “check again” → new run, register remains |
statement |
new information → recheck with this information in mind |
question |
question about the review → answer from current state, no review run |
work |
task with tools (incorporate, write) → Worker |
worker |
answer to the waiting Worker or continuation of its task → unchanged into its Inbox (only offered as long as a Worker is waiting) |
unclear |
clarification question to the user |
If the router fails, the message goes to a waiting Worker
(its answer was the last thing said), otherwise it is considered a
statement. If an
answer requires knowledge it doesn’t have, it names a lookup — the
Discovery search (how-do-i) provides the Manual, the router
answers with it a second time.
Worker. Wonko’s own calls are single calls without a tool loop.
work therefore goes to a Worker (workRecipe, default frankie) with
the review result and the rule not to invent open items. The Worker
persists; further tasks go into its Inbox. Its answer appears in the chat;
if the item has changed (fingerprint), Wonko checks once.
Worker’s follow-up questions. A tool loop asks how it answers: It ends
its turn with an answer (REPLY) and remains IDLE — a
lifecycle event does not follow (BLOCKED is deliberately not reported by
ParentNotificationListener to the Parent). Therefore, the answer itself is the signal:
After that, the Worker waits, and the router receives its last answer
and the worker intent. “A” to “A or B?” goes unchanged to the
Worker — not as a new task (work would send the review result again)
and not as a statement (which would restart the review).
Whether the answer was a question or a result is decided by the router
on the next user message; a message for the review remains with Wonko.
Change with open questionnaire. If the Worker changes the item while a questionnaire is open, the recheck waits until the questionnaire is answered — Wonko states this in the chat. An immediate new run would silently discard the questionnaire, and the answers to it would be lost. First the answers (register), then the new run; if the review ends before (“stop”), the waiting recheck is canceled with it.
Follow-up work. “Review X and incorporate it”: Wonko remembers this
postscript and offers it at the end of the run as a confirmation question
(open blockers in the quote). Confirmed — ANSWERED without value (confirm button) or with
the proposal itself (this is how wonko-interpret writes an agreement) — it
goes into the input like a new message. Free text instead of confirmation is
not an agreement (“no, not now”): The offer is closed, the
text goes as a free message to the router, with the closed offer
in mind; only if the router reads an agreement in it, it becomes
work. Any other outcome discards the postscript.
8. Skills
Param skills (default ["@wonko"]): names, @label against the tags:
of the Skills, !-exclusions — the same grammar as relevantSkills
(skills). Additionally, Skills activated on the process
(/skill, START-Guard) are included. Composable Skills go into every
model call; unusable ones are listed once per run with a reason in the report.
Thus, Wonko becomes a compliance or spec review via Recipe, without code.
9. Lifecycle and End
- Start: Load or create register, resolve mandate from param/Recipe + register, take over open topics, queue first checkpoints.
- Progress: one TODO line “completed/total” per point type plus open questions via the existing TODO display; upon completion, the final status is sent, then the list is cleared.
- Status:
RUNNINGduring work,BLOCKEDas long as a questionnaire is open. - Convergence (
WonkoConvergence) — the run ends before another round of questions ifmaxRoundsis reached (max-rounds, with remaining list);- two rounds were answered predominantly “later” (
fatigue); - three rounds show no decrease in open topics (
no-progress).
- End: Queue and question pool empty, user stops, convergence or
budget stop → Report (
WonkoReportRenderer) with evidence and reasons, save register. As a Worker (with Parent), the process closes withDONE; in Chat, it remainsIDLE— a new message restarts the loop.
10. Recipes and Parameters
| Recipe | Type |
|---|---|
wonko |
Engine default, user-facing |
wonko-check, wonko-decompose, wonko-locate, wonko-compare, wonko-coverage, wonko-interpret, wonko-route |
internal LightLlm profiles — one model edge each |
| Param | Meaning | Default |
|---|---|---|
subject |
Document paths | from the conversation |
references |
Folder (…/) or document paths |
— |
mandate |
§5 | {default: confirm} |
skills |
§8 | ["@wonko"] |
workRecipe |
Worker for work |
frankie |
maxQuestionsPerRound |
Questions per questionnaire | 3 |
maxRounds |
Question rounds per run | 6 |
maxCalls |
Model calls until checkpoint | 300 |
parallelism |
Concurrent comparisons | 4 |
maxClaims / maxPassagesPerClaim / maxCandidates / maxLocateDepth |
Scope of reference review | 25 / 4 / 12 / 1 |
coverage |
Generate COVERAGE points | true |
*Recipe |
Replace internal profiles | see above |
11. What Wonko is NOT / v1-Limits
- Not a Fixer. Wonko does not change the item itself — only the Worker does so on explicit instruction.
- Not a One-Shot Review. The
code-reviewSkill provides a report; Wonko conducts a dialogue about intentions with mandate and memory. - Documents only.
codeanddesignitems are planned, not built. - Not yet built: Questionnaire display in Foot (fence as text, answer via free text), Inbox type
QUESTIONNAIREand channel dispatcher (questions to Parent/Inbox), topic gate for findings without evidence (withoutreferences, only the register prompt applies, no similarity matching), mandate as free text, Engine command/mandate, register export as Markdown.
12. Tests
- Unit:
vance-brain/src/test/java/de/mhus/vance/brain/wonko/*Test, plus the Codec/Mapper tests for the questionnaire (QuestionsCodecTest,QuestionsCodecParityTest,SteerMessageCodecQuestionAnswersTest,PendingMessageMapperTest; Web:questionsCodec.parity.test.ts). - E2E (
@Tag("llm"),./wb qa …):WonkoComplianceLlmTest— requirement against 14 artificial rules (relevant, irrelevant, one only reachable via cross-reference);WonkoReviewLlmTest— spec with built-in contradiction, typed answers, free text stop, second run on the same item. What the code guarantees is tested (Settled topics never reappear, register chain, report, verified quotes). - Unit focus: Mandate rules, convergence, question selection, duplicate gate, passage search, and quote verification.
13. References
- Framework: think-engines, recipes, light-llm-service
- Questionnaire: doc-kind-questions
- Related patterns: benjy-engine (Queue as state), wowbagger-engine (parallel single calls), frankie-engine (Worker)