The bug was caused by mark_tool_calls_consumed() being called after displaying each chunk, which advanced last_consumed_position to the end of the current buffer. When the next chunk arrived with JSON, the unchecked_buffer started at position 0 of the slice, causing is_on_own_line() to return true (position 0 is always "on its own line"). Removed the problematic mark_tool_calls_consumed() call from the "no tool executed" branch. The remaining call after actual tool execution is correct and necessary. Added integration test that verifies inline JSON in prose is not detected as a tool call.
14 KiB
Project Memory
Updated: 2026-01-19T08:32:03Z | Size: 14.0k chars
Remember Tool Wiring
crates/g3-core/src/tools/memory.rs[0..5000] -execute_remember(),get_memory_path(),merge_memory()crates/g3-core/src/tool_definitions.rs[11000..12000] - remember tool definition increate_core_tools()crates/g3-core/src/tool_dispatch.rs[48] - dispatch case for "remember"crates/g3-core/src/prompts.rs[4200..6500] - Project Memory section in native promptcrates/g3-cli/src/lib.rs[1472..1495] -read_project_memory()loads memory at startup
Context Window & Compaction
crates/g3-core/src/context_window.rs[0..815] -ContextWindow,reset_with_summary(),should_compact(),thin_context()crates/g3-core/src/lib.rs[0..132483] -Agentstruct,force_compact(),stream_completion_with_tools()
Session Storage & Continuation
crates/g3-core/src/session_continuation.rs[0..541] -SessionContinuation,save_continuation(),load_continuation()crates/g3-core/src/paths.rs[0..133] -get_session_logs_dir(),get_thinned_dir(),get_session_file()crates/g3-core/src/session.rs- Session logging utilities
Tool System
crates/g3-core/src/tool_definitions.rs[0..544] -create_core_tools(),create_tool_definitions(),ToolConfigcrates/g3-core/src/tool_dispatch.rs[0..73] -dispatch_tool()routing
CLI Argument Parsing
crates/g3-cli/src/lib.rs[270..380] -Clistruct with clap derive macroscrates/g3-cli/src/lib.rs[1700..2200] -run_interactive()with/command handlers
Streaming Markdown Formatter
crates/g3-cli/src/streaming_markdown.rs[21500..22500] -format_header()processes headers with inline formattingcrates/g3-cli/tests/streaming_markdown_test.rs- Tests for markdown formatting includingtest_bold_inside_header,test_italic_inside_header,test_code_inside_header,test_mixed_formatting_inside_header
Auto-Memory Feature
crates/g3-core/src/lib.rs[1459..1522] -send_auto_memory_reminder()sends reminder to LLM after tool callscrates/g3-core/src/lib.rs[1451..1454] -set_auto_memory()enables/disables auto-memorycrates/g3-core/src/lib.rs[116] -tool_calls_this_turn: Vec<String>tracks tools called per turncrates/g3-cli/src/lib.rs[393] -auto_memory: boolCLI flag definitioncrates/g3-cli/src/lib.rs[641..642, 684..685] - Flag applied to agent in console/machine modescrates/g3-cli/src/lib.rs[1340..1350, 1394..1404] - Auto-memory reminder called in single-shot modecrates/g3-cli/src/lib.rs[1758, 1931, 2216] - Auto-memory reminder called in interactive mode
Tool Call Tracking
crates/g3-core/src/lib.rs[2843..2855] -execute_tool_in_dir()tracks all tool calls for auto-memory
Agent Mode
crates/g3-cli/src/lib.rs[695..910] -run_agent_mode()handles specialized agent execution with custom promptscrates/g3-cli/src/lib.rs[820..835] - Agent creation withAgent::new_with_custom_prompt()crates/g3-cli/src/lib.rs[837] -agent.set_agent_mode()enables agent-specific session tracking
CLI Entry Points and Modes
crates/g3-cli/src/lib.rs[0..140000] -run()main entry,run_agent_mode(),run_accumulative_mode(),run_autonomous(),run_interactive(),run_interactive_machine()crates/g3-cli/src/lib.rs-execute_task()(~line 1990),execute_task_machine()(~line 2262) - duplicated retry logic
Retry Infrastructure
crates/g3-core/src/retry.rs[0..12000] -execute_with_retry(),retry_operation(),RetryConfig,RetryResult- used by g3-planner but not g3-cli
UI Abstraction Layer
crates/g3-core/src/ui_writer.rs[0..4500] -UiWritertrait,NullUiWritercrates/g3-cli/src/ui_writer_impl.rs[0..14000] -ConsoleUiWriterimplementationcrates/g3-cli/src/simple_output.rs[0..1200] -SimpleOutputhelper (separate from UiWriter)
Feedback Extraction
crates/g3-core/src/feedback_extraction.rs[0..22000] -extract_coach_feedback(),try_extract_from_session_log(),try_extract_from_native_tool_call()
Streaming Utilities
crates/g3-core/src/streaming.rs[0..10000] -truncate_line(),truncate_for_display(),log_stream_error(),is_connection_error()
Background Process Management
crates/g3-core/src/background_process.rs[0..3000] -BackgroundProcessManager,start(),list(),is_running(),get(),remove()- Design: No
stop()method - processes are stopped via shell tool usingkill <pid>
Unified Diff Application
crates/g3-core/src/utils.rs[5000..15000] -apply_unified_diff_to_string(),parse_unified_diff_hunks()- Handles multi-hunk diffs, CRLF normalization, range constraints
Error Classification
crates/g3-core/src/error_handling.rs[0..567 lines] -classify_error(),ErrorType,RecoverableError- Priority order: rate limit > network > server > busy > timeout > token limit > context length
- Note: "Connection timeout" classifies as NetworkError (not Timeout) due to "connection" keyword priority
CLI Module Extractions
crates/g3-cli/src/metrics.rs[0..5416] -TurnMetrics,format_elapsed_time(),generate_turn_histogram()crates/g3-cli/src/project_files.rs[0..5577] -read_agents_config(),read_project_readme(),read_project_memory(),extract_readme_heading()crates/g3-cli/src/coach_feedback.rs[0..4025] -extract_from_logs()for coach-player loop feedback extraction
Context Compaction
crates/g3-core/src/compaction.rs[0..11213] -perform_compaction(),CompactionResult,CompactionConfig,calculate_capped_summary_tokens(),should_disable_thinking(),build_summary_messages(),apply_summary_fallback_sequence()- Unified compaction used by both
force_compact()and auto-compaction instream_completion_with_tools()
Streaming Markdown Formatter (Code Blocks)
crates/g3-cli/src/streaming_markdown.rs[693..735] -flush_incomplete()handles unclosed blocks at end of streamcrates/g3-cli/src/streaming_markdown.rs[654..675] -emit_code_block()joins block_buffer and highlights codecrates/g3-cli/src/streaming_markdown.rs[439..462] -process_in_code_block()detects closing fence on newline- Bug fix: closing ``` without trailing newline must be detected in flush_incomplete(), not just process_in_code_block()
ACD (Aggressive Context Dehydration)
crates/g3-core/src/acd.rs[0..22000] -Fragment,Fragment::new(),Fragment::save(),Fragment::load(),generate_stub(),list_fragments(),get_latest_fragment_id()crates/g3-core/src/tools/acd.rs[0..8500] -execute_rehydrate()tool implementationcrates/g3-core/src/paths.rs[3200..3400] -get_fragments_dir()returns.g3/sessions/<session_id>/fragments/crates/g3-core/src/compaction.rs[195..240] - ACD integration inperform_compaction(), creates fragment and stub whenacd_enabledcrates/g3-core/src/context_window.rs[10100..10700] -reset_with_summary_and_stub()adds stub before summarycrates/g3-cli/src/lib.rs[157..161] ---acdCLI flagcrates/g3-cli/src/lib.rs[1476..1525] -/fragmentsand/rehydratecommands
ACD Fragment Storage Format
{
"fragment_id": "abc123",
"created_at": "2026-01-11T...",
"messages": [...],
"message_count": 47,
"user_message_count": 23,
"assistant_message_count": 24,
"tool_call_summary": {"read_file": 4, "shell": 5},
"estimated_tokens": 18500,
"topics": ["implemented auth", "fixed bug"],
"preceding_fragment_id": "xyz789"
}
UTF-8 Safe String Slicing Pattern
Problem: Rust string slices (&s[..n]) use byte indices, not character indices. Multi-byte UTF-8 characters (emoji, bullets •, ×, ⚡) cause panics if sliced mid-character.
Solution: Use char_indices() to find byte boundaries:
// Get byte index of the Nth character
let byte_idx = s.char_indices()
.nth(char_limit)
.map(|(i, _)| i)
.unwrap_or(s.len());
let truncated = &s[..byte_idx];
// For length checks, use chars().count() not len()
if s.chars().count() <= max_len { ... }
Danger zones: Display truncation, ACD stubs, user input handling, any string with non-ASCII characters.
CLI Module Structure (Post-Refactor)
crates/g3-cli/src/lib.rs[0..415] - Entry point,run(), mode dispatch, config loadingcrates/g3-cli/src/cli_args.rs[0..133] -Clistruct with clap derive macros, argument parsingcrates/g3-cli/src/autonomous.rs[0..785] -run_autonomous(), coach-player feedback loopcrates/g3-cli/src/agent_mode.rs[0..284] -run_agent_mode()specialized agent executioncrates/g3-cli/src/accumulative.rs[0..343] -run_accumulative_mode()iterative requirementscrates/g3-cli/src/interactive.rs[0..851] -run_interactive(),run_interactive_machine(), REPL with/commandscrates/g3-cli/src/task_execution.rs[0..212] -execute_task_with_retry(),OutputModeenum - unified retry logiccrates/g3-cli/src/utils.rs[0..91] -display_welcome_message(),get_workspace_path()
Studio - Multi-Agent Workspace Manager
crates/studio/src/main.rs[0..12500] -cmd_run(),cmd_status(),cmd_accept(),cmd_discard(),extract_session_summary()crates/studio/src/session.rs-Session,SessionStatus, session metadata managementcrates/studio/src/git.rs-GitWorktree, git worktree management for isolated agent sessions
Session log format: Session logs are stored at <worktree>/.g3/sessions/<session_id>/session.json with structure:
{
"context_window": {
"conversation_history": [{"role": "...", "content": "..."}],
"percentage_used": 45.2,
"total_tokens": 200000,
"used_tokens": 90400
},
"session_id": "...",
"status": "...",
"timestamp": "..."
}
Project Memory Location
- Memory is now stored at
analysis/memory.md(version controlled, shared across worktrees) crates/g3-core/src/tools/memory.rs-get_memory_path()returnsanalysis/memory.mdcrates/g3-cli/src/project_files.rs-read_project_memory()reads fromanalysis/memory.md
Compact Tool Output
crates/g3-cli/src/ui_writer_impl.rs-print_tool_compact()handles compact display for file ops and other toolscrates/g3-core/src/streaming.rs-format_*_summary()functions for each tool type
Racket Code Search Support
Tree-sitter based syntax-aware search for Racket .rkt files.
crates/g3-core/src/code_search/searcher.rs- Racket parser init [~line 45] -
tree_sitter_racket::LANGUAGE - Extension mapping [~line 90] -
.rkt,.rktl,.rktd→ "racket"
- Racket parser init [~line 45] -
Auto-Memory Reminder Format
Rich few-shot prompting for higher quality memory entries with per-symbol char ranges.
crates/g3-core/src/lib.rssend_auto_memory_reminder()[47800..48800] - MEMORY CHECKPOINT prompt with few-shot examples
crates/g3-core/src/prompts.rs- Memory Format section [3800..4500] - system prompt template and examples
Language-Specific Prompt Injection
Auto-detects programming languages in workspace and injects toolchain guidance.
-
crates/g3-cli/src/language_prompts.rsLANGUAGE_PROMPTS[12..19] - static array of (lang_name, extensions, prompt_content)detect_languages()[22..32] - scans workspace for language filesget_language_prompts_for_workspace()[88..108] - returns formatted prompt for detected languagesscan_directory_for_extensions()[42..77] - recursive scan with depth limit (2), skips hidden/vendor dirs
-
prompts/langs/- directory for language prompt markdown filesracket.md- Racket/raco toolchain guidance (compilation, testing, analysis, profiling)
-
crates/g3-cli/src/project_files.rscombine_project_content()[89..106] - now acceptslanguage_contentparameter
To add a new language:
- Create
prompts/langs/<lang>.mdwith toolchain guidance - Add entry to
LANGUAGE_PROMPTSinlanguage_prompts.rswith extensions
Agent-Specific Language Prompts
Injects agent+language-specific guidance when running in agent mode in a workspace with detected languages.
-
crates/g3-cli/src/language_prompts.rsAGENT_LANGUAGE_PROMPTS[21..26] - static array of (agent_name, lang_name, prompt_content) tuplesget_agent_language_prompt()[115..121] - looks up prompt for specific agent+lang comboget_agent_language_prompts_for_workspace()[124..137] - usesdetect_languages()then looks up agent-specific prompts
-
crates/g3-cli/src/agent_mode.rs- Lines 149-159 - calls
get_agent_language_prompts_for_workspace()and appends to system prompt
- Lines 149-159 - calls
-
prompts/langs/<agent>.<lang>.md- file naming pattern for agent+lang promptsprompts/langs/carmack.racket.md- Racket-specific guidance for carmack agent
To add a new agent+lang prompt:
- Create
prompts/langs/<agent>.<lang>.md - Add entry to
AGENT_LANGUAGE_PROMPTSinlanguage_prompts.rswithinclude_str!
MockProvider for Testing
Configurable mock LLM provider for integration testing without real API calls.
-
crates/g3-providers/src/mock.rsMockProvider[220..320] - mock provider with response queue, request trackingMockResponse[35..200] - configurable response with chunks and usageMockChunk[45..100] - individual streaming chunk (content, finished, tool_calls)scenariosmodule [410..480] - preset scenarios:text_only_response(),multi_turn(),tool_then_response()
-
crates/g3-core/tests/mock_provider_integration_test.rstest_butler_bug_scenario()- reproduces consecutive user messages bugtest_text_only_response_saves_to_context()- verifies text responses savedtest_multi_turn_text_only_maintains_alternation()- verifies user/assistant alternation
Usage pattern:
let provider = MockProvider::new()
.with_response(MockResponse::text("Hello!"));
let mut registry = ProviderRegistry::new();
registry.register(provider);
let agent = Agent::new_for_test(config, NullUiWriter, registry).await?;