- Faster prompt-to-result loop — no cognitive cost of guessing the right card.
- The scenario library grows without bloating the command widget. New
specialized scenarios (Vercel status, GitHub PR fix, Mintlify
publish, …) are reachable through the same Task input by adding an
examples:block to their YAML. - A reusable chain primitive — the classifier is the first user, but future composite scenarios (incident_response → aws_pulse → github_pr_fix) can chain through the same mechanism.
What’s changed
Backend
worker/worker/graph/scenarios/schemas.py—Scenario.examples: list[str]field added. Scenarios that opt in to the universal input declare 3–5 representative prompts that characterize their domain.worker/worker/graph/scenarios/orchestrator.py— three additions:- Tier-3 resolution rule:
metadata.kind == "task"with no explicitscenario_id/intent_routeroutes tointent_classifier. Programmatic callers that setscenario_id/intent_routeare unaffected. - Chain primitive —
ScenarioResult.chained_intent_routeplus a loop inrun()capped atMAX_CHAIN_DEPTH=3. When a level’soutput_parsed.chosen_intent_routeis set, the orchestrator re-dispatches the same job to the chosen scenario viametadata.scenario_id(tier-1 explicit pin). - Classifier-failure safety net — when the classifier itself aborts
or returns no usable route, the orchestrator chains to
quick_qaso the user gets a useful response rather than a routing fault.
- Tier-3 resolution rule:
worker/worker/graph/scenarios/runners.py— JSON response levels auto-populatelevel.output_parsed; the runner promoteschosen_intent_routefrom a level’s parsed output to top-level state so the orchestrator’s chain primitive picks it up.knowledge-hub/scenarios/intent_classifier.yaml— the new meta scenario. Two prompt levels:classify(free-tier JSON classifier call with a schema verifier) anddisambiguate(low-confidence fallback that asks the user viaask_userwith an enum schema). Gate threshold:level.classify.output_parsed.confidence >= 0.7.worker/worker/graph/scenarios/prompts.py— two new builders:intent_classifier_route(synthesizes the registry into a routing prompt) andintent_classifier_disambiguate(emits the ask_user envelope + final JSON).- All user-facing scenarios (
aws_pulse_report,code_build,document_writing,quick_qa,research_brief,security_review) backfilled withexamples:blocks. Admin-only scenarios (code_build_strict,parallel_review_demo) intentionally omit examples so they stay out of the classifier catalog. quick_qa.yaml—intent_routes:shrank from[task, qa]to[qa]. Thetaskroute is reserved for the tier-3 classifier rule; power users who want quick_qa directly should setscenario_id=quick_qaorintent_route=qa.
Frontend
apps/frontend/components/layout/footer/ask-user-enum-panel.tsx— new component. Detects enum-shapedask_userschemas ({type: object, properties: {answer: {enum: [...], x-labels: {...}}}}) and renders one button per option. Click sends the chosen value via the existing resume path. (Update 2026-06-03: this disambiguation panel is now rendered by theUniversalFeedbackPanelvia thex-feedbackcontract —intent_classifier_disambiguatedual-emits both shapes. See the Universal User-Feedback System.)apps/frontend/components/workflow/timeline/event-timeline.tsx— the existingClarificationPromptnow dispatches toAskUserEnumPanelfor enum schemas and falls back to the textarea otherwise.apps/frontend/components/layout/footer/command-widget.tsx— the AWS Pulse card was removed (it was a temporary workaround introduced before the classifier landed). The Task card’s description now reads “Free-text prompt; Cerebrum routes it to the right scenario automatically”.
Follow-up prompts are classified per-prompt (2026-06-05)
The classifier originally only ran for the first prompt of a workflow. Follow-up prompts appended to a workflow that was seeded by theproject command inherited that seed’s metadata.intent_route="project"
(and kind="project") via merge_workflow_metadata, so the worker matched
the tier-2 intent_route rule and pinned every follow-up to
code_build — running the full paid L1→L4 pipeline even for a pure
question. The tier-3 classifier path was never reached.
mother-ai/src/routes.rs— newreset_routing_keys_for_appendhelper, applied inappend_prompt(both the Postgres and no-Postgres branches). Before queuing an appended prompt it drops the inheritedintent_routeandscenario_idand forcesmetadata.kind="task", so each follow-up is re-classified on its own content. The workflow’s project binding (the nestedprojectobject,project_slug, model/effort hints) is preserved untouched, so a follow-up the classifier routes back tocode_buildstill targets the right repo.
quick_qa) → request a security
review (security_review) → keep coding (code_build, same repo). The
_dispatch_chain re-dispatch carries the project object through every hop,
so repo targeting survives classification.
Because the project binding is preserved on follow-ups, a non-code scenario
(quick_qa, research_brief, security_review, document_writing) now runs
with a project workspace present. The thread runner gates its legacy
markdown-apply + build-verify pipeline on whether the resolved scenario owns
the workspace (scenario_workspace_owned, i.e. has a tool_loop level). A
scenario that ran but does not own the workspace skips that pipeline entirely
— so a Q&A follow-up no longer parses its answer as L4 file blocks or emits a
spurious build_verify_started / no-files-repair. See
worker/worker/orchestration/thread_runner.py (the scenario_non_mutating
branch in run()). Genuine code scenarios use tool_loop and keep their own
l3_build_verify verdict; legacy non-scenario runs (no scenario_id in state)
are unchanged.
Boundary: this re-routes classification, not project origination. A
workflow seeded by a plain task/QA prompt has no project object, so a
mid-workflow “start a coding project” follow-up routes to code_build but
runs without a repo/branch/Vercel binding (ProjectWorkspaceManager.prepare
returns None). Originating a repo-backed project remains the job of the
dedicated project command, which collects the structured fields the append
composer does not.
Plumbing cleanup
mother-ai/src/routes.rs—aws_pulseremoved fromSUPPORTEDand the enqueue match. The AWS Pulse scenario is still loaded by the worker; it’s now reachable only through the Task input + classifier.worker/worker/main.py—orchestration_kindsshrank from{task, project, aws_pulse}to{task, project}.worker/worker/models.py—aws_pulsedropped from theJobEnvelope.kindLiteral.apps/frontend/lib/mother-ai-client.ts—aws_pulsedropped fromCommandIntentKind.
Impact scope
- Backwards-compatible. Jobs that arrive with an explicit
metadata.scenario_idormetadata.intent_routebypass the classifier entirely. Programmatic callers (replay harness, admin tooling, internal automation) stay unaffected. - Off by default for non-task kinds. The tier-3 rule only fires
for
metadata.kind == "task". The firstkind=projectprompt keeps its route tocode_build; follow-up prompts are reset tokind=taskon append and re-classified per-prompt (see “Follow-up prompts are classified per-prompt” above). Other non-LLM kinds (rule,fact,planner,metric) bypass the orchestrator entirely. - One-time scenario library audit. Every user-facing scenario
needs an
examples:block to be discoverable through the universal input. The bundled six were backfilled at ship; new scenarios should add examples as part of the same PR that adds the scenario. - Chain depth capped at 3. The classifier counts as one hop, so composable follow-ups (classifier → real scenario → optional follow-on) have two more slots before the cap fires.
Tests
tests/test_scenario_chain.py— chain hop, MAX_CHAIN_DEPTH cap, unknown-target fallback to quick_qa, metadata propagation, andtest_chain_preserves_project_binding(theprojectobject survives classification → chain so a code follow-up still targets the repo).tests/test_intent_classifier_load.py— YAML parses, prompt refs resolve, scenario compiles withwhen:expression, classifier catalog has the right opt-in shape.tests/test_intent_classifier_scenario.py— end-to-end with scripted FakeProviders: high-confidence path (no disambiguate), low-confidence path (mocked ask_user resume → chain), schema-fail retry recovery.tests/test_scenario_dispatch.py—taskis no longer in the route index, plustest_appended_followup_with_project_binding_reclassifies(a{kind:"task", project:{…}}follow-up with nointent_routeresolves tointent_classifier, notcode_build).mother-ai(cargo test) —reset_routing_keys_for_appendunit tests: stripsintent_route/scenario_id, forceskind="task", preservesproject; composes withmerge_workflow_metadata; passes through non-object metadata.tests/test_thread_runner.py—NonMutatingScenarioInProjectTests: a non-workspace-owning scenario (quick_qa) in a project-bound run emits nobuild_verify_*events, parses no L4 file blocks, and skips the empty push; a workspace-owning scenario (code_build) still takes the carry-through path.
test_provider_registry.py::test_paid_provider_fallback_chain and
test_security_review.py::SecurityReviewEndToEndTests::*.