diff --git a/tests/test_action_menu_order.py b/tests/test_action_menu_order.py
index c4e957054..84472662e 100644
--- a/tests/test_action_menu_order.py
+++ b/tests/test_action_menu_order.py
@@ -161,13 +161,13 @@ def test_email_settings_show_away_account_and_compact_display_controls() -> None
assert "enabled ? 'Show' : 'Hide'" in source
assert "email-settings-inline-link" in source
assert "email-auto-reply-exclude" not in source
- assert "_emailWritingStyleHtml(writingStyle) + _emailDisplaySettingsHtml()" in source
+ assert "_emailWritingStyleHtml(writingStyle) + _emailDisplaySettingsHtml(cfg)" in source
assert ".email-settings-status.is-success" in style
assert "var(--color-success, #4caf50)" in style
assert ".email-style-settings-extract svg" in style
assert "export async function mountEmailSettings(host)" in source
assert "_openGlobalEmailSettings('show-tags')" in source
- assert source.count('class="admin-card email-settings-section') == 4
+ assert source.count('class="admin-card email-settings-section') == 5
assert 'id="settings-email-default-card"' in (ROOT / "static/index.html").read_text(encoding="utf-8")
assert "multipleAccounts" in source
diff --git a/tests/test_builtin_themes_static.py b/tests/test_builtin_themes_static.py
index dde1514fd..f1fbcee60 100644
--- a/tests/test_builtin_themes_static.py
+++ b/tests/test_builtin_themes_static.py
@@ -8,7 +8,7 @@ THEME_JS = (ROOT / "static/js/theme.js").read_text(encoding="utf-8")
def test_five_distinct_builtin_themes_are_available() -> None:
expected = {
- "eclipse": "constellations",
+ "eclipse": "starfield-depth",
"porcelain": "dots",
"arcade": "synapse",
"blueprint": "dots",
diff --git a/tests/test_chat_ttft_timer_static.py b/tests/test_chat_ttft_timer_static.py
index d5d28b430..303f66699 100644
--- a/tests/test_chat_ttft_timer_static.py
+++ b/tests/test_chat_ttft_timer_static.py
@@ -25,8 +25,9 @@ def test_measured_ttft_is_shown_in_message_stats():
def test_compact_footer_and_details_show_real_performance_counters():
assert "`${Number(tps).toFixed(2)} tok/s`" in RENDERER
- assert "`${Number(ttft).toFixed(3)}s TTFT`" in RENDERER
- assert "`${Number(injectedTokens).toLocaleString()} in`" in RENDERER
+ assert "const visibleTtft = metrics.client_ttft ?? metrics.time_to_first_token" in RENDERER
+ assert "${Number(visibleTtft).toFixed(3)}s" in RENDERER
+ assert "${Number(injectedTokens).toLocaleString()}" in RENDERER
assert 'Input' in RENDERER
assert 'Injected' in RENDERER
assert 'all rounds' not in RENDERER
diff --git a/tests/test_clawmm_r47_malformed_write_body.py b/tests/test_clawmm_r47_malformed_write_body.py
index ea2b36ee8..00a35b800 100644
--- a/tests/test_clawmm_r47_malformed_write_body.py
+++ b/tests/test_clawmm_r47_malformed_write_body.py
@@ -87,7 +87,7 @@ async def test_malformed_text_artifact_write_uses_one_bounded_raw_body_handoff(m
events = [json.loads(chunk[6:]) for chunk in raw if '[DONE]' not in chunk]
assert len(requests) == 2
assert 'tools' not in requests[1]
- assert requests[1]['max_tokens'] == 4096
+ assert requests[1]['max_tokens'] == 8192
assert len(executed) == 1
assert executed[0].tool_type == 'write_file'
assert executed[0].content == (
diff --git a/tests/test_compare_mobile_tabs_static.py b/tests/test_compare_mobile_tabs_static.py
index 15d8366ce..14270050b 100644
--- a/tests/test_compare_mobile_tabs_static.py
+++ b/tests/test_compare_mobile_tabs_static.py
@@ -12,7 +12,7 @@ def test_mobile_compare_mounts_accessible_tabs_without_removing_panes():
index = _read("static/js/compare/index.js")
panes = _read("static/js/compare/panes.js")
- assert "mountMobilePaneTabs(container, grid)" in index
+ assert "mountMobilePaneTabs(container, grid, (anchor) => _addPane(anchor))" in index
assert "role', 'tablist'" in panes
assert "role', 'tab'" in panes
assert "role', 'tabpanel'" in panes
diff --git a/tests/test_docs_no_orphan_images.py b/tests/test_docs_no_orphan_images.py
index f6ed43560..02e0c4494 100644
--- a/tests/test_docs_no_orphan_images.py
+++ b/tests/test_docs_no_orphan_images.py
@@ -83,7 +83,8 @@ def test_pages_site_owns_its_entrypoint_and_media():
assert REPO / "website/index.html" in website_files
assert REPO / "docs/index.html" not in docs_files
- assert not [p for p in docs_files if p.suffix.lower() in VIDEO_EXTS | {".md"}]
+ assert not [p for p in docs_files if p.suffix.lower() in VIDEO_EXTS]
+ assert not [p for p in docs_files if p.name in PUBLIC_GUIDES]
website_paths = {p.relative_to(REPO / "website").as_posix() for p in website_files}
assert PUBLIC_GUIDES <= website_paths
diff --git a/tests/test_document_library_expanded_export.py b/tests/test_document_library_expanded_export.py
index 812048752..9d9fc5ecc 100644
--- a/tests/test_document_library_expanded_export.py
+++ b/tests/test_document_library_expanded_export.py
@@ -18,7 +18,7 @@ def test_expanded_document_card_has_export_beside_clone():
def test_expanded_export_reuses_download_function_without_proxy_click():
- assert "const exportDocumentFile = async () =>" in DOC_LIBRARY_JS
+ assert "const exportDocumentFile = async (format = 'original') =>" in DOC_LIBRARY_JS
assert "await exportDocumentFile();" in DOC_LIBRARY_JS
assert "exportItem.click();" not in DOC_LIBRARY_JS
assert "exportItem.type = 'button';" in DOC_LIBRARY_JS
diff --git a/tests/test_document_library_mobile_footer.py b/tests/test_document_library_mobile_footer.py
index 186d50be1..e690ecc30 100644
--- a/tests/test_document_library_mobile_footer.py
+++ b/tests/test_document_library_mobile_footer.py
@@ -10,24 +10,26 @@ SOURCE = (ROOT / "static/js/documentLibrary.js").read_text(encoding="utf-8")
STYLE = (ROOT / "static/style.css").read_text(encoding="utf-8")
-def test_mobile_footer_exposes_open_and_more_only():
+def test_mobile_footer_exposes_delete_open_and_more():
assert "doclib-expanded-open-btn" in SOURCE
assert "doclib-expanded-mobile-more" in SOURCE
assert "label: 'Open in new chat'" in SOURCE
assert "'Open in original' : 'Open document'" in SOURCE
- assert "label: 'Export file'" in SOURCE
+ assert "label: 'Export file ›'" in SOURCE
+ assert "label: 'Original format'" in SOURCE
+ assert "label: 'Markdown (.md)'" in SOURCE
assert "'Restore document' : 'Archive document'" in SOURCE
assert "label: 'Delete document'" in SOURCE
- mobile_css = STYLE.split("The Documents preview footer only exposes Open and More", 1)[1]
+ mobile_css = STYLE.split("On phones, keep Delete explicit", 1)[1]
mobile_css = mobile_css.split("/* Chat top bar", 1)[0]
for hidden_action in (
- ".doclib-expanded-delete-btn",
".doclib-expanded-archive-btn",
".doclib-expanded-clone-btn",
".doclib-expanded-export-btn",
):
assert hidden_action in mobile_css
+ assert ".doclib-expanded-delete-btn {\n display: inline-flex !important" in mobile_css
assert ".doclib-expanded-mobile-more" in mobile_css
assert "display: inline-flex" in mobile_css
assert "box-sizing: border-box" in mobile_css
diff --git a/tests/test_editor_canvas_navigation_js.py b/tests/test_editor_canvas_navigation_js.py
index e61eaee05..f20568cbf 100644
--- a/tests/test_editor_canvas_navigation_js.py
+++ b/tests/test_editor_canvas_navigation_js.py
@@ -91,13 +91,15 @@ def test_apply_pan_updates_state_canvas_overlay_and_cursor_classes():
def test_hand_navigation_is_wired_to_toolbar_keyboard_and_offline_graph():
toolbar = (ROOT / "static/js/editor/build/toolbar.js").read_text(encoding="utf-8")
+ shortcuts = (ROOT / "static/js/editor/tool-shortcuts.js").read_text(encoding="utf-8")
keyboard = (ROOT / "static/js/editor/keyboard-shortcuts.js").read_text(encoding="utf-8")
events = (ROOT / "static/js/editor/canvas-events.js").read_text(encoding="utf-8")
editor = (ROOT / "static/js/galleryEditor.js").read_text(encoding="utf-8")
service_worker = (ROOT / "static/sw.js").read_text(encoding="utf-8")
assert "{ id: 'hand', label: 'Hand'" in toolbar
- assert "key: 'H'" in toolbar
+ assert "t.key = TOOL_SHORTCUTS[t.id]" in toolbar
+ assert "hand: 'H'" in shortcuts
assert "e.code === 'Space'" in keyboard
assert "setTemporaryPan?.(true)" in keyboard
assert "isDirectPanIntent(state.tool, state.spacePanActive" in events
diff --git a/tests/test_editor_layer_thumbnail_render.py b/tests/test_editor_layer_thumbnail_render.py
index 3af077197..aa9e283e1 100644
--- a/tests/test_editor_layer_thumbnail_render.py
+++ b/tests/test_editor_layer_thumbnail_render.py
@@ -20,8 +20,8 @@ def test_hover_thumbnail_uses_rendered_layer_output():
def test_inline_layer_rows_share_the_common_thumbnail_renderer():
panel = (ROOT / "static/js/editor/layer-panel.js").read_text()
- assert "const previewCanvas = renderLayer?.(layer) || layer.canvas;" in panel
- assert "const thumb = createInlineThumbnail(previewCanvas, `${layer.name} preview`);" in panel
+ assert "const thumb = createInlineThumbnail(() => renderLayer?.(layer) || layer.canvas, `${layer.name} preview`);" in panel
+ assert "thumb._refreshPreview = draw;" in panel
assert "thumbCtx.fillStyle = ((x / tile + y / tile) & 1)" not in panel
diff --git a/tests/test_editor_text_layers_js.py b/tests/test_editor_text_layers_js.py
index 9cd5f4815..bf61dd880 100644
--- a/tests/test_editor_text_layers_js.py
+++ b/tests/test_editor_text_layers_js.py
@@ -119,6 +119,7 @@ def test_rasterize_text_layer_clears_retained_metadata_only_for_text():
def test_text_tool_is_retained_across_editor_subsystems():
editor = (ROOT / "static/js/galleryEditor.js").read_text(encoding="utf-8")
toolbar = (ROOT / "static/js/editor/build/toolbar.js").read_text(encoding="utf-8")
+ shortcuts = (ROOT / "static/js/editor/tool-shortcuts.js").read_text(encoding="utf-8")
controls = (ROOT / "static/js/editor/build/controls.js").read_text(encoding="utf-8")
codec = (ROOT / "static/js/editor/document-codec.js").read_text(encoding="utf-8")
geometry = (ROOT / "static/js/editor/document-geometry.js").read_text(encoding="utf-8")
@@ -127,7 +128,8 @@ def test_text_tool_is_retained_across_editor_subsystems():
merge = (ROOT / "static/js/editor/wire-merge-buttons.js").read_text(encoding="utf-8")
assert "{ id: 'text', label: 'Text'" in toolbar
- assert "key: 'T'" in toolbar
+ assert "t.key = TOOL_SHORTCUTS[t.id]" in toolbar
+ assert "text: 'T'" in shortcuts
assert 'id="ge-text-section"' in controls
assert "if (state.tool === 'text') return _placeText(e);" in editor
assert "kind: l.kind || 'raster'" in editor
diff --git a/tests/test_editor_topbar_static.py b/tests/test_editor_topbar_static.py
index 0f26b42d5..8f2ed3d50 100644
--- a/tests/test_editor_topbar_static.py
+++ b/tests/test_editor_topbar_static.py
@@ -32,15 +32,15 @@ def test_stacked_labels_share_one_size_and_position_rule():
assert "top: 2px;" in STYLE
-def test_narrow_topbar_reflows_essential_actions_instead_of_clipping_them():
- assert "ge-topbar-overflow" in OVERFLOW
- assert "topbar.classList.add('ge-topbar-overflow')" in OVERFLOW
- assert "(max-width: 700px)" in OVERFLOW
- assert ".ge-topbar.ge-topbar-overflow" in STYLE
- assert "flex: 1 0 100%;" in STYLE
- right_start = STYLE.index(".ge-topbar-overflow .ge-topbar-right")
- overflow_right = STYLE[right_start:STYLE.index("}", right_start)]
- assert "justify-content: flex-start;" in overflow_right
+def test_narrow_topbar_scrolls_essential_actions_and_hides_ai_group_first():
+ assert "if (topbar.scrollWidth > topbar.clientWidth)" in OVERFLOW
+ assert "aiGroup.forEach(el => { el.style.display = 'none'; });" in OVERFLOW
+ assert "new ResizeObserver(() => syncOverflow())" in OVERFLOW
+ mobile = STYLE[STYLE.index("/* Editor topbar — scrolls horizontally") :]
+ topbar = mobile[mobile.index(".ge-topbar {") : mobile.index("}", mobile.index(".ge-topbar {"))]
+ assert "overflow-x: auto;" in topbar
+ assert "flex-wrap: nowrap;" in topbar
+ assert "justify-content: flex-start;" in topbar
def test_desktop_tool_rail_keeps_long_tool_names_readable():
diff --git a/tests/test_email_attachment_search.py b/tests/test_email_attachment_search.py
index 98dc39f78..02470334b 100644
--- a/tests/test_email_attachment_search.py
+++ b/tests/test_email_attachment_search.py
@@ -160,8 +160,10 @@ def test_attachment_cache_backfill_preserves_message_id(tmp_path, monkeypatch):
def test_single_email_tag_has_no_more_control():
library = open("static/js/emailLibrary.js", encoding="utf-8").read()
-
- assert 'class="email-tags-more email-tags-more-single"' not in library
+ group = library[library.index("function _emailTagGroupHtml("):library.index("function _fitEmailCardTags(")]
+ assert "if (visible.length === 1) return visible[0];" in group
+ assert "if (visible.length === 2) return visible.join('');" in group
+ assert group.index("if (visible.length === 2)") < group.index("email-tags-more-single")
def test_email_folder_and_filter_pickers_treat_their_buttons_as_inside_clicks():
@@ -181,7 +183,7 @@ def test_empty_reply_has_two_editable_rows_and_reply_survives_compact_toolbar():
fit_start = library.index("function _fitReaderActions")
fit_end = library.index("const _readerActionFitObserver", fit_start)
fit = library[fit_start:fit_end]
- assert '[data-act="ai-reply"], [data-act="reply-all"]' in fit
+ assert '[data-act="reply-all"], [data-act="forward"]' in fit
assert '[data-act="reply"]' not in fit
diff --git a/tests/test_email_folder_chip_static.py b/tests/test_email_folder_chip_static.py
index 41f6d0a3b..193cfa6aa 100644
--- a/tests/test_email_folder_chip_static.py
+++ b/tests/test_email_folder_chip_static.py
@@ -16,7 +16,7 @@ def test_folder_chip_stays_with_date_and_moves_down():
folder_rule_start = css.index(".email-folder-chip {", first_folder_rule + 1)
folder_rule = css[folder_rule_start:][:220]
assert "position: relative;" in folder_rule
- assert "top: 2px;" in folder_rule
+ assert "top: 4px;" in folder_rule
group_rule = css[css.index(".email-meta-date-group {"):][:180]
assert "display: inline-flex;" in group_rule
assert "white-space: nowrap;" in group_rule
diff --git a/tests/test_email_open_dedup_js.py b/tests/test_email_open_dedup_js.py
index b5fea966a..9e5bd9f1c 100644
--- a/tests/test_email_open_dedup_js.py
+++ b/tests/test_email_open_dedup_js.py
@@ -127,15 +127,16 @@ def test_attachment_warning_only_checks_authored_reply_text():
assert "text.split(/^>|^On .* wrote:/m)" not in helper
-def test_email_send_saves_recovery_draft_before_send_and_closes_only_on_success():
+def test_email_send_saves_recovery_draft_before_send_and_retains_it_on_failure():
source = (_REPO / "static/js/document.js").read_text(encoding="utf-8")
send = source[source.index("async function _sendEmail"):source.index("\n\n async function _saveDraft", source.index("async function _sendEmail"))]
assert "async function _saveEmailDraftForRecovery" in source
assert send.index("_saveEmailDraftForRecovery({") < send.index("fetch(`${API_BASE}/api/email/send`")
+ assert send.index("if (isLibraryOpen()) closeLibrary();") > send.index("const sendRequest = fetch(")
+ assert send.index("if (isLibraryOpen()) closeLibrary();") < send.index("const res = await sendRequest")
success_branch = send[send.index("if (data.success) {"):]
- assert "closeLibrary()" in success_branch
- assert send.index("closeLibrary()") > send.index("const res = await sendRequest")
+ assert "fetch(`${API_BASE}/api/document/${sendDocId}`, { method: 'DELETE' })" in success_branch
assert "Draft kept in Drafts." in send
assert "recoveryDraft.draft_uid" in send
diff --git a/tests/test_email_picker_exclusivity_static.py b/tests/test_email_picker_exclusivity_static.py
index 18f217cfa..365514f43 100644
--- a/tests/test_email_picker_exclusivity_static.py
+++ b/tests/test_email_picker_exclusivity_static.py
@@ -10,8 +10,11 @@ def test_email_folder_and_filter_pickers_are_exclusive_and_escape_safe():
assert source.count("const filterMenu = document.getElementById('email-filter-menu');") == 1
assert source.count("const folderMenu = document.getElementById('email-folder-menu');") == 1
- assert source.count("e.stopImmediatePropagation();") >= 2
- assert "_wireEmailPickerEscapeGuard();" in source
- assert "window.addEventListener('keydown'" in source
+ assert source.count("e.stopImmediatePropagation?.();") >= 2
+ assert "filterMenu?._dismiss?.();" in source
+ assert "folderMenu?._dismiss?.();" in source
+ escape_handler = source[source.index("state._libInnerEscHandler = (e) => {"):source.index("window.addEventListener('keydown', state._libInnerEscHandler, true)")]
+ assert "if (dismissTopMenu()) {" in escape_handler
+ assert "window.addEventListener('keydown', state._libInnerEscHandler, true)" in source
inbox = (ROOT / "static/js/emailInbox.js").read_text(encoding="utf-8")
assert re.search(r"from './emailLibrary\.js\?v=[A-Za-z0-9_-]+'", inbox)
diff --git a/tests/test_harness_version.py b/tests/test_harness_version.py
index d83c5fff0..999491e8f 100644
--- a/tests/test_harness_version.py
+++ b/tests/test_harness_version.py
@@ -5,10 +5,9 @@ import re
ROOT = Path(__file__).resolve().parents[1]
-def test_original_harness_version_is_canonical_semver() -> None:
+def test_harness_version_is_canonical_semver() -> None:
version = (ROOT / "HARNESS_VERSION").read_text(encoding="utf-8").strip()
- assert version == "0.20.18"
assert re.fullmatch(r"\d+\.\d+\.\d+", version)
diff --git a/tests/test_live_thinking_chat_integration.py b/tests/test_live_thinking_chat_integration.py
index ca6e1c864..e8636d310 100644
--- a/tests/test_live_thinking_chat_integration.py
+++ b/tests/test_live_thinking_chat_integration.py
@@ -145,15 +145,15 @@ def test_final_response_skips_duplicate_end_of_stream_render():
"// --- Final render (skip if stream was ever backgrounded or currently in background) ---",
"} // end if (!_isBgFinal)",
)
- assert "if (!terminalFinalResponseRendered) _renderStream();" in post_loop
+ assert "if (!terminalFinalResponseRendered && !_turnRendering.isVisible(roundHolder.querySelector('.body'), _terminalAnswerHtml(roundText, roundHolder.querySelector('.body')))) _renderStream();" in post_loop
assert "const finalDisplay = terminalFinalResponseRendered ? '' : _streamDisplayText" in post_loop
def test_only_rich_completed_turns_reload_saved_canonical_assistant_row():
assert "if (_canonicalTerminalSaved && _savedAssistantMessageId && !_pendingContinue)" not in _CHAT
assert "if (!_pendingContinue)" in _CHAT
- assert "fall back to its latest" in _CHAT
- assert ": [...history].reverse().find(msg => msg && msg.role === 'assistant');" in _CHAT
+ assert "if (!streamSessionId || !_savedAssistantMessageId) return false;" in _CHAT
+ assert "String(msg.metadata?._db_id || '') === _savedAssistantMessageId" in _CHAT
assert "const _needsCanonicalTurnRebuild" in _CHAT
assert "lastToolThread" in _CHAT
assert "_streamTurnMarker.remove();" in _CHAT
diff --git a/tests/test_llm_core_ollama_thinking.py b/tests/test_llm_core_ollama_thinking.py
index d7d1485ec..305cd6ac8 100644
--- a/tests/test_llm_core_ollama_thinking.py
+++ b/tests/test_llm_core_ollama_thinking.py
@@ -172,4 +172,4 @@ class TestThinkSuppression:
"qwen35-9b-tool-router-v4-firstaction-noschema-adapter",
)
assert "think" not in payload
- assert payload["max_tokens"] == 1024
+ assert payload["max_tokens"] == llm_core.LLMConfig.DEFAULT_MAX_TOKENS
diff --git a/tests/test_notes_mobile_checklist_toggle_static.py b/tests/test_notes_mobile_checklist_toggle_static.py
index 1ff792f38..eb8dfdaa9 100644
--- a/tests/test_notes_mobile_checklist_toggle_static.py
+++ b/tests/test_notes_mobile_checklist_toggle_static.py
@@ -87,6 +87,12 @@ def test_mobile_bulk_select_long_press_is_shared_across_card_types():
assert ".skill-card[data-skill-name]" in helper
assert ".task-card[data-id]" in helper
assert "const HOLD_MS = 450" in helper
- assert "body:has(#memory-select-btn.active)" in css
- assert "body:has(#skills-select-btn.active)" in css
- assert "body:has(#tasks-select-btn.active)" in css
+ shared_buttons = (
+ "#memory-select-btn,", "#skills-select-btn,",
+ "#notes-select-btn,", "#tasks-select-btn,",
+ )
+ shared_css = css.split("/* Shared bulk-selection trigger.", 1)[1]
+ selectors = re.findall(r":is\(([^)]*)\)(?:\.active)?::before", shared_css)
+ assert len(selectors) == 2
+ for selector in selectors:
+ assert all(button in selector for button in shared_buttons)
diff --git a/tests/test_notes_sidebar_new_note.py b/tests/test_notes_sidebar_new_note.py
index c60bbefb2..6caf9f9a3 100644
--- a/tests/test_notes_sidebar_new_note.py
+++ b/tests/test_notes_sidebar_new_note.py
@@ -9,7 +9,7 @@ def test_notes_sidebar_new_action_matches_library_structure_and_animation():
css = (ROOT / "static/style.css").read_text(encoding="utf-8")
assert 'class="list-item-plus-btn sidebar-new-item-btn" id="notes-new-note-btn"' in html
- assert 'note' in html
+ assert 'new' in html[html.index('id="notes-new-note-btn"'):]
assert "#tool-notes-btn:hover #notes-new-note-btn" in css
assert "#notes-new-note-btn:hover svg" in css
@@ -72,7 +72,7 @@ def test_notes_body_and_tag_arrow_are_nudged_down():
assert css.count("padding: 10px 8px 8px") >= 2
assert ".notes-pane-body .doclib-chip-scroll-arrow.right" in css
- assert "top: calc(50% - 2px) !important" in css
+ assert "top: calc(50% - 6px) !important" in css[css.index(".notes-pane-body .doclib-chip-scroll-arrow.right"):]
def test_notes_tag_strip_cannot_grow_into_blank_space():
diff --git a/tests/test_pr6020_rebase_regressions.py b/tests/test_pr6020_rebase_regressions.py
index cacd0ebc0..c46669a27 100644
--- a/tests/test_pr6020_rebase_regressions.py
+++ b/tests/test_pr6020_rebase_regressions.py
@@ -15,6 +15,11 @@ NOTES_TOOLS = {
"ask_user",
"update_plan",
}
+GENERAL_COMPACT_TOOLS = {"ask_user", "web_search", "web_fetch"}
+CALENDAR_COMPACT_TOOLS = {
+ "ask_user", "bash", "get_workspace", "manage_calendar", "manage_notes",
+ "python", "read_file", "web_fetch", "web_search",
+}
def _collect(generator):
@@ -102,9 +107,7 @@ def test_odysseus_notes_mode_clamps_without_overriding_caller_denials(monkeypatc
assert {"manage_notes", "manage_calendar", "manage_tasks"} <= route["disabled_tools"]
-def test_odysseus_general_mode_disables_every_tool(monkeypatch):
- from src.tool_policy import known_tool_names
-
+def test_odysseus_general_mode_uses_compact_core(monkeypatch):
prompt_calls, _ = _install_route_probe(monkeypatch)
_run_probe(
@@ -113,11 +116,10 @@ def test_odysseus_general_mode_disables_every_tool(monkeypatch):
)
route = prompt_calls[0]
- assert route["relevant_tools"] == set()
- assert known_tool_names() <= route["disabled_tools"]
+ assert route["relevant_tools"] == GENERAL_COMPACT_TOOLS
-def test_odysseus_calendar_intent_uses_notes_mode(monkeypatch):
+def test_odysseus_calendar_intent_uses_compact_calendar_route(monkeypatch):
prompt_calls, _ = _install_route_probe(monkeypatch)
_run_probe(
@@ -125,10 +127,10 @@ def test_odysseus_calendar_intent_uses_notes_mode(monkeypatch):
relevant_tools={"manage_notes", "manage_calendar", "manage_tasks", "bash"},
)
- assert prompt_calls[0]["relevant_tools"] == NOTES_TOOLS
+ assert prompt_calls[0]["relevant_tools"] == CALENDAR_COMPACT_TOOLS
-def test_odysseus_calendar_followup_keeps_notes_mode(monkeypatch):
+def test_odysseus_calendar_followup_keeps_compact_calendar_route(monkeypatch):
prompt_calls, _ = _install_route_probe(monkeypatch)
messages = [
{"role": "user", "content": "Add lunch tomorrow to my calendar."},
@@ -153,7 +155,7 @@ def test_odysseus_calendar_followup_keeps_notes_mode(monkeypatch):
relevant_tools={"manage_notes", "manage_calendar", "manage_tasks", "bash"},
)
- assert prompt_calls[0]["relevant_tools"] == NOTES_TOOLS
+ assert prompt_calls[0]["relevant_tools"] == CALENDAR_COMPACT_TOOLS
def test_agent_route_passes_workspace_to_system_prompt(monkeypatch):
@@ -169,7 +171,7 @@ def test_agent_route_passes_workspace_to_system_prompt(monkeypatch):
assert prompt_calls[0]["workspace"] == "/tmp/example-repo"
-def test_odysseus_qwen_temperature_is_capped_for_agent_requests(monkeypatch):
+def test_odysseus_compact_primary_uses_deterministic_temperature(monkeypatch):
_, stream_calls = _install_route_probe(monkeypatch)
_run_probe(
@@ -178,7 +180,7 @@ def test_odysseus_qwen_temperature_is_capped_for_agent_requests(monkeypatch):
temperature=1.2,
)
- assert stream_calls[0]["temperature"] == 0.2
+ assert stream_calls[0]["temperature"] == 0.0
def test_qwen_fallback_candidate_gets_capped_temperature(monkeypatch):
@@ -214,7 +216,7 @@ def test_non_qwen_fallback_keeps_requested_temperature(monkeypatch):
fallbacks=[("https://backup.example/v1", "gpt-4o", {})],
)
- assert stream_calls[0]["temperature"] == 0.2
+ assert stream_calls[0]["temperature"] == 0.0
factory = stream_calls[0]["candidate_request_factory"]
request = asyncio.run(factory(1, "https://backup.example/v1", "gpt-4o", {}))
assert request["kwargs"]["temperature"] == 1.2
diff --git a/tests/test_research_history_expansion.py b/tests/test_research_history_expansion.py
index 948dbdaca..d1c692f17 100644
--- a/tests/test_research_history_expansion.py
+++ b/tests/test_research_history_expansion.py
@@ -1,6 +1,7 @@
"""Regression coverage for full-height Deep Research history expansion."""
from pathlib import Path
+import re
ROOT = Path(__file__).resolve().parent.parent
@@ -11,7 +12,11 @@ def test_history_list_participates_in_shared_library_expansion_layout():
css = (ROOT / "static/style.css").read_text(encoding="utf-8")
assert 'id="research-past-list" class="doclib-grid memory-list research-jobs-list"' in panel
- assert "width:min(560px, 90vw);max-height:78vh" in panel
+ card = css[css.index("#research-pane .research-history-card {"):]
+ card = card[:card.index("}")]
+ assert "flex: 1 1 0;" in card
+ assert "min-height: 0;" in card
+ assert "overflow: hidden;" in card
assert 'class="memory-tab-panel research-tab-panel"' in panel
assert 'class="admin-card research-new-job"' in panel
assert 'id="research-history-filters" class="skills-summary-strip"' in panel
@@ -21,7 +26,11 @@ def test_history_list_participates_in_shared_library_expansion_layout():
assert "pane.classList.toggle('research-results-view', tab !== 'research')" in panel
assert "display: flex; flex-direction: column; gap: 10px;" in css
assert ".research-tabs {\n flex: 0 0 auto;\n margin: -4px -4px 0;" in css
- assert "font-size:11px; letter-spacing:0;" in css
+ title = css[css.index("#research-pane .research-history-title-row h2 {"):]
+ title = title[:title.index("}")]
+ assert "letter-spacing: 0;" in title
+ desc = css[css.index("#research-pane .research-history-desc {"):]
+ assert "font-size: 11px;" in desc[:desc.index("}")]
assert "font: inherit; font-size: 12px; cursor: pointer; text-align: left;" in css
assert "#research-pane .research-history-search-wrap .memory-search-input" in css
assert "height: 30px;" in css
@@ -65,8 +74,8 @@ def test_research_panel_uses_one_versioned_module_instance():
renderer = (ROOT / "static/js/chatRenderer.js").read_text(encoding="utf-8")
index = (ROOT / "static/index.html").read_text(encoding="utf-8")
- version = "20260902researchhistoryalign34"
- assert f"research/panel.js?v={version}" in app
- assert renderer.count(f"research/panel.js?v={version}") == 2
- assert f"style.css?v={version}" in index
- assert f"app.js?v={version}" in index
+ panel_version = re.search(r"research/panel\.js\?v=([^'\"]+)", app)
+ asset_version = re.search(r"/static/app\.js\?v=([^'\"]+)", index)
+ style_version = re.search(r"/static/style\.css\?v=([^'\"]+)", index)
+ assert panel_version and renderer.count(f"research/panel.js?v={panel_version.group(1)}") == 2
+ assert asset_version and style_version and asset_version.group(1) == style_version.group(1)
diff --git a/tests/test_research_panel_ui.py b/tests/test_research_panel_ui.py
index 0cfa5a4e3..304b273a7 100644
--- a/tests/test_research_panel_ui.py
+++ b/tests/test_research_panel_ui.py
@@ -25,18 +25,18 @@ def test_research_settings_use_custom_pickers_and_shared_provider_icons():
assert "provider === 'google' ? 'google_pse'" in icons
first_picker_offset = style.split(".research-setting:has(#research-rounds),", 1)[1].split("}", 1)[0]
assert ".research-setting:has(#research-category)" in first_picker_offset
- assert "top: -4px;" in first_picker_offset
+ assert "top: 0;" in first_picker_offset
def test_completed_research_keeps_primary_and_utility_actions_visible_without_format_icon():
panel = (ROOT / "static/js/research/panel.js").read_text(encoding="utf-8")
assert "research-job-format-icon" not in panel
- assert "Visual Report" in panel
- assert "Discuss" in panel
- assert 'data-action="copy" title="Copy report to clipboard"' in panel
- assert 'data-action="dismiss" title="Clear from list"' in panel
- assert 'data-action="delete" title="Delete from disk"' in panel
+ assert 'data-action="report" title="Open visual report"' in panel
+ assert 'data-action="chat" title="Open follow-up chat with this research as context"' in panel
+ assert "{ action: 'copy', icon: _copyIcon, label: 'Copy report' }" in panel
+ assert "{ action: 'dismiss', icon: _cancelIcon, label: 'Hide from list' }" in panel
+ assert "{ action: 'delete', icon: _trashIcon, label: 'Delete from disk', danger: true }" in panel
def test_research_format_survives_live_and_reconnected_jobs():
diff --git a/tests/test_tasks_completed_default_static.py b/tests/test_tasks_completed_default_static.py
index ae37bdedc..44ae48fac 100644
--- a/tests/test_tasks_completed_default_static.py
+++ b/tests/test_tasks_completed_default_static.py
@@ -40,7 +40,8 @@ def test_tasks_filter_chips_include_active_paused_switch():
assert "mkChip(`active (${activeCount})`, 'active', _taskStatusFilter === 'active', 'status');" in src
assert "mkChip(`paused (${pausedCount})`, 'paused', _taskStatusFilter === 'paused', 'status');" in src
assert "if (_taskStatusFilter && String(t.status || '').toLowerCase() !== _taskStatusFilter) return false;" in src
- assert "if (value === null) _taskStatusFilter = null;" in src
+ assert "_taskStatusFilter = _taskStatusFilter === value ? null : value;" in src
+ assert "_taskStatusFilter = null;" in src[src.index("if (kind === 'status')"):src.index("_renderList();", src.index("if (kind === 'status')"))]
def test_tasks_completed_view_exposes_active_paused_shortcuts():
diff --git a/tests/test_tool_approval_frontend_routing.py b/tests/test_tool_approval_frontend_routing.py
index ecf7f3b33..8b2f66d9a 100644
--- a/tests/test_tool_approval_frontend_routing.py
+++ b/tests/test_tool_approval_frontend_routing.py
@@ -155,12 +155,12 @@ def test_every_changed_approval_module_is_cache_busted_together():
assert found, f"missing cache-busted reference for {module_name}"
assert len(set(found)) == 1, f"split module graph for {module_name}: {found}"
- # These shared modules are imported throughout the graph. Keep their URL
- # canonical and unversioned; mixing a query URL with plain relative imports
- # creates a second singleton with separate state and listeners.
- for module_name in ("sessions.js", "ui.js", "memory.js", "markdown.js", "models.js"):
+ # Shared modules must have one URL apiece. ui.js is consistently versioned
+ # throughout the graph; the other shared modules remain unversioned.
+ for module_name in ("sessions.js", "memory.js", "markdown.js", "models.js"):
assert any(module_name in source for source in sources)
assert not versions(module_name), f"split module graph for {module_name}"
+ assert len(set(versions("ui.js"))) == 1
compare_stream = (root / "static/js/compare/stream.js").read_text(encoding="utf-8")
compare_vote = (root / "static/js/compare/vote.js").read_text(encoding="utf-8")