From 0dadb16f746b080f40a957b4a211bbeb2e8d66da Mon Sep 17 00:00:00 2001 From: Thomas Date: Wed, 23 Sep 2026 14:00:41 -0400 Subject: [PATCH 1/2] Removed duplicate tests --- docs/tests.md | 11 ++---- package.json | 3 +- tests/backend/test_server_baseline.py | 46 ------------------------- tests/backend/test_services.py | 24 +++++++------- tests/frontend/js_syntax_check.cjs | 48 --------------------------- 5 files changed, 15 insertions(+), 117 deletions(-) delete mode 100644 tests/frontend/js_syntax_check.cjs diff --git a/docs/tests.md b/docs/tests.md index 4f37e2f1..b9282726 100644 --- a/docs/tests.md +++ b/docs/tests.md @@ -19,19 +19,13 @@ installation. These fixtures never download or launch real GPU binaries. npm test ``` -Runs the full frontend suite in three stages: `test:unit` (JavaScript syntax checks, fast Node unit tests, structural flag-definition validation, and module loading/contract checks), `test:flags` (flag compatibility against real binaries), and `test:frontend` (the Playwright browser suite). +Runs the full frontend suite in three stages: `test:unit` (fast Node unit tests, structural flag-definition validation, and module loading/contract checks), `test:flags` (flag compatibility against real binaries), and `test:frontend` (the Playwright browser suite). ```powershell npm run test:unit ``` -Runs the aggregate fast stage only — every Node unit suite plus `test:syntax`, `test:frontend:modules`, and `test:flag-definitions` — with no browser launch and no llama.cpp binary required. Use this as the inner loop; `test:flags` and `test:frontend` remain separate explicit stages, and `npm test` is the full gate. - -```powershell -npm run test:syntax -``` - -Checks every frontend JavaScript file with `node --check`. +Runs the aggregate fast stage only — every Node unit suite plus `test:frontend:modules` and `test:flag-definitions` — with no browser launch and no llama.cpp binary required. Use this as the inner loop; `test:flags` and `test:frontend` remain separate explicit stages, and `npm test` is the full gate. ```powershell npm run test:frontend:modules @@ -199,7 +193,6 @@ Fast Node tests: - `flag_definitions_unit.cjs`: structural validation of flag/category definitions and representative invalid cases, plus reference-identity checks for the shared chat-template, reasoning-format, and main/draft KV-cache option arrays. - `llama_flags_runner_unit.cjs`: deterministic binary-selection fixtures, required-build failures, no fallback from explicit paths, custom-backend discovery, failed/timed-out help, negated flags, and fork-only exclusions. - `llama_flags_supported_unit.cjs`: compares GUI flags against real `llama-server` / `llama-cli` help output. Flags marked `fork_only: true` are always exempt; flags marked `removed_in: "bNNNNN"` (upstream removals retained for older builds) are exempt when the probed binary reports a build at or above that tag, parsed from `--version`. CI requires the pinned CPU binaries; optional local discovery can skip with a message. -- `js_syntax_check.cjs`: syntax-only check for frontend JavaScript. Browser smoke test: diff --git a/package.json b/package.json index a88650d4..3ad1e845 100644 --- a/package.json +++ b/package.json @@ -3,8 +3,7 @@ "private": true, "scripts": { "test": "npm run test:unit && npm run test:flags && npm run test:frontend", - "test:unit": "npm run test:syntax && node tests/frontend/custom_launch_args_unit.cjs && node tests/frontend/launch_args_unit.cjs && node tests/frontend/chat_template_selection_unit.cjs && node tests/frontend/output_cursor_unit.cjs && node tests/frontend/inference_stats_unit.cjs && node --test tests/frontend/memory_estimate_ui_unit.cjs tests/frontend/inference_polling_unit.cjs tests/frontend/process_output_unit.cjs && node --test tests/frontend/monitor_package_unit.cjs tests/frontend/monitor_system_cards_unit.cjs tests/frontend/monitor_gpu_cards_unit.cjs tests/frontend/monitor_inference_rendering_unit.cjs tests/frontend/monitor_terminal_runtime_unit.cjs tests/frontend/monitor_polling_unit.cjs tests/frontend/monitor_card_preferences_unit.cjs && node tests/frontend/process_lifecycle_unit.cjs && node tests/frontend/benchmark_args_unit.cjs && node tests/frontend/chat_rendering_unit.cjs && node tests/frontend/chat_ui_unit.cjs && node tests/frontend/chat_window_unit.cjs && node tests/frontend/chat_tools_unit.cjs && node tests/frontend/chat_compaction_unit.cjs && node tests/frontend/character_cards_unit.cjs && node tests/frontend/sampler_presets_unit.cjs && node tests/frontend/presets_unit.cjs && node tests/frontend/preset_roving_focus_unit.cjs && node tests/frontend/model_switch_ui_unit.cjs && node tests/frontend/hf_download_ui_unit.cjs && node tests/frontend/remote_tunnel_ui_unit.cjs && node tests/frontend/api_tab_unit.cjs && node tests/frontend/external_server_ui_unit.cjs && node tests/frontend/shared_services_unit.cjs && node tests/frontend/manager_releases_unit.cjs && node tests/frontend/manager_lifecycle_unit.cjs && node tests/frontend/manager_model_cache_unit.cjs && node tests/frontend/manager_model_dir_unit.cjs && node tests/frontend/theme_ui_unit.cjs && npm run test:frontend:modules && npm run test:flag-definitions", - "test:syntax": "node tests/frontend/js_syntax_check.cjs", + "test:unit": "node tests/frontend/custom_launch_args_unit.cjs && node tests/frontend/launch_args_unit.cjs && node tests/frontend/chat_template_selection_unit.cjs && node tests/frontend/output_cursor_unit.cjs && node tests/frontend/inference_stats_unit.cjs && node --test tests/frontend/memory_estimate_ui_unit.cjs tests/frontend/inference_polling_unit.cjs tests/frontend/process_output_unit.cjs && node --test tests/frontend/monitor_package_unit.cjs tests/frontend/monitor_system_cards_unit.cjs tests/frontend/monitor_gpu_cards_unit.cjs tests/frontend/monitor_inference_rendering_unit.cjs tests/frontend/monitor_terminal_runtime_unit.cjs tests/frontend/monitor_polling_unit.cjs tests/frontend/monitor_card_preferences_unit.cjs && node tests/frontend/process_lifecycle_unit.cjs && node tests/frontend/benchmark_args_unit.cjs && node tests/frontend/chat_rendering_unit.cjs && node tests/frontend/chat_ui_unit.cjs && node tests/frontend/chat_window_unit.cjs && node tests/frontend/chat_tools_unit.cjs && node tests/frontend/chat_compaction_unit.cjs && node tests/frontend/character_cards_unit.cjs && node tests/frontend/sampler_presets_unit.cjs && node tests/frontend/presets_unit.cjs && node tests/frontend/preset_roving_focus_unit.cjs && node tests/frontend/model_switch_ui_unit.cjs && node tests/frontend/hf_download_ui_unit.cjs && node tests/frontend/remote_tunnel_ui_unit.cjs && node tests/frontend/api_tab_unit.cjs && node tests/frontend/external_server_ui_unit.cjs && node tests/frontend/shared_services_unit.cjs && node tests/frontend/manager_releases_unit.cjs && node tests/frontend/manager_lifecycle_unit.cjs && node tests/frontend/manager_model_cache_unit.cjs && node tests/frontend/manager_model_dir_unit.cjs && node tests/frontend/theme_ui_unit.cjs && npm run test:frontend:modules && npm run test:flag-definitions", "test:frontend:modules": "node tests/frontend/module_namespace_unit.cjs", "test:flag-definitions": "node tests/frontend/flag_definitions_unit.cjs", "test:flags": "node tests/frontend/llama_flags_runner_unit.cjs && node tests/frontend/llama_flags_supported_unit.cjs", diff --git a/tests/backend/test_server_baseline.py b/tests/backend/test_server_baseline.py index 7251e9b6..820fc702 100644 --- a/tests/backend/test_server_baseline.py +++ b/tests/backend/test_server_baseline.py @@ -58,11 +58,6 @@ def make_handler(self, origin="", referer="", host=""): handler.headers = headers return handler - def test_allows_localhost_origins(self): - for origin in ("http://127.0.0.1:5240", "http://localhost:5240"): - with self.subTest(origin=origin): - self.assertTrue(self.make_handler(origin=origin).is_safe_request_origin()) - def test_allows_active_tunnel_origin(self): server.set_remote_tunnel_state( status="running", @@ -97,22 +92,7 @@ def test_wildcard_bind_allows_same_port_ip_request_host_origin(self): finally: server.GUI_HOST = original_host - def test_rejects_unknown_origin(self): - handler = self.make_handler(origin="https://evil.example") - - self.assertFalse(handler.is_safe_request_origin()) - - def test_allows_requests_without_origin_or_referer(self): - self.assertTrue(self.make_handler().is_safe_request_origin()) - - def test_referer_must_start_with_allowed_origin(self): - allowed = self.make_handler(referer="http://127.0.0.1:5240/index.html") - denied = self.make_handler(referer="http://127.0.0.1.evil.example:5240/") - prefix_bypass = self.make_handler(referer="http://localhost:5240@evil.example/") - self.assertTrue(allowed.is_safe_request_origin()) - self.assertFalse(denied.is_safe_request_origin()) - self.assertFalse(prefix_bypass.is_safe_request_origin()) class HandlerResponseTests(ServerStateIsolationMixin, unittest.TestCase): @@ -730,32 +710,6 @@ def test_remote_tunnel_log_is_truncated_in_state(self): class ValidationTests(ServerStateIsolationMixin, unittest.TestCase): - def test_hf_repo_id_validation(self): - self.assertEqual(server.validate_hf_repo_id("owner/model"), "owner/model") - - for value in ("", "owner", "../model", "owner/model.", "owner//model"): - with self.subTest(value=value): - with self.assertRaises(ValueError): - server.validate_hf_repo_id(value) - - def test_hf_revision_validation_defaults_and_rejects_traversal(self): - self.assertEqual(server.validate_hf_revision(""), "main") - self.assertEqual(server.validate_hf_revision("refs/pr/1"), "refs/pr/1") - - for value in ("/main", r"main\bad", "refs/../main", "bad\x00name"): - with self.subTest(value=value): - with self.assertRaises(ValueError): - server.validate_hf_revision(value) - - def test_hf_filename_validation_accepts_safe_gguf_paths(self): - self.assertEqual(server.validate_hf_filename("Q4/model.gguf"), "Q4/model.gguf") - - def test_hf_filename_validation_rejects_unsafe_names(self): - for value in ("", "/model.gguf", "../model.gguf", "model.bin", "bad:name.gguf", "bad\x00name.gguf"): - with self.subTest(value=value): - with self.assertRaises(ValueError): - server.validate_hf_filename(value) - def test_parse_port_defaults_for_invalid_values(self): self.assertEqual(server.parse_port("1234"), 1234) self.assertEqual(server.parse_port("0"), 8080) diff --git a/tests/backend/test_services.py b/tests/backend/test_services.py index c03d5523..bfcdd15c 100644 --- a/tests/backend/test_services.py +++ b/tests/backend/test_services.py @@ -58,16 +58,6 @@ def read(self, size=-1): return chunk -class FakeDownloadResponseTests(unittest.TestCase): - def test_read_respects_requested_size(self): - resp = FakeDownloadResponse([b"abc", b"def"]) - - self.assertEqual(resp.read(2), b"ab") - self.assertEqual(resp.read(3), b"cde") - self.assertEqual(resp.read(10), b"f") - self.assertEqual(resp.read(10), b"") - - class LocalLlamaHttpTests(unittest.TestCase): @staticmethod def make_response(body, content_type="", status=200): @@ -4000,8 +3990,10 @@ def test_rejects_no_slash(self): hf_service.validate_hf_repo_id("ownermodel") def test_rejects_double_dots(self): - with self.assertRaises(ValueError): - hf_service.validate_hf_repo_id("owner/..model") + for value in ("owner/..model", "../model"): + with self.subTest(value=value): + with self.assertRaises(ValueError): + hf_service.validate_hf_repo_id(value) def test_rejects_trailing_dot(self): with self.assertRaises(ValueError): @@ -4036,6 +4028,10 @@ def test_rejects_null_byte(self): with self.assertRaises(ValueError): hf_service.validate_hf_filename("mod\x00el.gguf") + def test_rejects_colon(self): + with self.assertRaises(ValueError): + hf_service.validate_hf_filename("bad:name.gguf") + def test_rejects_windows_reserved_device_names(self): for name in ( "con.gguf", @@ -4078,6 +4074,10 @@ def test_rejects_traversal(self): with self.assertRaises(ValueError): hf_service.validate_hf_revision("refs/../main") + def test_rejects_null_byte(self): + with self.assertRaises(ValueError): + hf_service.validate_hf_revision("bad\x00name") + class WebSearchDirectTests(unittest.TestCase): def test_web_search_rejects_empty_query_without_importing_ddgs(self): diff --git a/tests/frontend/js_syntax_check.cjs b/tests/frontend/js_syntax_check.cjs deleted file mode 100644 index 792708fd..00000000 --- a/tests/frontend/js_syntax_check.cjs +++ /dev/null @@ -1,48 +0,0 @@ -const { spawnSync } = require("node:child_process"); -const fs = require("node:fs"); -const path = require("node:path"); - -const ROOT = path.resolve(__dirname, "..", ".."); -const TARGET_DIRS = [ - path.join(ROOT, "ui", "js"), - path.join(ROOT, "tests", "frontend"), -]; - -function collectJsFiles(dir) { - const files = []; - for (const entry of fs.readdirSync(dir, { withFileTypes: true })) { - const fullPath = path.join(dir, entry.name); - if (entry.isDirectory()) { - files.push(...collectJsFiles(fullPath)); - } else if (entry.isFile() && /\.(?:js|cjs)$/.test(entry.name)) { - files.push(fullPath); - } - } - return files; -} - -const files = TARGET_DIRS.flatMap(collectJsFiles).sort(); -const failures = []; - -for (const file of files) { - const result = spawnSync(process.execPath, ["--check", file], { - cwd: ROOT, - encoding: "utf8", - }); - if (result.status !== 0) { - failures.push({ - file: path.relative(ROOT, file), - output: (result.stderr || result.stdout || "").trim(), - }); - } -} - -if (failures.length) { - for (const failure of failures) { - console.error(`Syntax check failed: ${failure.file}`); - console.error(failure.output); - } - process.exit(1); -} - -console.log(`syntax check passed for ${files.length} frontend JavaScript files`); From 5b4e02fa5a4f162d1edc9bafd118b213db4bbd62 Mon Sep 17 00:00:00 2001 From: Thomas Date: Wed, 23 Sep 2026 15:22:33 -0400 Subject: [PATCH 2/2] Refactor frontend state and launch configuration handling --- .github/workflows/docs.yml | 30 + backend/app.py | 5 - backend/routing.py | 16 +- docs/chat-popout-capabilities.md | 39 +- docs/chat-popout.md | 2 +- docs/directory.md | 2 +- docs/tests.md | 16 +- package.json | 2 +- tests/backend/test_routing.py | 30 +- tests/backend/test_server_baseline.py | 64 +- tests/backend/test_system_stats.py | 2 +- tests/frontend/chat_popout_capabilities.cjs | 246 - .../chat_popout_capability_fixture.cjs | 378 -- tests/frontend/chat_window_unit.cjs | 1456 +++--- tests/frontend/flag_sync_smoke.cjs | 4346 +++++++++-------- 15 files changed, 3041 insertions(+), 3593 deletions(-) create mode 100644 .github/workflows/docs.yml delete mode 100644 tests/frontend/chat_popout_capabilities.cjs delete mode 100644 tests/frontend/chat_popout_capability_fixture.cjs diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 00000000..b05ab502 --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,30 @@ +name: Documentation + +on: + push: + branches: [main, master] + paths: + - '*.md' + - 'docs/**' + - 'tests/backend/test_docs_*.py' + - '.github/workflows/docs.yml' + pull_request: + branches: [main, master] + paths: + - '*.md' + - 'docs/**' + - 'tests/backend/test_docs_*.py' + - '.github/workflows/docs.yml' + +jobs: + docs: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: '3.13' + cache: pip + cache-dependency-path: requirements.txt + - run: pip install -r requirements.txt + - run: python -m unittest tests.backend.test_docs_links tests.backend.test_docs_sync -v diff --git a/backend/app.py b/backend/app.py index 4543027d..a8d646bc 100644 --- a/backend/app.py +++ b/backend/app.py @@ -830,11 +830,6 @@ def dispatch_api_request(self, method, parsed, body=None): else: self.send_error(404) return - if isinstance(match.handler, str): - handler = getattr(self, match.handler) - handler(parsed, body, dict(match.params)) - return - request = Request( method=method, path=path, diff --git a/backend/routing.py b/backend/routing.py index 1ba7e074..4cae08e1 100644 --- a/backend/routing.py +++ b/backend/routing.py @@ -1,26 +1,20 @@ """Small route registry used by the stdlib HTTP entrypoint.""" from dataclasses import dataclass -from typing import Any, Dict, Mapping, Optional, Tuple +from typing import Callable, Dict, Mapping, Optional, Tuple @dataclass(frozen=True) class RouteMatch: - handler: Any + handler: Callable params: Mapping[str, str] - @property - def handler_name(self) -> str: - if isinstance(self.handler, str): - return self.handler - return getattr(self.handler, "__name__", str(self.handler)) - @dataclass(frozen=True) class _Route: method: str path: str - handler: Any + handler: Callable param_name: Optional[str] = None is_prefix: bool = False @@ -37,7 +31,7 @@ def __init__(self) -> None: self._exact: Dict[Tuple[str, str], RouteMatch] = {} self._prefixes: list[_Route] = [] - def add(self, method: str, path: str, handler: Any) -> "Router": + def add(self, method: str, path: str, handler: Callable) -> "Router": self._exact[(method.upper(), path)] = RouteMatch(handler, {}) return self @@ -45,7 +39,7 @@ def add_prefix( self, method: str, prefix: str, - handler: Any, + handler: Callable, param_name: Optional[str] = None, ) -> "Router": self._prefixes.append( diff --git a/docs/chat-popout-capabilities.md b/docs/chat-popout-capabilities.md index 48ac5a0c..1564ed8d 100644 --- a/docs/chat-popout-capabilities.md +++ b/docs/chat-popout-capabilities.md @@ -1,15 +1,15 @@ # Chat pop-out Phase 1 capability findings Date: 2026-09-11 -Status: Phase 1 fixture evidence (2026-09-11). The production pop-out in `ui/js/chat-window/` is covered in [Chat in a separate window](chat-popout.md) and [Tests](tests.md); this fixture itself implements no production behavior. +Status: Historical Phase 1 evidence. The synthetic fixture was retired on 2026-09-23; +its recorded browser and native findings remain below. Current production behavior +and automated checks are documented in [Chat in a separate window](chat-popout.md) +and [Tests](tests.md). -The disposable check is run with: - -```powershell -node tests/frontend/chat_popout_capabilities.cjs -``` - -The runner launches Playwright Chromium with fresh browser contexts and an ephemeral HTTP server. The server serves only the synthetic fixture page. It does not serve `ui/`, read or write `llama_gui_conversations`, accept chat requests, or expose any `/api/*` route. Each scenario uses a unique nonce and the only storage key it may create is `chat-popout-capability:`. Closing the context removes that disposable state. +The retired probe used fresh Chromium contexts and an ephemeral server serving a +synthetic page, without application code, real chat history, or backend routes. +The following fixture findings describe that 2026-09-11 experiment, not a current +production regression suite. ## Evidence from the loopback fixture @@ -36,29 +36,6 @@ Every fallback case uses a fresh browser context and keeps the source page recov The blocked-popup, blocked-storage, missing-Web-Locks, and missing-opener conditions are controlled browser-context simulations. The normal popup, opener reference, same-context storage sharing, Web Locks contention, secure-context result, and close checks run against Chromium itself. Native window activation is not asserted by the headless fixture. A real embedded host that strips or changes these capabilities still needs a native check. -## Bootstrap and request-setting audit - -The current frontend is a strict ordered global-script page. `app.js` is not a safe popup entry point to run wholesale: - -- Before `DOMContentLoaded`, it selects `llama-server`, replaces `flagCore` values with defaults, creates the shared inference-stat engine, and configures API, sampler-preset, preset, tunnel, external-server, Hugging Face, Quick Launch, benchmark, Monitor, process-lifecycle, and Model Switcher modules. It also subscribes to lifecycle snapshots. -- Its `DOMContentLoaded` handler initializes the theme, tabs, tool and Configure controls, installation/API/preset/Quick Launch/Chat/benchmark/Monitor flows, model-directory controls, release fetching, command preview, endpoint rendering, launch/stop actions, polling visibility handlers, and the remaining page wiring. -- `chatUi.configure()` is a top-level dependency injection call near the end of `app.js`; `initChatTab()` only calls `chatUi.init()`. Loading the full application in a popup would therefore risk a second configuration authority, lifecycle controller, release fetch, and polling cycle. - -The smallest popup bootstrap must retain the ordered dependencies required by the Chat package (`ui/js/chat/`) (`flagCore`, `chatTools`, `app-data`, `chat-rendering`, `chat-compaction`, and `character-cards`) and provide the existing Chat DOM/dependency boundary. It should inject a verified host adapter for settings, runtime identity/status, authorization behavior, and the existing baseline helper. The popup must not initialize its own lifecycle controller or external-server restore. This is an audit result for the next phase; it does not add a popup module or alter the application bootstrap. - -`buildChatBody(history, draft, includeUsage)` currently consumes these request inputs: - -- `#chat-system-prompt` and `chatTools.getInstructions()` for the system message. -- The complete history after `chatCompaction.workingMessages(...)`, passed through `chatTools.requestMessages(...)`, plus the trimmed draft as a final user message. -- The active model from the lifecycle snapshot first, then running status, then shared `flagCore` alias/selected model, with `local-model` as the last fallback. -- `stream: true` and, when requested, `stream_options.include_usage: true`. -- Shared `flagCore` sampler values: `temperature`, `top_p`, `top_k`, `min_p`, `repeat_penalty`, and `n_predict` mapped to `max_tokens` (with numeric normalization and the `-1` server-default sentinel omitted from `max_tokens`). -- `#chat-thinking-effort` (`auto`, `off`, `low`, `medium`, `high`, or `xhigh`). Non-auto values produce the top-level `reasoning_effort` and the compatibility `chat_template_kwargs`; `off` uses `none` and disables thinking. -- `#chat-web-search-toggle` and the clamped `#chat-web-search-max-results` value (1–10, default 5) when search is enabled. -- `chatTools.getDefinitions()` when the configured browser tools list is non-empty. - -Context-preview and compaction keys also include the active runtime, external target, template capabilities, and the same request body. A popup host adapter therefore needs notifications that invalidate stale previews when the main window changes runtime or shared sampler state. - ## Launch-environment limits The Phase 1 fixture evidence is limited to a normal Chromium browser using loopback HTTP and isolated synthetic contexts. The following capabilities are not established by that fixture; later native observations are recorded separately below: diff --git a/docs/chat-popout.md b/docs/chat-popout.md index 30808080..612c8e1d 100644 --- a/docs/chat-popout.md +++ b/docs/chat-popout.md @@ -40,4 +40,4 @@ Only pages running this implementation in the same browser storage partition par For a native launcher or browser check, use disposable test conversations and verify popup/opener retention, shared storage, focus, close/reload recovery, and Return/Send access with both panels and Context open. Check the launcher's embedded view separately from its external-browser link. Keep CI on Linux and Windows; native checks do not require adding macOS runners. -See [capability evidence](chat-popout-capabilities.md) and [test commands](tests.md). +See [historical capability evidence](chat-popout-capabilities.md) and [test commands](tests.md). diff --git a/docs/directory.md b/docs/directory.md index a83851c0..c60a1b58 100644 --- a/docs/directory.md +++ b/docs/directory.md @@ -1364,7 +1364,7 @@ Prefer `rg` for local search. On Windows/PowerShell, use patterns like `rg -n "p | `docs/security.md` | Security model, trust boundaries, and reporting guidance | | `docs/troubleshooting.md` | Common installation, launch, model, GPU, and connectivity problems | | `docs/chat-popout.md` | Chat pop-out user guide: transfer, recovery, and browser/launcher support | -| `docs/chat-popout-capabilities.md` | Phase 1 pop-out capability fixture evidence | +| `docs/chat-popout-capabilities.md` | Historical pop-out capability and native-browser evidence | | `docs/upstream-changes.md` | llama.cpp upstream changes needing coordinated GUI updates | | `docs/software-versioning-policy.md` | CalVer versioning and stable-release policy | | `docs/images/` | Screenshots used by README.md | diff --git a/docs/tests.md b/docs/tests.md index b9282726..10e7a070 100644 --- a/docs/tests.md +++ b/docs/tests.md @@ -7,6 +7,10 @@ This repo has two main test groups: The goal is not exhaustive coverage. Tests should make common regressions easier to diagnose, especially around shared launch state, command generation, route/service behavior, and UI helper logic. +The Documentation workflow runs the link and route-registry documentation checks on +Markdown and `docs/` changes, including docs-only pull requests. The full Tests +workflow retains its documentation exclusions and Linux/Windows matrix. + ## Common Commands `python -m unittest tests.backend.test_official_backends -v` (using the project @@ -62,7 +66,7 @@ npm run test:frontend Runs the Playwright smoke test for browser-level shared-state sync. This is also the only suite that can cover the Configure sampler preset panel, because `renderFlags()` destroys and rebuilds it — the `