feat(gates): thêm cổng CASAN thứ tư (Gate O) và mở cổng LOC ra cả cây mã

Gate O — module production phải có ít nhất một nơi import
---------------------------------------------------------
Ba cổng đang có đều không bắt được mã chết, đúng như 1.400 dòng ở commit
trước đã chứng minh. Gate O dựng đồ thị import bằng AST từ
`__init__`/`__main__`/`app`, theo cả import muộn trong thân hàm.

Hai ngoại lệ tự động để `ALLOWLIST` không phải chép lại cùng một lý do nhiều
lần: `__init__.py` của gói mà mọi thành viên đều dormant, và module chỉ được
chính mã dormant đã miễn trừ import.

Cổng cũng đếm tuổi 9 seam chưa nối dây (nhãn `SEAM · dựng <ngày>`) và nhắc
khi quá 30 ngày. Chỉ [WARN], không làm CI đỏ: để nó đỏ thì CI sẽ đỏ vào một
buổi sáng mà không ai sửa gì, và cách nhanh nhất để xanh lại là sửa ngày.

Cổng LOC — quét 366 file thay vì 191
------------------------------------
`DEFAULT_TARGET_DIRS` chỉ có 4 gói Clean Architecture, nên một file 944 dòng
trong `ui/` vẫn qua cổng. Nay quét cả `ui/`, `core/`, `providers/`,
`security/`, `mcp_servers/` và các module ở thư mục gốc.

18 file đã dài hơn 400 dòng từ trước nằm trong `LEGACY_ALLOWANCE` — bánh cóc
chỉ quay một chiều, và nó đo DÒNG MÃ chứ không đo dòng vật lý. Bánh cóc chỉ
hỏi một câu, "file này có đang để thêm việc vào không?", mà viết thêm một
docstring thì không. Đếm dòng vật lý ở đó biến cổng thành thứ phạt người viết
tài liệu, và cách dễ nhất để làm nó xanh lại sẽ là xoá bớt chú thích. Trần
400 vẫn đếm dòng vật lý — đó là hợp đồng đã chốt của cổng S.

CI
--
Ghim tên thư mục checkout là `cowork_local`: nhiều test characterization sinh
tiến trình con `python -c "from cowork_local... import ..."`, mà tiến trình
con chỉ import được khi trên sys.path có thư mục mang đúng tên gói. Checkout
vào thư mục tên khác làm 73 test đỏ vì lý do không liên quan tới mã.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-30 10:36:52 +09:00
co-authored by Claude Opus 5
parent a71085b39e
commit e5c184ce07
4 changed files with 602 additions and 30 deletions
+9 -1
View File
@@ -8,7 +8,8 @@ Verification Stages (CASAN):
1. [C] Clean Architecture Guard (scripts/check_imports.py)
2. [A] Atomic & Secrets Audit (scripts/audit_security.py)
3. [S] Single Responsibility / LOC Guard (scripts/check_loc.py)
4. [A/N] Automated Tests & No-Regression Suite (pytest)
4. [O] Orphan Module Guard (scripts/check_orphan_modules.py)
5. [A/N] Automated Tests & No-Regression Suite (pytest)
"""
from __future__ import annotations
@@ -90,6 +91,13 @@ def main() -> int:
"S - Single Responsibility LOC Limit (<= 400 LOC)",
[sys.executable, str(REPO_ROOT / "scripts" / "check_loc.py"), "--max-lines", "400"],
),
# Ba cổng trên không cổng nào bắt được mã chết: một file không ai import
# vẫn đúng chiều phụ thuộc, vẫn không có credential, vẫn dưới 400 dòng.
# Cổng O đi tìm đúng khoảng trống đó.
(
"O - Orphan Module Guard (moi module phai co noi import)",
[sys.executable, str(REPO_ROOT / "scripts" / "check_orphan_modules.py")],
),
]
if not args.skip_tests: