Trước đây công tắc chỉ chặn tool mạng của agent; lệnh shell chỉ bị proxy
giả, còn M365, Teams, nút Test, MCP đang chạy, task script, link đính kèm
task, pip tự cài và tài nguyên web trong xem trước HTML vẫn ra mạng tự do.
- Cổng chung application/network/network_guard.py, nối vào cấu hình sống
ở Composition Root; nhà cung cấp AI (chat, danh sách model, thử model)
không đi qua cổng này.
- Lệnh shell của agent và task script chạy trong Windows AppContainer
không có quyền mạng (macOS: sandbox-exec, Linux: unshare --net);
không cô lập được thì từ chối chạy.
- Không cấp quyền kế thừa của AppContainer lên thư mục chứa PySide6:
Chromium không nạp được Qt6WebEngineCore.dll và tab Graph bị hỏng.
- Bật chặn thì dừng MCP đang chạy; tool OneDrive đồng bộ trên máy vẫn dùng.
- Mặc định tắt khi mở app lần đầu; nhãn và tooltip 3 ngôn ngữ cập nhật.
- Test: tests/test_network_guard_lanes.py (có bài AppContainer thật).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Thư mục làm việc vừa là sandbox (agent chỉ đọc/ghi bên trong nó) vừa là kho
kiến thức chung của project (file ở gốc được mọi đoạn chat tự đọc). Hai project
trỏ vào cùng một thư mục là đọc lẫn dữ liệu của nhau và ghi đè lên nhau — đúng
điều docstring đầu core/projects.py nói sandbox sinh ra để ngăn, nhưng không có
chỗ nào thực thi: cả ba nơi đặt thư mục đều gán thẳng output_dir rồi lưu.
Thêm core.projects.folder_conflict: so theo workspace_dir() chứ không theo
output_dir (project chưa đặt thư mục riêng vẫn đang chiếm thư mục quản lý sẵn),
chuẩn hoá đường dẫn bằng expanduser + abspath + normcase, và coi thư mục lồng
nhau là trùng — đứng ở thư mục cha vẫn với tới được file của project con.
Chặn tại cả ba nơi ghi output_dir: nút Đổi ở màn Project, thư mục mirror cloud,
và nút chọn thư mục trong tab Cowork. Chặn lúc CHỌN chứ không lúc Lưu, vì nút
Lưu chỉ ghi tên/mô tả/chỉ dẫn — chặn ở đó sẽ khoá luôn việc đổi tên một project
lỡ đang trùng thư mục.
Dữ liệu đã trùng sẵn không bị tự sửa: có nhãn cảnh báo ngay dưới ô Thư mục làm
việc, nói rõ trùng với project nào, để người dùng tự quyết.
Luật ở module riêng (presentation/workspace/project_folder_rules.py) vì nhét
vào project_editing.py sẽ đẩy file đó lên 435 dòng, vượt trần 400 của
scripts/check_loc.py.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Vào sub-tab Project khi đang mở một project thì biểu mẫu hiện đủ dữ liệu nhưng
nút "Sửa project" vẫn mờ, không bấm được.
Trạng thái bật/mờ của ba nút chỉ được tính trong set_project_editable, mà việc
đổi project không đi qua hàm đó — _load_current chỉ nạp lại biểu mẫu.
_sync_project_buttons có chạy khi đổi sub-tab nhưng chỉ chỉnh ẩn/hiện, nên nút
hiện ra mang theo trạng thái mờ tính từ lúc dựng cửa sổ, khi chưa project nào
được chọn.
Dồn phép tính về _sync_project_buttons — nơi duy nhất biết đủ cả ba yếu tố — và
cho nó chạy thêm khi project_selected phát tín hiệu.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Hàng "Cài đặt" là một QPushButton tự dựng icon + chữ chứ không phải hàng của
QTreeWidget như Dashboard/Giám sát, nên khoảng cách icon-chữ do layout đặt chứ
không do delegate của cây. Đặt _NAV_ROW_GAP + 4 (=10) ở đó, cộng 10px lề trái
và hộp icon 22px, đưa chữ ra x=42 trong khi delegate đặt chữ ở x=35 — thụt phải
7px, thấy rõ bằng mắt.
Lấy khe đó từ style (PM_FocusFrameHMargin + 1) thay vì tự đặt số, đúng khe mà
delegate dùng. Kèm test hồi quy đo bằng pixel trên cửa sổ thật, ép ba hàng về
cùng icon và cùng chữ để đo bố cục chứ không đo hình dáng glyph.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Copy of sections VI-IX from Feature_Architecture_Proposal.md
(roadmap, team assignment/KPI, anti-patterns, function migration map).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>