Per Tier 1 addendum 3 (the real defect): tests hotpatch individual state
fields via set_value instead of calling the proper project-switch
flow. The session-scoped subprocess may be on a stale project from a
prior test (e.g. test_context_sim_live switches to
temp_livecontextsim.toml and never switches back). The RAG engine uses
active_project_root (derived from active_project_path) as its base_dir,
NOT ui_files_base_dir. So hotpatching files/rag_enabled via set_value
while active_project_path is stale leaves the RAG engine looking at a
dead dir.
Fix: switch to the workspace project explicitly at the start of the
test (like a user would) using client.push_event('custom_callback',
...) + client.wait_for_project_switch(...). The path must be absolute
because the subprocess's CWD is the workspace, so a relative path
like 'tests/artifacts/.../manualslop.toml' would resolve to the wrong
dir from the subprocess's CWD.
Verified: the switch fires successfully (no WARNING printed). But the
RAG search still returns 0 chunks — the index_file rebuild is not
adding the files. The exact cause is still under investigation.
This is the proper fix per Tier 1 (NOT "delete stale files" which
treats the symptom). The sim tests' teardown() also needs a switch-back
to the workspace project (separate track).
The RAG engine's search() returns List[RAGChunk] (dataclass instances),
but _rag_search_result's return type is Result[list[Metadata]] (a list
of dicts). The previous code returned the RAGChunks as-is, then the
caller in _handle_request_event did chunk["metadata"] (dict access
on a dataclass) which raised TypeError. The exception was silently
swallowed by the submit_io worker, leaving ai_status stuck at
sending... for the full 50-second test poll before failing.
Two surgical changes:
1. _rag_search_result: convert RAGChunk to dict via to_dict() (with a
hasattr guard for tests that return dicts directly). Matches the
function's documented return type.
2. _handle_request_event: use isinstance guards + dict.get() on the
chunk fields. Defensive against the type mismatch and matches the
dict contract.
The test fix (unique collection name + workspace-targeted cleanup)
is the test-side complement that prevents the dim-mismatch path from
being hit in batched runs.
Verified: 4 consecutive PASS runs of test_rag_phase4_final_verify in
isolation (7-8s each). 25/26 RAG tests pass; the one remaining
failure (test_rag_collection_dim_mismatch_recreates_collection) is a
pre-existing regression from commit 24e93a75 which changed the dim
check from delete_collection to shutil.rmtree without updating the
test mock setup. Out of scope for this fix.
- Added import sys to src/api_hook_client.py.
- Fixed App.__getattr__ to use direct attribute access on controller to avoid recursion.
- Simplified _get_app_attr and _has_app_attr in src/api_hooks.py.
- Centralized RAG and symbol enrichment in AppController._handle_request_event.
- Updated ests/test_symbol_parsing.py to match the new enrichment flow.
- Removed redundant task appending from i_status and mma_status setters.
- Improved _sync_rag_engine to only set 'ready' status after indexing is confirmed.
- Updated est_status_encapsulation.py to reflect setter changes.
- Fixed circular import in chromadb by using lazy imports in
ag_engine.py.
- Moved RAG engine initialization to background threads in AppController to avoid blocking UI.
- Added _rag_engine_lock to prevent race conditions during engine re-initialization.
- Updated Gemini embedding model to gemini-embedding-001 (available) from ext-embedding-004 (not found).
- Fixed _rebuild_rag_index to use fresh
ag_engine instance from self in every iteration.
- Optimized est_rag_phase4_final_verify.py and est_rag_phase4_stress.py to wait for RAG sync before continuing.
- Added dummy embedding fallback in LocalEmbeddingProvider if sentence-transformers fails to load.