feat(intake): add multi-turn project intake with deterministic gate - #545
Open
andresnunes1404 wants to merge 14 commits into
Open
feat(intake): add multi-turn project intake with deterministic gate#545andresnunes1404 wants to merge 14 commits into
andresnunes1404 wants to merge 14 commits into
Conversation
Body background, grid-bg opacity, and vignette darkening were tuned too dark to be legible; lighten them so the grid and navy background show through. Also fix REGISTO DO SISTEMA log ordering: column-reverse combined with insertBefore(firstChild) was pushing the newest line to the bottom instead of the top.
…cise full inference pipeline The warmup was sending an empty /api/generate request, which loads the model into memory but does not trigger the full chat-inference pipeline (JIT compilation, KV-cache allocation). The first real /api/chat call with a large prompt (e.g. intent judge system prompt + transcript) could therefore exceed even generous timeouts on laptop GPUs. Switch to a minimal /api/chat with a system + user message pair and num_predict=1, matching the approach already used by OpenAICompatibleBackend.warm_up(). Closes isair#537
… to exercise full inference pipeline" This reverts commit ce9e198.
Backs the multi-turn project intake tool's state machine (awaiting_type -> in_progress -> completed) with a dedicated table, following the same CREATE TABLE IF NOT EXISTS pattern as meals. Co-Authored-By: Claude Sonnet 5 <[email protected]>
…ettings Also surfaces both in the auto-generated settings UI and extends the shared MockConfig test doubles used by tests/ and evals/. Co-Authored-By: Claude Sonnet 5 <[email protected]>
projectIntake runs the deterministic, multi-turn interview (template matching, one question per turn, abandon phrase, Obsidian brief write on completion). startProjectDevelopment is the separate, later single-shot trigger that resolves a saved plan note, reads it, and hands it off to Antigravity via MCP. Both are registered in the builtin tool catalogue. Co-Authored-By: Claude Sonnet 5 <[email protected]>
Runs right after redaction, before recent-dialogue lookup, MCP refresh, the tool router, and the planner: an active intake session forces the projectIntake tool call and skips everything else for the turn. Fail-open on any DB error. Also ends the agentic loop immediately after any projectIntake/startProjectDevelopment call (both call sites: model-emitted and planner direct-exec) so the chat model can never re-invoke the tool with fabricated input and silently advance multi-turn intake state more than once per turn. Co-Authored-By: Claude Sonnet 5 <[email protected]>
Registers project_intake.spec.md in CLAUDE.md's Spec File Registry, adds the "3c. Project Intake Gate" entry (and Flow-diagram step) to docs/llm_contexts.md as the first non-LLM pre-planner gate to also call an MCP server directly from tool code, and mentions the new capability in README's Built-in Tools list. Co-Authored-By: Claude Sonnet 5 <[email protected]>
Covers the one point in the flow where tool selection genuinely depends on the LLM router/planner rather than the deterministic gate: the initial trigger phrase must be routed to projectIntake and the reply must be exactly the type question. Also asserts the follow-up turn is gated deterministically rather than relying on the model to pick the tool again on its own. Co-Authored-By: Claude Sonnet 5 <[email protected]>
andresnunes1404
force-pushed
the
feat/project-intake
branch
from
July 22, 2026 16:18
4cdf3a9 to
0d59d4f
Compare
Saying the start-a-new-project phrase again while a projectIntake session is already awaiting_type/in_progress was being silently swallowed as free-text input to the current question. Detect it with the same substring/normalisation approach used for the abandon phrase and ask the user to explicitly finish or abandon first, without touching current_index/answers_json.
A session with no explicit abandon phrase and no activity stayed in_progress/awaiting_type forever, hijacking every future turn via the gate — even across app restarts, since it's a DB row. get_gated_session now checks updated_at against the new project_intake_stale_minutes config key (default 30) and marks a stale session completed/abandoned, letting the turn fall through to normal routing.
Two paths could crash a turn permanently: the awaiting_type -> in_progress transition's context.db.update_intake_session() call had no try/except (unlike the sibling in_progress path), and json.loads() on questions_json/answers_json had none either — a malformed value crashed the turn, and since the gate forces this tool every subsequent turn too, the conversation stayed permanently broken. Both now fail gracefully: a DB write failure returns a friendly error without corrupting session state, and malformed JSON abandons the session with a friendly message instead of crashing.
Verified live via list_tools against cfg.mcps: the Obsidian server is keyed "obsidian" (not "Jarvis Brain"), and delegation goes through the "jarvis-router" server's run_antigravity tool rather than a standalone "Antigravity" server. Tool names and call shapes are corrected to match: vault_write, search_simple, vault_read, vault_patch, run_antigravity. search_simple/vault_read return JSON payloads (not plain text/markdown), so _extract_note_paths and the new _extract_read_content helper unwrap them, with a fallback for plain text. vault_patch requires targetType/target/operation rather than a raw content overwrite, so the status update now targets the "Status" heading directly instead of regex-rewriting the whole file. run_antigravity's schema only accepts a "task" string, so the delegation framing is folded into that field instead of a separate "instructions" argument.
isair
force-pushed
the
develop
branch
2 times, most recently
from
July 26, 2026 20:17
075622a to
2d40388
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds a deterministic, multi-turn "project intake" flow: the user describes a project idea over a short back-and-forth, Jarvis matches it to a template, asks one question per turn, then writes a brief to Obsidian and (on a separate later trigger) hands the plan off to Antigravity for development.
project_intake_sessionstable (src/jarvis/memory/db.py): newCREATE TABLE IF NOT EXISTS+ CRUD backing the intake state machine (awaiting_type->in_progress->completed), following the same pattern as the existingmealstable.project_intake_enabledandproject_templates_path(src/jarvis/config.py), surfaced in the auto-generated settings UI and added to the sharedMockConfigtest doubles used bytests/andevals/.projectIntakeandstartProjectDevelopmenttools (src/jarvis/tools/builtin/project_intake.py, registered in the builtin catalogue):projectIntakeruns the interview itself (template matching againstproject_templates.json, one question per turn, an abandon phrase, and the Obsidian brief write on completion);startProjectDevelopmentis the separate, later single-shot trigger that resolves a saved plan note, reads it back, and delegates it to Antigravity via MCP.src/jarvis/reply/engine.py): runs right after redaction, before recent-dialogue lookup, MCP refresh, the tool router, and the planner. An active intake session forces theprojectIntaketool call and skips everything else for the turn, fail-open on any DB error. The agentic loop also now ends immediately after anyprojectIntake/startProjectDevelopmentcall (both the model-emitted and planner direct-exec call sites), so the chat model can't re-invoke the tool with fabricated input and silently advance multi-turn state more than once per turn.project_intake.spec.mdin CLAUDE.md's Spec File Registry, adds the "3c. Project Intake Gate" entry and flow-diagram step todocs/llm_contexts.mdas the first non-LLM pre-planner gate that also calls an MCP server directly from tool code, and mentions the capability in README's Built-in Tools list.evals/test_project_intake_flow.py): covers the one part of the flow that genuinely depends on the LLM router/planner rather than the deterministic gate — the initial trigger phrase must route toprojectIntakewith a reply that is exactly the type question — and asserts the follow-up turn is gated deterministically instead of relying on the model to pick the tool again on its own.Note on MCP tool names
The Obsidian and Antigravity MCP server/tool names in
project_intake.py(OBSIDIAN_MCP_SERVER = "Jarvis Brain",OBSIDIAN_WRITE_TOOL = "create_note",OBSIDIAN_SEARCH_TOOL = "simple_search",OBSIDIAN_READ_TOOL = "get_file_contents",OBSIDIAN_PATCH_TOOL = "patch_content",ANTIGRAVITY_MCP_SERVER = "Antigravity",ANTIGRAVITY_DISPATCH_TOOL = "run_task") are best-effort defaults based on common MCP server conventions and are placeholders pending confirmation against the user's actual configured servers.Test plan
tests/tools/builtin/test_project_intake.pyandtests/tools/builtin/test_start_project_development.pypasstests/test_engine_project_intake_gate.pypassesevals/test_project_intake_flow.pypasses🤖 Generated with Claude Code