Feature/delta team/epic r04 #7

Merged
gitea-admin merged 67 commits from feature/delta-team/epic-R04 into main 2026-08-31 05:15:15 +00:00
24 changed files with 66 additions and 56 deletions
Showing only changes of commit be5c5747a9 - Show all commits
+7 -3
View File
@@ -34,10 +34,14 @@ jobs:
with:
python-version: "3.11"
cache: pip
cache-dependency-path: cowork_local/requirements-test.txt
cache-dependency-path: cowork_local/requirements.txt
- name: Install test dependencies
run: python -m pip install --disable-pip-version-check -r requirements-test.txt
# Mot file duy nhat: requirements-test.txt cu chi co pytest, nhung
# 64/108 file test dung widget that (20 file import PySide6 thang o dau
# file, khong co bao ve) nen no van phai keo ve gan nhu ca danh sach
# runtime. Cai rieng file kia thi pytest chet ngay luc thu thap test.
- name: Install dependencies
run: python -m pip install --disable-pip-version-check -r requirements.txt
- name: Check Python syntax
run: |
+1 -1
View File
@@ -48,7 +48,7 @@ Prefer the existing lightweight Conventional Commit prefixes: `feat:`, `fix:`, `
Run the application from the parent directory with `python -m cowork_local`. The current reliable test command is:
```bash
python -m pip install -r requirements-test.txt
python -m pip install -r requirements.txt
python -m pytest tests -q
```
+7 -1
View File
@@ -59,10 +59,16 @@ python -m cowork_local
### 3. Run Automated Tests
```bash
python -m pip install -r requirements-test.txt
python -m pip install -r requirements.txt
pytest -q
```
There is one requirements file, not a runtime/test pair. A separate test file
would hold only `pytest`: 64 of the 108 test modules build real widgets, and 20
of them import PySide6 unguarded at module scope, so it would have to pull in
almost the whole runtime list anyway — two files for one near-identical list is
just a second place for the pins to drift.
---
## 🛡️ CASAN Quality Gate & Verification
+20 -20
View File
@@ -27,30 +27,30 @@ _current = DEFAULT_LANGUAGE
_listeners: List[Callable[[], None]] = []
# key -> {"en": ..., "ja": ..., "vi": ...}
from . import i18n_login_dialog as _i18n_login_dialog
from . import i18n_sidebar as _i18n_sidebar
from . import i18n_composer as _i18n_composer
from . import i18n_hint as _i18n_hint
from . import i18n_cowork_tab as _i18n_cowork_tab
from . import i18n_settings_dialog as _i18n_settings_dialog
from . import i18n_skills_dialog as _i18n_skills_dialog
from . import i18n_libreoffice_view as _i18n_libreoffice_view
from . import i18n_agents_admin_tab as _i18n_agents_admin_tab
from . import i18n_monitoring_overview as _i18n_monitoring_overview
from . import login_dialog as _login_dialog
from . import sidebar as _sidebar
from . import composer as _composer
from . import hint as _hint
from . import cowork_tab as _cowork_tab
from . import settings_dialog as _settings_dialog
from . import skills_dialog as _skills_dialog
from . import libreoffice_view as _libreoffice_view
from . import agents_admin_tab as _agents_admin_tab
from . import monitoring_overview as _monitoring_overview
# Gộp theo đúng thứ tự cũ: khoá trùng thì cụm sau thắng, y như khi tất cả
# còn nằm chung một dict literal.
STRINGS: Dict[str, Dict[str, str]] = {
**_i18n_login_dialog.STRINGS,
**_i18n_sidebar.STRINGS,
**_i18n_composer.STRINGS,
**_i18n_hint.STRINGS,
**_i18n_cowork_tab.STRINGS,
**_i18n_settings_dialog.STRINGS,
**_i18n_skills_dialog.STRINGS,
**_i18n_libreoffice_view.STRINGS,
**_i18n_agents_admin_tab.STRINGS,
**_i18n_monitoring_overview.STRINGS,
**_login_dialog.STRINGS,
**_sidebar.STRINGS,
**_composer.STRINGS,
**_hint.STRINGS,
**_cowork_tab.STRINGS,
**_settings_dialog.STRINGS,
**_skills_dialog.STRINGS,
**_libreoffice_view.STRINGS,
**_agents_admin_tab.STRINGS,
**_monitoring_overview.STRINGS,
}
View File
View File
+1 -14
View File
@@ -4,7 +4,6 @@ rem Cowork-Local BamBOO - cai dat thu vien Python (chay MOT lan)
rem
rem Cach dung:
rem install.bat cai vao moi truong ao rieng (khuyen dung)
rem install.bat --dev cai them thu vien de chay test
rem install.bat --system cai thang vao Python dang co, khong dung venv
rem install.bat --force dung lai moi truong ao tu dau
rem
@@ -26,13 +25,11 @@ set "APPHOME=%LOCALAPPDATA%\CoworkLocal"
set "VENV=%APPHOME%\venv"
set "LAUNCHER=%APPHOME%\launcher"
set "DEV=0"
set "USE_SYSTEM=0"
set "FORCE=0"
:parse_args
if "%~1"=="" goto args_done
if /I "%~1"=="--dev" set "DEV=1" & shift & goto parse_args
if /I "%~1"=="--system" set "USE_SYSTEM=1" & shift & goto parse_args
if /I "%~1"=="--force" set "FORCE=1" & shift & goto parse_args
if /I "%~1"=="-h" goto usage
@@ -120,15 +117,6 @@ if errorlevel 1 (
goto fail
)
if "%DEV%"=="1" (
echo [3/5] Cài thêm thư viện chạy test ^(--dev^)...
%PIP% install --disable-pip-version-check -r "%REPO%\requirements-test.txt"
if errorlevel 1 (
echo [LỖI] Cài thư viện test thất bại.
goto fail
)
)
rem --------------------------------------------------------------------------
rem 4. Lien ket de goi import duoc dung ten
rem
@@ -188,9 +176,8 @@ exit /b 0
:usage
echo.
echo install.bat [--dev] [--system] [--force]
echo install.bat [--system] [--force]
echo.
echo --dev cài thêm thư viện để chạy test ^(pytest, pydantic^)
echo --system cài thẳng vào Python đang có, không tạo môi trường ảo
echo --force xoá môi trường ảo cũ rồi tạo lại từ đầu
echo.
-2
View File
@@ -1,2 +0,0 @@
pydantic>=2,<3
pytest>=8,<10
+9
View File
@@ -39,3 +39,12 @@ pywin32>=306; sys_platform == "win32" # Office -> PDF, thông báo Outlook
# opendataloader-pdf # bộ đọc PDF thay thế — KHÔNG cài sẵn có chủ ý:
# # application/workspaces/graph_index_service.py tự cài
# # khi cần, qua core/deps.py::ensure_module.
# --- Chạy test ---
# Gộp vào đây thay vì để riêng requirements-test.txt: file kia chỉ có đúng
# `pytest`, mà 64/108 file test dựng widget thật nên nó vẫn phải kéo về gần
# như toàn bộ danh sách trên. Hai file cho một danh sách gần trùng nhau chỉ
# tạo thêm một chỗ để lệch phiên bản.
#
# Người dùng cuối cài thừa pytest vài MB — đổi lại chỉ còn MỘT file phải nhớ.
pytest>=8,<10
+5 -1
View File
@@ -40,6 +40,10 @@ if hasattr(sys.stdout, "reconfigure"):
DEFAULT_TARGET_DIRS = [
"domain", "application", "infrastructure", "presentation",
"ui", "core", "providers", "security", "mcp_servers",
# ``i18n/`` và ``theme/`` từng là 13 file rời nằm thẳng ở thư mục gốc nên
# được quét theo diện "module gốc"; gom vào gói rồi thì phải khai ở đây,
# không thì chúng lặng lẽ tuột khỏi tầm quét.
"i18n", "theme",
]
DEFAULT_MAX_LINES = 400
@@ -60,7 +64,7 @@ SCAN_ROOT_MODULES = True
#: đúng là tách file.
LEGACY_ALLOWANCE = {
"ui/workspace_tab.py": 566,
"ui/widgets.py": 505,
"ui/widgets.py": 466,
"ui/task_editor_dialog.py": 627,
"ui/accounts_tab.py": 559,
"core/skills.py": 405,
+8 -6
View File
@@ -87,12 +87,14 @@ def source() -> dict[str, str]:
def test_template_exposes_exactly_three_provider_neutral_tools() -> None:
# The MCP SDK is a RUNTIME dependency (requirements.txt) and is deliberately
# absent from requirements-test.txt, which is all CI installs. Importing it at
# module scope aborted collection for the ENTIRE suite, so the guard lives here,
# inside the only test that touches the SDK. Guarding per-test rather than
# per-module keeps the other cases -- pure-Python contract checks that need no
# SDK -- running on CI instead of silently skipping with it.
# Importing the MCP SDK at module scope aborted collection for the ENTIRE
# suite whenever the SDK was missing, so the guard lives here, inside the only
# test that touches it. Guarding per-test rather than per-module keeps the
# other cases -- pure-Python contract checks that need no SDK -- running
# instead of silently skipping with it.
#
# ``mcp`` is in requirements.txt, so a correctly installed checkout runs this
# test for real; the guard only covers an environment installed by hand.
types = pytest.importorskip("mcp.types")
assert set(TOOL_NAMES) == EXPECTED_TOOLS
+2 -2
View File
@@ -34,10 +34,10 @@ original value, so the deviation is auditable rather than silent.
"""
from __future__ import annotations
from .theme_palettes import ( # noqa: F401 — giữ đường vào cũ
from .palettes import ( # noqa: F401 — giữ đường vào cũ
DARK, LIGHT, Palette, _chevron_asset, _FONT, _MONO, _PALETTES,
)
from .theme_qss import _TEMPLATE
from .qss import _TEMPLATE
from dataclasses import dataclass, asdict
from string import Template
+2 -2
View File
@@ -4,7 +4,7 @@ Tách khỏi ``theme.py`` vì nó là **dữ liệu**, không phải logic: mộ
``string.Template`` mà ``stylesheet()`` thay biến vào. Để chung thì mỗi lần
muốn sửa một hàm nhỏ trong theme.py lại phải cuộn qua 470 dòng CSS.
Sửa màu thì sang ``theme_palettes.py``; ở đây chỉ sửa hình dạng và khoảng cách.
Sửa màu thì sang ``theme/palettes.py``; ở đây chỉ sửa hình dạng và khoảng cách.
"""
from __future__ import annotations
@@ -12,7 +12,7 @@ from dataclasses import dataclass, asdict
from string import Template
from .theme_qss_controls import QSS_CONTROLS
from .qss_controls import QSS_CONTROLS
_QSS_SHELL = """
/* ---- reset ------------------------------------------------------------ */
@@ -1,10 +1,10 @@
"""Nửa sau của khuôn QSS: bề mặt, tab, ô nhập, nút, badge, log.
Cắt đôi khuôn QSS đúng mạch của chính nó: ``theme_qss.py`` giữ phần vỏ
Cắt đôi khuôn QSS đúng mạch của chính nó: ``theme/qss.py`` giữ phần vỏ
(reset + shell: thanh rail, khung chính), file này giữ phần điều khiển.
Hai nửa được nối lại trong ``theme_qss.py``.
Hai nửa được nối lại trong ``theme/qss.py``.
Sửa màu thì sang ``theme_palettes.py``; ở đây chỉ sửa hình dạng và khoảng cách.
Sửa màu thì sang ``theme/palettes.py``; ở đây chỉ sửa hình dạng và khoảng cách.
"""
from __future__ import annotations
+1 -1
View File
@@ -26,7 +26,7 @@ class SegmentedControl(QWidget):
currentIndexChanged = Signal(int)
#: Độ đậm mà ``theme_qss.py`` áp cho nút đang chọn
#: Độ đậm mà ``theme/qss.py`` áp cho nút đang chọn
#: (``QPushButton#segItem:checked { font-weight: 600 }``). Đổi ở QSS thì
#: phải đổi cả ở đây, nếu không chữ lại bị cắt.
_CHECKED_WEIGHT = QFont.DemiBold