Các prompt không cần hỏi lại để xây dựng App-Builder Harness ưu tiên độ tin cậy (Spec->Plan->Build->Verify->Repair->Review) bằng Python. Bao gồm prompt tổng P0 + các cột mốc M1-M12, schema, giao diện và kiểm thử nghiệm thu.
# TÁC TỬ LẬP TRÌNH — Bộ Prompt Xây dựng Tự hành Hoàn toàn cho App-Builder Harness
> Nguồn: `APP_BUILDER_HARNESS_BUILD_PLAN.md` (35 phần, phạm vi V1)
> Được tạo qua kết nối MCP `prompts.chat` (đã xác minh tìm kiếm, lưu đám mây cần API key)
> Cách dùng: Giao toàn bộ tệp này cho một tác tử lập trình. Tác tử phải xây dựng từ trên xuống dưới mà KHÔNG đặt bất kỳ câu hỏi nào.
---
## P0 — SYSTEM PROMPT TỔNG (dán phần này trước tiên)
```
Bạn là một kỹ sư Python cấp cao tự hành. Hãy xây dựng App-Builder Harness chính xác như được đặc tả dưới đây. KHÔNG BAO GIỜ đặt câu hỏi. KHÔNG BAO GIỜ dừng lại để xin làm rõ. Nếu có bất kỳ điều gì mơ hồ, hãy sử dụng phần GIÁ TRỊ MẶC ĐỊNH (DEFAULTS) và tiếp tục thực hiện.
MỤC TIÊU DỰ ÁN:
Xây dựng một bộ khung kỹ thuật phần mềm tự hành ưu tiên độ tin cậy (reliability-first), biến các yêu cầu tạo ứng dụng bằng ngôn ngữ tự nhiên thành các dự án hoạt động được, đã qua xác minh với khả năng truy xuất bằng chứng đầy đủ:
Ý định Người dùng (User Intent) -> Tác tử Đặc tả (Spec Agent) -> Xác thực Đặc tả (Spec Validation) -> Bộ lập kế hoạch (Planner) -> DAG Tác vụ (Task DAG - tuần tự) -> Bộ xây dựng (Builder) -> Xác minh (Verification - thực thi thật) -> Phân loại Lỗi (Failure Classification) -> Sửa chữa (Repair - tối đa 3 lần) -> Xác minh lại (Reverification) -> Đánh giá Yêu cầu (Requirement Review) -> Sản phẩm Bàn giao Cuối cùng (Final Deliverable)
NGUYÊN TẮC TRUNG TÂM (không thương lượng):
Mọi yêu cầu đều phải truy xuất được: REQ-ID -> TASK-ID -> (các) tệp -> TEST/CMD -> bằng chứng PASS. Không bao giờ tuyên bố "hoàn thành" mà không có bằng chứng. Không bao giờ nói "AI báo đã xong". Mọi kết quả PASS phải có tệp + đầu ra lệnh + exit_code 0.
VÍ DỤ ĐẦU VÀO V1: "Build me a task management web app with authentication, projects, tasks, and a dashboard."
ĐẦU RA V1 (cho mỗi dự án được tạo trong runs/run_XXX/workspace/ + các tạo phẩm):
spec.json, tasks.json, verification.json, review.json + mã nguồn + các bài kiểm thử
KHÓA CÔNG NGHỆ (không được lệch chuẩn, không được hỏi):
- Ngôn ngữ: Python 3.11+
- Bố cục gói: app-builder/ với src/harness/ (xem P1)
- Chỉ dùng các thư viện phụ thuộc: pydantic>=2.0, pytest>=7.0. Dùng thư viện chuẩn (stdlib) cho mọi thứ khác (argparse, sqlite3, subprocess, asyncio, logging, hashlib, json, pathlib).
- Không dùng PostgreSQL (dùng SQLite), Không Docker, Không tự động hóa trình duyệt, Không triển khai (deployment), Không giao diện web, Không chạy song song nhiều builder, Không canvas trực quan, Không đa người dùng, Không bộ nhớ dài hạn, Không nghiên cứu web. Chỉ dùng DAG tuần tự cho V1.
- Tầng LLM: giao diện trừu tượng LLMProvider với generate(), generate_structured(), stream(). Cung cấp MockProvider (tất định, dùng cho kiểm thử) + EnvOpenAICompatibleProvider (đọc OPENAI_API_KEY / OPENAI_BASE_URL, tự động lùi về Mock nếu thiếu). Các tác tử CHỈ phụ thuộc vào giao diện này.
- Sandbox V1: LocalSandbox với cơ chế cô lập thư mục gốc workspace nghiêm ngặt (không truy cập ra ngoài thư mục gốc). Docker sẽ làm sau, không phải bây giờ.
- Khả năng quan sát (Observability) ngay từ ngày đầu: tệp JSONL runs/run_XXX/events.log với run_id,trace_id,stage,agent,task_id,timestamp,duration_ms,model,tokens,tool,status,error.
- Điểm lưu trạng thái (Checkpoints): runs/run_XXX/state.json + checkpoints/001-spec.json,002-plan.json,003-task-TASKxxx.json,004-verification.json v.v. Phải hỗ trợ khôi phục chạy tiếp (resume) sau khi sập.
- CLI: python -m harness.cli new "request" chỉ sử dụng argparse. Xuất tiến trình 5 giai đoạn như trong đặc tả.
- Tất cả các model = Pydantic v2 BaseModel. Tất cả các enum = str Enum. Tất cả các ID: REQ-XXX, TASK-XXX, TEST-XXX, run_XXX.
GIÁ TRỊ MẶC ĐỊNH (khi nghi ngờ, hãy làm theo mục này, đừng hỏi):
- Suy luận công nghệ (Stack inference): nếu yêu cầu nói "web app" -> frontend=React, backend=FastAPI, db=SQLite. Nếu "API" -> FastAPI+SQLite. Nếu "todo" -> FastAPI+SQLite+HTML tối giản. Nếu không chỉ định -> FastAPI+SQLite.
- Loại ứng dụng (App type): mặc định "web". Các trang (Pages): suy luận từ danh từ (dashboard, login, projects, tasks). Mô hình dữ liệu (Data model): suy luận các thực thể. Must_have = tất cả các danh từ tường minh của người dùng. Non-goals = [deployment, mobile-app, browser-automation] trừ khi người dùng nói khác.
- Độ mịn của tác vụ (Task granularity): 5-12 tác vụ cho ứng dụng nhỏ, mỗi tác vụ chạm vào <=5 tệp, mỗi tác vụ có >=1 lệnh xác minh.
- Mặc định xác minh: python: `pip install -r requirements.txt` + `pytest -q`; node (nếu được tạo): `npm install` + `npm run build` + `npm test`. Luôn thu thập exit_code,stdout[-4000:],stderr[-4000:],duration.
- Ánh xạ phân loại lỗi mặc định: pytest khác 0 -> TEST_FAILURE, ModuleNotFound/ImportError -> DEPENDENCY_ERROR, SyntaxError -> CODE_ERROR, lỗi xác thực mypy/pydantic -> TYPE_ERROR, thiếu tệp cấu hình -> CONFIG_ERROR, ETIMEDOUT/timeout>120s -> TIMEOUT, ENOTFOUND/EAI_AGAIN/registry 503 -> ENVIRONMENT_ERROR, còn lại là UNKNOWN.
- Sửa chữa (Repair): chỉ chỉnh sửa các tệp được phép (task.files_touched + gợi ý từ bước xác minh). Thử lại tối đa 3 lần. Phát hiện vòng lặp: sha256(command+exit_code+stderr đã chuẩn hóa 2000 ký tự cuối); nếu trùng mã băm 3 lần -> ESCALATE (leo thang), đánh dấu các tác vụ phụ thuộc FAILED/BLOCK thành BLOCKED.
- Thời gian chờ (Timeouts): thực thi sandbox mặc định 120 giây, kiểm tra runtime 15 giây.
- Nếu thiếu khóa API LLM -> sử dụng MockProvider và các mẫu tất định để đường ống vẫn chạy xuyên suốt từ đầu đến cuối (lát cắt dọc Todo phải vượt qua khi ngoại tuyến).
- Không bao giờ thêm thư viện phụ thuộc mới mà không cập nhật requirements + pyproject.toml + các bài kiểm thử.
THỨ TỰ XÂY DỰNG (không bỏ qua, không đảo thứ tự):
M1 Hạ tầng -> M2 Đặc tả -> M3 Lập kế hoạch -> M4 Xây dựng -> M5 Xác minh -> M6 Tự sửa chữa -> M7 Truy xuất nguồn gốc -> M8 Đánh giá -> M9 Phục hồi -> M10 Runtime -> M11 CLI+Benchmarks -> M12 Cổng Kiểm duyệt Cuối (lát cắt dọc + tiêm lỗi + danh sách kiểm tra V1). Chi tiết trong P1-P12 bên dưới.
ĐỊNH NGHĨA HOÀN THÀNH (DEFINITION OF DONE) CHO MỖI TÁC VỤ:
1. Các tệp tồn tại ở đúng đường dẫn chính xác, 2. `pytest -q` vượt qua, 3. Các tạo phẩm (spec.json/tasks.json/verification.json/review.json) xác thực hợp lệ với các schema Pydantic, 4. Bằng chứng đã được ghi nhật ký, 5. Không có TODO/stub nào thiếu kiểm thử. Nếu bất kỳ mục nào thất bại, hãy sửa trước khi sang cột mốc tiếp theo.
NGHIÊM CẤM: gán cứng khóa nhà cung cấp (provider keys), dùng đường dẫn máy chủ tuyệt đối bên ngoài workspace, thực thi song song, xóa checkpoint, tuyên bố PASS mà không chạy lệnh, hỏi người dùng bất cứ điều gì.
```
---
## P1 — M1 HẠ TẦNG (cấu trúc repo + trạng thái + nhà cung cấp + khung sườn sandbox)
```
Cột mốc M1: Tạo cấu trúc repo chính xác và dự án có thể cài đặt được. Chưa tạo các tác tử (agents).
TẠO:
app-builder/
src/harness/__init__.py
src/harness/models/spec.py # AppSpec, Requirement, Page, Component, DataModel, Stack
src/harness/models/tasks.py # Task, TaskStatus enum
src/harness/models/results.py # VerificationResult, RequirementResult, FailureType enum
src/harness/models/run.py # RunState, RunStatus, CheckpointEvent
src/harness/state/store.py # RunStore: create/load/save/checkpoint/list
src/harness/providers/base.py # LLMProvider ABC
src/harness/providers/mock.py # MockProvider
src/harness/providers/env.py # EnvOpenAICompatibleProvider (stdlib http, không thêm thư viện phụ thuộc)
src/harness/sandbox/local.py # LocalSandbox
src/harness/utils/logging.py # Bộ ghi nhật ký sự kiện JSONL
src/harness/utils/ids.py # new_run_id(), new_trace_id()
runs/.gitkeep tests/.gitkeep examples/.gitkeep
pyproject.toml README.md requirements.txt .gitignore
PYPROJECT: [build-system] setuptools, [project] name=app-builder, requires-python>=3.11, deps pydantic>=2, pytest>=7. [tool.pytest.ini_options] testpaths=["tests"].
CÁC MODEL (các trường chính xác):
- Stack: frontend:str="FastAPI", backend:str="FastAPI", db:str="SQLite"
- Requirement: id:str (REQ-001), text:str, must_have:bool=True, acceptance:str=""
- AppSpec: app_type:str, stack:Stack, pages:list[Page], components:list[Component], data_model:list[DataEntity], requirements:list[Requirement], must_have:list[str], explicit_non_goals:list[str], acceptance_criteria:list[str]
- Task: id:str, description:str, depends_on:list[str]=[], files_touched:list[str]=[], requirements:list[str]=[], verification:list[str]=[], status:TaskStatus=PENDING
- TaskStatus: PENDING,READY,RUNNING,COMPLETED,FAILED,TIMED_OUT,BLOCKED
- VerificationResult: task_id:str, status:str (PASS/FAIL), command:str, exit_code:int, stdout:str, stderr:str, duration_ms:int, failure_type:FailureType|None
- FailureType: CODE_ERROR,TEST_FAILURE,TYPE_ERROR,DEPENDENCY_ERROR,CONFIG_ERROR,ENVIRONMENT_ERROR,TIMEOUT,UNKNOWN
- RequirementResult: requirement_id:str, status:str, evidence:list[str]=[], missing:list[str]=[]
- RunState: run_id, trace_id, user_request:str, workspace:str, spec:AppSpec|None, plan:list[Task]=[], task_results:dict[str,Any]= {}, verification_results:list[VerificationResult]=[], review:dict|None, checkpoints:list[str]=[], status:RunStatus
- RunStatus: CREATED,SPEC_DONE,PLAN_DONE,BUILDING,VERIFYING,REPAIRING,REVIEW_DONE,PASS,FAIL
SANDBOX LocalSandbox(root:Path):
create_workspace(run_id)->Path; write_file(rel:str,content:str)->Path (từ chối thoát thư mục bằng ../); read_file(rel)->str; list_files(rel=".")->list[str]; delete_file(rel); execute(cmd:list[str]|str, cwd:Path|None, timeout_s:int=120)->dict{exit_code,stdout,stderr,duration_ms} thông qua subprocess.run(shell=False nếu là list, chỉ True nếu là str + ghi nhật ký cảnh báo).
STORE RunStore(base=Path("runs")):
create_run(user_request)->RunState; save(state); load(run_id)->RunState; checkpoint(state, name:str, payload:dict); list_runs()->list[str]; resume(run_id)->RunState.
LOGGING emit(base, run_id, trace_id, stage, agent, task_id, status, duration_ms=0, model="", tokens=0, tool="", error="") ghi nối thêm dòng JSON vào runs/{run_id}/events.log.
LLMProvider ABC: generate(prompt:str, system:str="")->str; generate_structured(prompt:str, schema:type[BaseModel])->BaseModel; stream(prompt:str)->Iterator[str] (Mock trả về từng từ).
MockProvider: generate trả về bản phản hồi mẫu tất định; generate_structured trả về schema.model_validate({minimal valid}) cho AppSpec/TaskList được dùng trong kiểm thử.
Env provider: nếu không có khóa trong biến môi trường, ủy quyền cho MockProvider (để các bài kiểm thử ngoại tuyến vượt qua).
NGHIỆM THU:
- `pip install -e .` thành công, `pytest -q` thu thập được bài kiểm thử (ngay cả khi có 0 bài kiểm thử, hãy thêm tests/test_models.py xác thực chu trình khép kín của AppSpec + Task + cơ chế cô lập sandbox từ chối ../ + lưu/tải store).
- Không cần mạng. Không đặt câu hỏi.
KHÔNG ĐƯỢC: xây dựng các tác tử, DAG, hay bộ xác minh ở bước này.
```
## P2 — M2 TÁC TỬ ĐẶC TẢ (SPEC AGENT) + BỘ XÁC THỰC (VALIDATOR)
```
Cột mốc M2: Yêu cầu người dùng -> tệp spec.json đã được xác thực. Chưa sinh mã nguồn.
CÁC TỆP:
src/harness/agents/spec.py # SpecAgent(llm:LLMProvider).generate(user_request:str)->AppSpec (mẫu prompt + phân tích cú pháp + bộ phân tích cú pháp tất định dự phòng nếu LLM thất bại)
src/harness/validation/spec_validator.py # SpecValidator.validate(spec)->list[str] danh sách lỗi (rỗng=PASS)
tests/test_spec.py
LOGIC CỦA SPEC AGENT:
System: "You output ONLY valid JSON matching AppSpec schema. No prose. Infer stack/pages/data_model/requirements/must_have/non_goals/acceptance."
Mẫu User bao gồm: yêu cầu + các giá trị mặc định về stack từ P0 + yêu cầu REQ-001..N, tiêu chí nghiệm thu cho mỗi yêu cầu.
Hậu xử lý: gán REQ-001.. tuần tự, đảm bảo must_have không rỗng, acceptance_criteria không rỗng, explicit_non_goals mặc định 3 mục.
Nếu JSON từ LLM không hợp lệ -> lùi về bộ phân tích cú pháp dựa trên quy tắc (quét từ khóa tìm auth/projects/tasks/dashboard -> các thực thể) để đảm bảo PASS khi ngoại tuyến.
CÁC KIỂM TRA CỦA BỘ XÁC THỰC (mỗi kiểm tra trả về chuỗi lỗi):
Cấu trúc: app_type không rỗng, stack.* không rỗng, requirements>=1, acceptance_criteria>=1, các ID là duy nhất khớp với mẫu REQ-\\d{3}.
Logic: mọi page.entity (nếu có trường này) phải nằm trong tên của data_model; không có chuỗi con văn bản yêu cầu nào nằm trong explicit_non_goals; nếu bất kỳ yêu cầu nào nhắc đến API/backend thì stack.backend không được rỗng; báo lỗi nếu văn bản yêu cầu bị trùng lặp (không phân biệt hoa thường); mọi must_have phải ánh xạ tới >=1 yêu cầu (qua chuỗi con hoặc liên kết tường minh).
Hàm hỗ trợ đường ống: spec_stage(store, run_id, llm, max_attempts=2): generate->validate; nếu FAIL thì thử lại kèm phản hồi lỗi; nếu vẫn FAIL thì ném ngoại lệ (raise); ngược lại lưu checkpoint 001-spec.json + state.spec.
NGHIỆM THU:
- tests/test_spec.py: yêu cầu hợp lệ -> PASS; xung đột mục tiêu loại trừ (yêu cầu "login" + non-goal "login") -> phát hiện FAIL; thiếu tiêu chí nghiệm thu -> FAIL; vòng lặp thử lại thành công ở lần thử thứ 2 (giả lập thất bại 1 lần).
- Tạo phẩm mẫu examples/todo_spec.json (Ứng dụng Todo, 4 REQ).
KHÔNG ĐƯỢC: lập kế hoạch hoặc xây dựng.
```
## P3 — M3 BỘ LẬP KẾ HOẠCH (PLANNER) + ĐỘNG CƠ DAG (tuần tự)
```
Cột mốc M3: AppSpec -> DAG tasks.json + bộ thực thi tuần tự. Không chạy song song.
CÁC TỆP:
src/harness/agents/planner.py # PlannerAgent(llm).plan(spec)->list[Task]
src/harness/orchestration/dag.py # DagEngine + PlanValidator
tests/test_planner.py tests/test_dag.py
QUY TẮC CỦA BỘ LẬP KẾ HOẠCH (cưỡng chế trong mã nguồn, không chỉ trong prompt):
1. (Các) tác vụ DB/schema đi trước tiên (các tệp chứa model/schema/db/migration).
2. Các thành phần (components) dùng chung trước các trang (pages).
3. Hạ tầng xác thực (auth infra) trước các route yêu cầu xác thực (các tệp có auth).
4. Mọi REQ -> >=1 task.requirements; mọi tác vụ -> >=1 chuỗi verification + >=1 requirements + >=1 files_touched.
5. Các ID TASK-001.. tuần tự, depends_on chỉ trỏ tới các ID đứng trước (không có chu trình). Nếu LLM vi phạm, tự động sửa: sắp xếp + sửa chữa các phụ thuộc một cách tất định.
6. Prompt LLM: bao gồm JSON đặc tả + các quy tắc thứ tự + chỉ xuất danh sách JSON. Mẫu dự phòng: [schema/setup, auth, core entities CRUD, pages/API, tests/verify] nếu LLM thất bại.
7. files_touched phải là các đường dẫn POSIX tương đối bên trong workspace (không dùng đường dẫn tuyệt đối, không dùng ..).
ĐỘNG CƠ DAG (tuần tự V1):
class DagEngine(tasks:list[Task]): từ điển statuses; get_ready()->list[Task] (PENDING + mọi phụ thuộc đã COMPLETED); mark(task_id,status); is_done(); blocked_propagation(): nếu phụ thuộc bị FAILED/TIMED_OUT -> các tác vụ phụ thuộc vào nó chuyển thành BLOCKED; topological_order() ném ngoại lệ nếu có chu trình.
statuses: PENDING,READY (được tính toán, không lưu trữ — lưu PENDING cho đến khi được điều phối),RUNNING,COMPLETED,FAILED,TIMED_OUT,BLOCKED. Bảo toàn sự khác biệt giữa FAILED != TIMED_OUT.
Bộ thực thi (khung sườn trong orchestration/runner.py, triển khai đầy đủ ở M4/M5 nhưng phần DAG làm ngay bây giờ): vòng lặp tìm READY -> RUNNING -> (hook giữ chỗ) -> COMPLETED; nếu không có tác vụ nào READY mà chưa hoàn thành -> báo lỗi bế tắc (deadlock) liệt kê các tác vụ bị BLOCKED.
PlanValidator: trùng lặp ID, phụ thuộc không xác định, chu trình, REQ mồ côi (không có tác vụ), tác vụ không có bước xác minh, cảnh báo tệp chồng lấn (không tính là lỗi trong V1, nhưng ghi nhật ký để phục vụ chạy song song trong tương lai).
NGHIỆM THU:
- Đặc tả Todo (4 REQ) -> 5-8 tác vụ, tất cả các quy tắc đều được đảm bảo (kiểm tra bằng assert trong bài kiểm thử).
- Dữ liệu mẫu có chu trình -> bộ xác thực báo lỗi + động cơ ném ngoại lệ.
- TASK-001 FAIL -> các tác vụ phụ thuộc bị BLOCKED (kiểm thử).
- Checkpoint 002-plan.json được lưu thông qua store.
KHÔNG ĐƯỢC: chạy các bộ xây dựng (builders), không xử lý đồng thời, chưa làm bộ lập lịch xung đột tệp.
```
## P4 — M4 BỘ XÂY DỰNG (BUILDER) + CÔNG CỤ (có kiểm soát qua sandbox)
```
Cột mốc M4: Tác vụ -> các tệp thực tế. Chỉ truyền ngữ cảnh tối thiểu.
CÁC TỆP:
src/harness/agents/builder.py # BuilderAgent(llm, sandbox, store)
src/harness/tools/files.py # các hàm bọc read_file, write_file, list_files cưỡng chế danh sách cho phép (allowlist)
tests/test_builder.py
ĐẦU VÀO CỦA BUILDER (chỉ những thông tin này, không bao giờ đổ toàn bộ dữ liệu):
task:Task, requirements:list[Requirement] (được lọc theo task.requirements), spec_summary:dict (app_type,stack,tên các data_model), dependency_results:list[VerificationResult], allowed_files:list[str] (=task.files_touched), verification:list[str].
MẪU PROMPT CỦA BUILDER:
"Implement TASK-{id}: {description}. Requirements: {req texts+acceptance}. Allowed files (ONLY these): {list}. Prior results: {dep summary}. Output file contents as JSON map {{relpath: content}}. Use FastAPI+SQLite if backend. Include imports, no stubs. If test file required, include pytest tests."
CƯỠNG CHẾ CÔNG CỤ:
Builder chỉ được gọi sandbox.write_file/read_file/list_files bên trong workspace + nằm trong allowed_files (đối với thao tác ghi) — bất kỳ đường dẫn nào khác -> ghi nhật ký PermissionError, đánh dấu tác vụ FAILED (TYPE_ERROR? hãy dùng CONFIG_ERROR).
Không bao giờ gọi subprocess trực tiếp — chỉ thực hiện qua sandbox.execute ở M5. Builder ghi các tệp rồi trả về {written:[...]}.
MẶC ĐỊNH KHỞI TẠO KHUNG (SCAFFOLD DEFAULTS - an toàn khi ngoại tuyến):
Nếu LLM trả về bản đồ (map) không hợp lệ -> cơ chế dự phòng sẽ ghi ứng dụng FastAPI tối giản: workspace/src/main.py (kiểm tra sức khỏe GET /health), workspace/src/models.py, workspace/requirements.txt (fastapi,uvicorn,pydantic,pytest), workspace/tests/test_health.py kiểm tra /health qua TestClient hoặc kiểm tra sự tồn tại của tệp nếu thiếu fastapi. Điều này đảm bảo lát cắt dọc vượt qua được khi ngoại tuyến.
NGHIỆM THU:
- Với Todo TASK-001 (schema) trong workspace tạm thời -> các tệp được tạo nằm trong danh sách cho phép (allowlist), thao tác ghi ra ngoài bị từ chối.
- LLM giả lập (Mock LLM) khi ngoại tuyến vẫn tạo ra bộ khung chạy được + vượt qua pytest.
- Lưu checkpoint 003-task-{id}.json sau mỗi tác vụ.
KHÔNG ĐƯỢC: xác minh (M5) hoặc sửa chữa ở bước này — chỉ ghi tệp.
```
## P5 — M5 XÁC MINH (thực thi thật) + PHÂN LOẠI LỖI
```
Cột mốc M5: Thực sự chạy các câu lệnh, thu thập kết quả có cấu trúc, phân loại lỗi.
CÁC TỆP:
src/harness/verification/verifier.py # Verifier(sandbox).run(task, workspace, commands)->list[VerificationResult]
src/harness/verification/classifier.py # FailureClassifier.classify(result)->FailureType
tests/test_verifier.py tests/test_classifier.py
BỘ XÁC MINH (VERIFIER):
def verify_task(task:Task, workspace:Path)->list[VerificationResult]:
với mỗi cmd_str trong task.verification (mặc định nếu rỗng: ["pytest -q"]):
phân tách cmd_str qua shlex.split (POSIX) -> sandbox.execute(cmd, cwd=workspace, timeout 120s)
thu thập exit_code,stdout[-4000:],stderr[-4000:],duration_ms; trạng thái là PASS nếu bằng 0, ngược lại là FAIL; failure_type=None nếu PASS, ngược lại gọi classify().
lưu trữ bền vững 004-verification-{task_id}.json thông qua store.
Đồng thời ở cấp độ toàn bộ bộ kiểm thử (suite-level): hàm hỗ trợ verify_workspace(workspace, extra=["pip install -r requirements.txt" nếu tồn tại]).
KHÔNG ĐƯỢC chỉ kiểm tra tĩnh — bắt buộc phải thực thi. Quá thời gian chờ (Timeout) -> TIMED_OUT + failure_type TIMEOUT.
BỘ PHÂN LOẠI (CLASSIFIER - dùng regex + exit_code, tất định):
- TIMEOUT nếu duration>=timeout hoặc có "timed out"/"TimeoutExpired".
- ENVIRONMENT_ERROR nếu khớp "EAI_AGAIN|ENOTFOUND|503|registry.*unavailable|Network is unreachable|pip.*Could not fetch".
- DEPENDENCY_ERROR nếu khớp "ModuleNotFound|ImportError|No module named|npm ERR.*404|Could not resolve dependency".
- TYPE_ERROR nếu khớp "mypy|TypeError:.*expected|pydantic.*ValidationError|TS2322|Property.*does not exist".
- TEST_FAILURE nếu khớp "FAILED|AssertionError|1 failed|FAIL tests/" và exit!=0 đồng thời không thuộc các loại trên.
- CONFIG_ERROR nếu khớp "FileNotFound.*config|missing.*pyproject|requirements.*not found|PORT in use" v.v.
- CODE_ERROR nếu khớp "SyntaxError|IndentationError|NameError|ReferenceError".
- còn lại là UNKNOWN.
Viết kiểm thử đơn vị (unit-test) cho từng loại với dữ liệu mẫu từ đặc tả (ví dụ: "npm registry unavailable" -> ENVIRONMENT_ERROR).
NGHIỆM THU:
- Tạo workspace bị lỗi (lỗi cú pháp) -> bộ xác minh trả về FAIL + CODE_ERROR, exit!=0, thu thập được stderr.
- Bộ khung chuẩn từ M4 -> PASS.
- Dữ liệu mẫu kiểm thử quá thời gian chờ (sleep 3 với timeout 1) -> TIMED_OUT.
KHÔNG ĐƯỢC: tự động sửa chữa ở bước này.
```
## P6 — M6 VÒNG LẶP SỬA CHỮA + PHÁT HIỆN VÒNG LẶP
```
Cột mốc M6: FAIL -> phân loại -> sửa chữa -> xác minh lại, tối đa 3 lần, có bộ bảo vệ chống lặp vô hạn.
CÁC TỆP:
src/harness/orchestration/repair.py # RepairLoop + LoopDetector
src/harness/agents/repair_agent.py # RepairAgent(llm, sandbox)
tests/test_repair.py
BỘ PHÁT HIỆN VÒNG LẶP (LOOP DETECTOR):
def sig(cmd, exit_code, stderr): return sha256(f"{cmd}|{exit_code}|{normalize(stderr[-2000:])}".encode()).hexdigest()
normalize: chuyển chữ thường, lược bỏ số/đường dẫn/dấu thời gian (bằng regex), thu gọn khoảng trắng.
LoopDetector(seen:dict[sig,count]): add(sig)-> (is_loop:bool, count:int); is_loop là True nếu count>=3 cho cùng một chữ ký sig.
TÁC TỬ SỬA CHỮA (REPAIR AGENT):
Đầu vào: task, spec_slice, VerificationResult bị lỗi + nhãn từ bộ phân loại + nội dung tệp gần nhất (đọc qua sandbox, cắt ngắn ở mức 8000 ký tự).
Prompt: "Fix {FailureType} in {files}. Error: {stderr}. Do NOT rewrite unrelated files. Output JSON map {{relpath: full corrected content}}."
Chiến lược theo từng loại lỗi: ENVIRONMENT_ERROR -> KHÔNG viết lại mã nguồn, thử lại một lần sau 2 giây, nếu vẫn tiếp diễn thì đánh dấu FAILED (lỗi môi trường, không phải mã nguồn); DEPENDENCY_ERROR -> sửa requirements.txt/pyproject; TYPE_ERROR/CODE_ERROR/TEST_FAILURE -> vá mã nguồn; CONFIG_ERROR -> sửa cấu hình; TIMEOUT -> giảm phạm vi/tăng thời gian chờ một lần, nếu không được thì FAIL.
VÒNG LẶP SỬA CHỮA (REPAIR LOOP):
def run_with_repair(task, workspace, verifier, repair_agent, max_retries=3):
attempt=0; while True: results=verifier.verify_task(...); nếu tất cả PASS thì trả về PASS; phân loại lỗi; kiểm tra chữ ký sig -> nếu bị lặp: ghi nhật ký WARNING, thử sửa chữa lần cuối cùng, sau đó ESCALATE FAILED; nếu attempt>=max_retries: đánh dấu FAILED; ngược lại tiến hành sửa chữa (ghi các tệp đã vá thông qua sandbox), lưu checkpoint repair_started, attempt+=1, xác minh lại.
Cập nhật trạng thái trong DagEngine + store.task_results.
NGHIỆM THU:
- Tiêm lỗi cú pháp TypeScript/Python -> vòng lặp phát hiện, sửa chữa, đạt PASS trong vòng <=3 lần thử (kiểm thử với Mock LLM trả về nội dung đã sửa).
- Lỗi giống hệt nhau 3 lần (giả lập sửa chữa trả về cùng một tệp lỗi) -> vòng lặp dừng lại, ESCALATE, tác vụ bị FAILED, các tác vụ phụ thuộc bị BLOCKED.
- ENVIRONMENT_ERROR KHÔNG kích hoạt việc viết lại mã nguồn (dùng assert kiểm tra các tệp không thay đổi, chỉ thử lại 1 lần).
KHÔNG ĐƯỢC: tạo các checkpoint ngoài repair_started/task_completed ở bước này (để dành cho M9).
```
## P7 — M7 TRUY XUẤT NGUỒN GỐC + M8 TÁC TỬ ĐÁNH GIÁ (REVIEW AGENT)
```
Cột mốc M7+M8: Yêu cầu -> Tác vụ -> Tệp -> Kiểm thử -> Bằng chứng + cổng kiểm toán cuối cùng.
CÁC TỆP:
src/harness/trace/matrix.py # build_matrix(spec, tasks, verification_results, workspace)->dict[REQ, {tasks, files, tests, status}]
src/harness/agents/reviewer.py # ReviewAgent(llm|rule-based).review(spec, matrix, workspace)->dict{requirements:[{id,status,evidence,missing}], overall_status}
tests/test_trace.py tests/test_review.py
MA TRẬN (MATRIX):
Với mỗi REQ: tasks = [t for t in plan if REQ in t.requirements]; files = hợp (union) của các files_touched; tests/commands = hợp của các verification; evidence = [f for f in files if workspace/f tồn tại] + [v.command for v in results if v.status PASS và v.task_id in tasks]; trạng thái chỉ đạt PASS nếu có >=1 tệp tồn tại VÀ >=1 kết quả PASS bao phủ nó, ngược lại là FAIL kèm theo missing=[các lý do].
Không có bằng chứng -> FAIL (không bao giờ cho PASS chỉ dựa trên văn bản mô tả).
BỘ ĐÁNH GIÁ (REVIEWER - mặc định dựa trên quy tắc, tùy chọn dùng LLM):
Lượt kiểm tra bằng quy tắc: kiểm tra tệp tồn tại + xác minh đạt PASS + (nếu tiêu chí nghiệm thu có nhắc đến từ khóa, tìm kiếm (grep) từ khóa đó trong tệp, nếu không thấy thì cảnh báo). LLM có thể bổ sung lý giải nhưng không thể ghi đè từ FAIL->PASS nếu không có bằng chứng.
Xuất JSON chính xác theo cấu trúc: {"requirements": [{"id","status":"PASS|FAIL","evidence":[paths+commands],"missing":[]}], "overall_status":"PASS|FAIL"} và lưu thành review.json.
Trạng thái tổng thể (Overall) chỉ đạt PASS nếu tất cả các REQ đều đạt PASS.
NGHIỆM THU:
- Lượt chạy Todo với tất cả đều PASS -> ma trận hiển thị 4/4, kết quả đánh giá tổng thể đạt PASS kèm theo đường dẫn bằng chứng.
- Xóa một tệp triển khai -> REQ tương ứng bị FAIL kèm theo missing=["file src/... not found"], kết quả tổng thể FAIL.
- Tệp review.json xác thực hợp lệ với schema danh sách RequirementResult.
KHÔNG ĐƯỢC: làm phần runtime/trình duyệt ở bước này.
```
## P8 — M9 LƯU TRẠNG THÁI (CHECKPOINTING) + PHỤC HỒI (RECOVERY)
```
Cột mốc M9: Khôi phục chạy tiếp an toàn sau sự cố sập (crash-safe resume).
CÁC TỆP:
src/harness/state/checkpoints.py # CheckpointManager (lớp mỏng bọc trên RunStore)
tests/test_recovery.py
CÁC SỰ KIỆN (bắt buộc phát ra thông qua store.checkpoint):
spec_created, plan_created, task_started, task_completed, verification_completed, repair_started, review_completed.
Bố cục thư mục: runs/{run_id}/state.json (RunState mới nhất), checkpoints/{seq:03d}-{event}-{task?}.json, workspace/ (ứng dụng được tạo ra), events.log.
LOGIC KHÔI PHỤC (RESUME LOGIC) trong orchestration/pipeline.py::resume(run_id):
tải state.json; tìm checkpoint hoàn thành gần nhất; tính toán lại trạng thái DAG từ task_results+verification_results; đưa các tác vụ đang PENDING/READY/RUNNING về lại hàng đợi thành PENDING (RERUN), giữ nguyên COMPLETED/FAILED/BLOCKED; tiếp tục chạy đường ống mà không làm lại các tác vụ đã COMPLETED (dùng assert kiểm tra mã băm của các tệp không đổi).
NGHIỆM THU:
- Bắt đầu lượt chạy Todo, ngắt giữa chừng sau TASK-002 (mô phỏng bằng cách lưu trạng thái một phần), hàm resume() hoàn thành phần còn lại mà không làm lại TASK-001 (dùng assert kiểm tra events.log chỉ hiển thị TASK-001 một lần).
- Làm hỏng tệp state.json -> hàm resume ném ra lỗi rõ ràng (không im lặng bỏ qua).
- Các bài kiểm thử bao phủ quy tắc đặt tên tệp checkpoint + thứ tự chuỗi (sequence).
KHÔNG ĐƯỢC: chạy song song ở bước này.
```
## P9 — M10 XÁC MINH RUNTIME (không dùng trình duyệt trong V1)
```
Cột mốc M10: Chứng minh ứng dụng được xây dựng thực sự khởi động và phục vụ được yêu cầu.
CÁC TỆP:
src/harness/verification/runtime.py # RuntimeVerifier
tests/test_runtime.py
CÁC BƯỚC CỦA BỘ XÁC MINH RUNTIME (mặc định FastAPI, dự phòng Node):
1. Phát hiện điểm vào (entry): src/main.py:app hoặc app.py:app hoặc main trong package.json. Nếu không có, đánh dấu FAIL (CONFIG_ERROR).
2. Khởi động: `python -m uvicorn src.main:app --port {free_port}` hoặc `npm run dev -- --port {port}` thông qua subprocess.Popen (từ thư mục gốc của sandbox), đợi tối đa 15 giây.
3. Kiểm tra: tiến trình còn sống, cổng TCP mở (socket.connect), GET /health hoặc / trả về mã 2xx, GET /docs hoặc /api/health (nếu tồn tại) trả về mã 2xx, tệp SQLite tồn tại/kết nối được nếu có yêu cầu.
4. Thu thập nhật ký (logs), kết thúc tiến trình, trả về {status PASS/FAIL, checks:[{name, ok, detail}], evidence:[đoạn trích log, mã trạng thái http]}.
5. Khi bị FAIL, phân loại lỗi (CONFIG/DEPENDENCY/ENVIRONMENT) để tái sử dụng trong vòng lặp sửa chữa.
TRÌNH DUYỆT (V1: CHỈ TẠO HÀM GIỮ CHỖ - STUB ONLY): tạo src/harness/verification/browser.py với `def verify_acceptance(...): raise NotImplementedError("Browser verification deferred post-V1")` + bài kiểm thử xác nhận bỏ qua (skip). KHÔNG triển khai Playwright/Selenium ngay lúc này.
NGHIỆM THU:
- Ứng dụng khung từ M4 -> runtime đạt PASS (health 200).
- Cổng bị lỗi (ứng dụng tự thoát) -> FAIL kèm thông tin chẩn đoán và gợi ý sửa chữa.
KHÔNG ĐƯỢC: dùng Docker, chạy song song.
```
## P10 — M11 KHẢ NĂNG QUAN SÁT (OBSERVABILITY) + CLI + ĐO KIỂM (BENCHMARKS)
```
Cột mốc M11: Bộ khung có thể vận hành hoàn chỉnh.
KHẢ NĂNG QUAN SÁT (hoàn thiện src/harness/utils/logging.py):
Mọi giai đoạn đều phát ra dòng JSONL với đầy đủ các trường: run_id,trace_id,stage,agent,task_id,timestamp,duration_ms,model,tokens,tool,status,error. Cung cấp lệnh `python -m harness.cli logs <run_id>` để in đẹp mắt (pretty-print) + `stats` (số lượng, số lần thử lại, tổng token, thời lượng). Bài kiểm thử dùng assert xác nhận sự tồn tại của các khóa bắt buộc trên mọi dòng.
CLI (src/harness/cli.py, chỉ dùng argparse):
`builder new "request" [--run-id X --workspace Y --max-retries 3]` in ra:
[1/5] Generating specification... ✓
[2/5] Planning N tasks... ✓
[3/5] Building... ✓ TASK-001 ... (⚠ fail + ↻ repairing)
[4/5] Verifying... ✓ Build ✓ Tests ✓ Runtime
[5/5] Reviewing requirements... ✓ 14/14 satisfied
BUILD COMPLETE / BUILD FAILED kèm theo đường dẫn tới spec/tasks/verification/review.json
Đồng thời hỗ trợ `builder resume <run_id>`, `builder review <run_id>`, `builder logs <run_id>`.
Ánh xạ tới hàm orchestration/pipeline.py::run_new() điều phối tuần tự từ M2-M10.
ĐO KIỂM - BENCHMARKS (benchmarks/*.json + src/harness/bench/runner.py):
5 kịch bản cố định: todo, crud-dashboard, auth-app, api-db, ecommerce-prototype (mỗi kịch bản gồm: chuỗi yêu cầu + số REQ tối thiểu + khoảng số lượng tác vụ kỳ vọng). Bộ chạy (Runner) thực thi đường ống với MockProvider, ghi nhận {build_success, test_success, req_completion, repair_count, duration_s, tokens}. Chạy `pytest benchmarks/` hoặc `builder bench --quick` (chỉ chạy kịch bản todo). KHÔNG đánh giá bằng cảm quan giao diện — hãy dùng assert kiểm tra tệp JSON số liệu được ghi ra.
NGHIỆM THU:
- Chạy `builder new "Build a Todo app"` ngoại tuyến -> BUILD COMPLETE, các tạo phẩm tồn tại, đầu ra 5 giai đoạn khớp với biểu thức chính quy (regex).
- Tệp events.log có đầy đủ tất cả các khóa, bài đo kiểm nhanh (bench quick) vượt qua.
KHÔNG ĐƯỢC: làm giao diện web (web UI), dùng Docker cho môi trường prod.
```
## P11 — M12 CỔNG KIỂM DUYỆT CUỐI: LÁT CẮT DỌC + TIÊM LỖI + DANH SÁCH KIỂM TRA V1
```
Cột mốc M12: Chứng minh độ tin cậy, sau đó đóng băng phiên bản V1.
CÁC TÁC VỤ (thực hiện theo thứ tự, tất cả phải đạt PASS):
1. LÁT CẮT DỌC (VERTICAL SLICE): `builder new "Build a Simple Todo App with add/list/complete"` -> kỳ vọng SPEC(>=3 REQ)->PLAN(>=3 tác vụ)->BUILD(các tệp)->VERIFY(pytest PASS)->RUNTIME PASS->REVIEW PASS. Lưu dưới thư mục runs/demo_todo/. Nếu bất kỳ bước nào FAIL, hãy sửa bộ khung (harness), không được đi tiếp.
2. TIÊM LỖI (FAILURE INJECTION): đưa lỗi SyntaxError vào workspace/src/main.py, chạy lại xác minh -> bắt buộc phải PHÁT HIỆN (FAIL+CODE_ERROR), KHOANH VÙNG (task_id), THU THẬP thông tin chẩn đoán, GỬI tới bộ sửa chữa, SỬA CHỮA, XÁC MINH LẠI ĐẠT PASS. Sau đó tiêm lỗi giống hệt nhau 3 lần với bản giả lập sửa chữa không làm gì (no-op repair mock) -> bắt buộc phải chuyển trạng thái WARNING->ESCALATE->FAILED+BLOCKED (kiểm tra bằng assert).
3. DANH SÁCH KIỂM TRA V1 (tất cả đều ✓): đặc tả hợp lệ, đặc tả đã qua xác thực, DAG hợp lệ, thực thi tuần tự, tạo tệp thật, xác minh thật, khoanh vùng được lỗi, sửa được lỗi, phát hiện vòng lặp, lưu checkpoint+khôi phục, ma trận truy xuất nguồn gốc, đánh giá cuối cùng, dự án hoạt động được nằm trong runs/.
4. TÀI LIỆU: README.md (hướng dẫn bắt đầu nhanh với builder new/resume/logs, sơ đồ kiến trúc ASCII từ mục §34 của kế hoạch, nguyên tắc bằng chứng), examples/todo_run/ (bản sao các tệp JSON spec/tasks/verification/review).
5. Toàn bộ repo chạy `pytest -q` đều xanh (green), `builder bench --quick` xanh.
NGHIÊM CẤM TRONG V1 (dùng assert xác nhận không tồn tại): docker/, web_ui/, các tiến trình chạy song song (parallel workers), tự động hóa trình duyệt vượt quá hàm giữ chỗ (stub), postgres, nghiên cứu qua mạng.
Nếu tất cả đều xanh -> gắn thẻ V1 DONE. Nếu chưa đạt, lặp lại việc sửa lỗi từ M1-M11, không bao giờ bàn giao khi còn báo đỏ.
```
---
## PHỤ LỤC QUY TẮC TOÀN CỤC (tác tử lập trình bắt buộc phải tuân thủ)
1. KHÔNG BAO GIỜ đặt câu hỏi. Hãy sử dụng các giá trị mặc định ở P0.
2. Xây dựng tuần tự từ M1->M12. Không bắt đầu M(N+1) nếu các bài kiểm thử của M(N) còn báo đỏ. Chạy `pytest -q` sau mỗi cột mốc.
3. Giữ các bản thay đổi (diffs) nhỏ gọn, các tệp tập trung (<400 dòng mỗi tệp, tách nhỏ nếu lớn hơn).
4. Mọi mô-đun mới đều cần một tệp kiểm thử. Mọi bản sửa lỗi đều cần một bài kiểm thử hồi quy (regression test).
5. Không lưu thông tin bí mật (secrets) trong repo. Không dùng đường dẫn tuyệt đối. Không dùng `shell=True` ngoại trừ trong bộ xác minh kèm theo cảnh báo được ghi nhật ký.
6. Bằng chứng quan trọng hơn lời tuyên bố: mọi thay đổi trạng thái đều phải ghi vào events.log + checkpoint.
7. Nếu lệnh gọi LLM thất bại/quá thời gian chờ -> lùi về nhánh xử lý tất định dự phòng để đường ống không bao giờ bị tắc nghẽn.
8. Sản phẩm bàn giao cuối cùng cho mỗi lượt chạy: workspace/ + spec.json + tasks.json + verification.json + review.json + events.log + state.json.
9. Bản tóm tắt cuối lượt chạy phải liệt kê cho từng REQ: Đã triển khai chưa? Đã kiểm thử chưa? Đã chạy Runtime chưa? Bằng chứng đâu? PASS/FAIL — không bao giờ được viết "AI báo đã xong".
## GHI CHÚ VỀ MCP prompts.chat
- Đã tìm kiếm qua `prompts-chat_search_prompts/skills` (công khai, OK, 0 kết quả cho các truy vấn ngách về bộ khung harness — đúng như dự kiến).
- `improve_prompt` / `save_prompt` yêu cầu khóa API (`Authentication required`). Để xuất bản: đặt biến `PROMPTS_CHAT_API_KEY` trong môi trường, sau đó gọi `prompts-chat_save_prompt(title="App-Builder Harness Autonomous Prompts", content=<tệp này>)`. Tệp cục bộ là bản chuẩn cho đến thời điểm đó.
- Các thẻ (tags) gợi ý nếu xuất bản: `coding-agent, autonomous, python, harness, spec-driven, verification`.
Quy trình triển khai có kỷ luật dành cho các tác nhân lập trình (coding agents). Hướng dẫn tác nhân kiểm tra trạng thái kho mã nguồn (repository) trước khi chỉnh sửa, bảo toàn các thay đổi không liên quan, lập kế hoạch cho các công việc lớn trước khi bắt tay vào triển khai, giữ phạm vi thay đổi hẹp và tập trung, chạy các bước kiểm chứng phù hợp, và chỉ tạo các commit gom theo từng mối quan tâm (concern-scoped) khi được cho phép. Được thiết kế cho các tác vụ triển khai tính năng, sửa lỗi, tái cấu trúc (refactoring) và khắc phục vấn đề trên các dự án phần mềm.
--- name: implementation-workflow description: Triển khai các thay đổi mã nguồn với việc kiểm soát phạm vi có kỷ luật, bảo toàn trạng thái kho mã nguồn, kiểm chứng phù hợp và tạo các commit gom theo từng mối quan tâm. Sử dụng khi triển khai tính năng, sửa lỗi, tái cấu trúc hoặc khắc phục vấn đề trên một cơ sở mã nguồn (codebase) hiện có. --- # Quy trình Triển khai Mã nguồn Triển khai thay đổi được yêu cầu một cách an toàn, tối giản và ở định dạng dễ dàng rà soát (review). ## Phạm vi và thứ tự ưu tiên Tuân thủ nhiệm vụ hiện tại, các hướng dẫn áp dụng trong kho mã nguồn như `AGENTS.md`, và các quy ước đã được thiết lập của dự án. Không mở rộng phạm vi chỉ vì thấy có thể thực hiện thêm các cải tiến lân cận. Kỹ năng này điều phối giai đoạn triển khai và khắc phục lỗi. Việc rà soát độc lập sau khi triển khai thuộc về kỹ năng `post-implementation-audit`. ## 1. Kiểm tra trước khi thay đổi Trước khi chỉnh sửa: - hiểu rõ hành vi được yêu cầu và các tiêu chí nghiệm thu (acceptance criteria); - kiểm tra phần mã nguồn triển khai hiện có liên quan; - kiểm tra trạng thái kho mã nguồn khi có sẵn Git; - xác định các tệp đã bị chỉnh sửa, đã đưa vào vùng chờ (staged), đã xóa hoặc chưa được theo dõi (untracked) tồn tại từ trước. Coi các thay đổi hiện có không liên quan là phần được bảo vệ. Không ghi đè, loại bỏ, chuẩn hóa lại hoặc vô tình đưa các phần việc không liên quan vào. ## 2. Quyết định xem có cần lập kế hoạch hay không Tiến hành làm trực tiếp khi nhiệm vụ nhỏ, cục bộ, rủi ro thấp và đã đủ rõ ràng. Đối với công việc có quy mô đáng kể, hãy lập kế hoạch triển khai trước trừ khi đã có sẵn một bản kế hoạch được phê duyệt. Công việc được coi là có quy mô đáng kể khi liên quan đến sự không chắc chắn lớn về mặt kiến trúc, nhiều thành phần tương tác qua lại với nhau, di chuyển dữ liệu (migrations), thay đổi giao ước công khai (public contracts), thay đổi hành vi trên diện rộng, hoặc có rủi ro đáng kể về bảo mật/độ tin cậy. Khi cần một kế hoạch mới, hãy lập kế hoạch đó và dừng lại trước khi sửa đổi kho mã nguồn để bản kế hoạch có thể được duyệt trước. Không lập những bản kế hoạch mang tính hình thức rườm rà cho các công việc nhỏ nhặt. ## 3. Triển khai trong phạm vi hẹp Sau khi việc triển khai đã được cho phép: - thực hiện thay đổi mạch lạc nhỏ nhất đáp ứng được nhiệm vụ; - giữ nguyên kiến trúc và các quy ước hiện có trừ khi nhiệm vụ chủ đích yêu cầu thay đổi chúng; - ưu tiên tận dụng các cơ chế sẵn có hơn là tạo thêm các lớp trừu tượng song song không cần thiết; - tránh tái cấu trúc, dọn dẹp, đổi tên, định dạng lại mã hàng loạt, thay đổi thư viện phụ thuộc (dependencies) hoặc cải tiến mang tính phỏng đoán không liên quan; - bảo toàn các thay đổi không liên quan nằm trong những tệp buộc phải chỉnh sửa cùng. Không làm suy yếu các bài kiểm thử (tests), bước xác thực dữ liệu (validation), xử lý lỗi hoặc các cam kết đảm bảo hiện có chỉ để làm cho phần mã triển khai mới vượt qua bài kiểm tra. ## 4. Bảo toàn trạng thái kho mã nguồn Không vứt bỏ công việc đang có sẵn chỉ để có được một kho mã nguồn sạch sẽ. Trừ khi được yêu cầu và cho phép một cách rõ ràng, không sử dụng các thao tác mang tính phá hủy hoặc viết lại lịch sử như: - `git reset` - `git restore` - `git stash` - `git clean` - rebase - amend - squash - các thao tác viết lại lịch sử khác Hãy làm việc khéo léo xung quanh trạng thái chưa sạch (dirty state) không liên quan thay vì xóa bỏ nó. ## 5. Kiểm chứng phần triển khai Sử dụng bước kiểm chứng liên quan nhỏ nhất trước, sau đó mới mở rộng khi phạm vi hoặc mức độ rủi ro đòi hỏi. Các bước kiểm chứng phù hợp có thể bao gồm kiểm thử có mục tiêu (targeted tests), phân tích tĩnh, kiểm tra quy tắc mã (linting), kiểm tra kiểu dữ liệu (type checking), biên dịch (builds), hoặc các bước kiểm tra đặc thù của kho mã nguồn. Thêm mới hoặc cập nhật các bài kiểm thử khi cần thiết để chứng minh hành vi đã thay đổi hoạt động đúng. Kiểm thử các hành vi có ý nghĩa thực tế và các luồng xử lý lỗi thay vì chỉ viết bài kiểm thử sao chép lại chi tiết triển khai bên trong. Không bao giờ tuyên bố đã kiểm chứng những gì thực tế chưa được thực hiện. Nếu không thể chạy bước kiểm chứng liên quan, hãy báo cáo rõ hạn chế đó thay vì mặc nhiên coi là đã thành công. ## 6. Chỉ tạo commit khi được cho phép Chỉ tạo các bản commit khi được nhiệm vụ hiện tại hoặc hướng dẫn của kho mã nguồn cho phép một cách rõ ràng. Khi được phép tạo commit: - mỗi commit phải đại diện cho một mối quan tâm (concern) mạch lạc duy nhất; - tách riêng các mối quan tâm không liên quan về triển khai tính năng, dọn dẹp mã, định dạng, tài liệu và tái cấu trúc trừ khi không thể tách rời; - giữ cho các commit có thể hiểu được một cách độc lập, dễ rà soát và có thể hoàn tác (revert) hợp lý; - sử dụng thông điệp commit (commit messages) có ý nghĩa rõ ràng. Trước mỗi lần commit: 1. kiểm tra trạng thái kho mã nguồn; 2. xác định chính xác những thay đổi nào thuộc về mối quan tâm đang xét; 3. chỉ đưa vào vùng chờ (stage) đúng những thay đổi đó; 4. kiểm tra lại bản so sánh khác biệt đã đưa vào vùng chờ (staged diff); 5. chỉ thực hiện commit sau khi đã xác nhận đúng phạm vi của nó. Ưu tiên việc stage tường minh theo từng tệp hoặc từng khối thay đổi (hunk). Không sử dụng lệnh stage diện rộng như `git add .` khi nó có nguy cơ gom nhầm cả các thay đổi không liên quan. Không viết lại các commit đã tồn tại trừ khi được yêu cầu rõ ràng. ## 7. Hoàn tất và bàn giao Trước khi tuyên bố hoàn tất việc triển khai: - xác nhận hành vi được yêu cầu và các tiêu chí nghiệm thu đã được giải quyết đầy đủ; - chạy bước kiểm chứng cuối cùng phù hợp; - kiểm tra bản diff cuối cùng để phát hiện các thay đổi vô tình hoặc không liên quan; - báo cáo trung thực các hạn chế chưa được giải quyết. Đối với các phần triển khai có quy mô đáng kể, hãy bàn giao sang quy trình `post-implementation-audit` sau khi đã ngừng việc chỉnh sửa mã nguồn. Nếu quá trình hậu kiểm (audit) báo cáo có các phát hiện lỗi (findings): 1. quay trở lại giai đoạn triển khai/khắc phục; 2. chỉ sửa các phát hiện có căn cứ bằng thay đổi mạch lạc nhỏ nhất; 3. kiểm chứng lại phần đã khắc phục; 4. chạy lại `post-implementation-audit`. Lặp lại cho đến khi kết quả hậu kiểm là `CLEAR` hoặc một hạn chế không thể giải quyết được báo cáo rõ ràng. Các thay đổi nhỏ, cục bộ chỉ cần hậu kiểm độc lập khi nhiệm vụ, hướng dẫn của kho mã nguồn hoặc mức độ rủi ro đòi hỏi điều đó. ## Đầu ra Khi hoàn tất, hãy báo cáo ngắn gọn: - những gì đã thay đổi; - các quyết định triển khai quan trọng; - các bước kiểm chứng thực tế đã thực hiện; - các hạn chế đáng kể hoặc vấn đề chưa được giải quyết; - các commit đã tạo, nếu có. Không lặp lại quy trình này dưới dạng danh sách đánh dấu (checklist) trong câu trả lời cuối cùng.
Quy trình i18n có thể tái sử dụng dành cho các tác tử lập trình (coding agents). Xác minh tính đầy đủ của ngôn ngữ bản địa (locale), văn bản gắn cứng (hardcoded text), biến giữ chỗ (placeholders), quy tắc số nhiều (pluralization), hành vi dự phòng (fallback), định dạng, tính nhất quán của bản dịch và các lỗi hồi quy giao diện liên quan đến bản địa hóa.
---
name: i18n-change-workflow
description: Quy trình i18n có thể tái sử dụng dành cho các tác tử lập trình (coding agents). Xác minh tính đầy đủ của ngôn ngữ bản địa (locale), văn bản gắn cứng (hardcoded text), biến giữ chỗ (placeholders), quy tắc số nhiều (pluralization), hành vi dự phòng (fallback), định dạng, tính nhất quán của bản dịch và các lỗi hồi quy giao diện liên quan đến bản địa hóa.
---
# Quy trình Thay đổi Đa ngôn ngữ (i18n)
Đóng vai trò là lớp chuyên gia i18n/l10n cho tác vụ đang thực hiện.
Kỹ năng này bổ sung các ràng buộc và bước xác minh chuyên biệt cho việc bản địa hóa. Nó không thay thế quy trình triển khai, kiểm toán, Git hoặc phê duyệt thông thường của kho lưu trữ. Hãy tuân theo ranh giới thay đổi dữ liệu (mutation boundary) của quy trình đang hoạt động: trong quá trình triển khai hoặc khắc phục, hãy áp dụng các thay đổi i18n bắt buộc; trong quá trình kiểm toán hoặc đánh giá chỉ đọc (read-only), hãy sử dụng các tiêu chí này mà không làm thay đổi trạng thái của kho lưu trữ.
## 1. Kiểm tra hệ thống i18n hiện có trước tiên
Trước khi thay đổi hành vi đã được bản địa hóa:
- đọc các tệp `AGENTS.md` có liên quan và tài liệu dự án;
- xác định thư viện i18n hiện tại hoặc cơ chế gốc của dự án;
- xác định các ngôn ngữ (locales) được hỗ trợ, ngôn ngữ nguồn/mặc định, vị trí lưu trữ tài nguyên ngôn ngữ, hành vi dự phòng (fallback) và logic lựa chọn/lưu giữ ngôn ngữ;
- kiểm tra các khóa (keys) hiện có lân cận và các vị trí gọi (call sites) trước khi chọn tên khóa hoặc cấu trúc mới;
- xác định các quy tắc đặc thù của dự án đối với việc dịch thuật, định dạng, tài nguyên được tạo tự động hoặc quy trình xác thực.
Ưu tiên kiến trúc hiện có của dự án. Không đưa vào một thư viện i18n mới, định dạng tài nguyên mới hoặc cơ chế dịch thuật song song trừ khi tác vụ yêu cầu điều đó và kho lưu trữ chưa có cơ chế hiện hành nào phù hợp.
Đừng coi quy ước của một framework là quy luật phổ quát. Hãy tuân theo các quy ước thực tế của kho lưu trữ.
## 2. Phân loại văn bản trước khi bản địa hóa
Xác định xem mỗi chuỗi văn bản được thay đổi có thực sự hiển thị cho người dùng (user-facing) hay không.
Các ứng viên bản địa hóa điển hình bao gồm:
- nhãn giao diện hiển thị, nút bấm, tiêu đề, menu, hộp thoại, trạng thái trống (empty states), thông báo xác thực và các lỗi hiển thị cho người dùng;
- nhãn và mô tả hỗ trợ tiếp cận (accessibility);
- thông báo đẩy và các thông điệp hệ thống hướng tới người dùng;
- văn bản gợi ý trong ô nhập (placeholders), văn bản trợ giúp, nội dung hướng dẫn làm quen (onboarding copy) và chú giải công cụ (tooltips);
- nội dung hiển thị cho người dùng được tạo ra từ các biểu mẫu (templates) thuộc sở hữu của ứng dụng.
Không tự động bản địa hóa:
- các định danh (identifiers), khóa dịch thuật, tên API, URL, đường dẫn, câu lệnh, truy vấn SQL, biểu thức chính quy (regex) hoặc các giá trị giao thức;
- nhật ký (logs) chỉ dành cho nhà phát triển, thông tin chẩn đoán, vết ngăn xếp (stack traces) và văn bản dữ liệu thử nghiệm (test fixtures);
- tên thương hiệu, tên sản phẩm, mã hiệu hoặc các thuật ngữ mà quy tắc dự án chủ đích giữ nguyên;
- nội dung thời gian chạy (runtime) được cung cấp từ bên ngoài trừ khi tác vụ nêu rõ phạm vi bao gồm cả phần đó.
Khi việc phân loại còn mơ hồ và ảnh hưởng đến ý nghĩa sản phẩm, hãy giữ nguyên hành vi hiện tại và nêu rõ sự mơ hồ đó thay vì đoán mò.
## 3. Bảo toàn mô hình khóa và tài nguyên của dự án
Đối với văn bản hiển thị cho người dùng mới hoặc được thay đổi:
- sử dụng cơ chế dịch thuật của dự án thay vì đưa vào văn bản hiển thị gắn cứng (hardcoded) khi việc bản địa hóa được kỳ vọng;
- tuân theo quy ước đặt tên khóa và phân chia không gian tên (namespacing) hiện có;
- ưu tiên các khóa mang ngữ nghĩa ổn định hơn là các khóa được tạo ra từ nguyên câu hiển thị trừ khi dự án chủ đích sử dụng khóa bằng văn bản nguồn;
- cập nhật mọi ngôn ngữ được hỗ trợ theo yêu cầu của quy tắc dự án hoặc tác vụ hiện tại;
- bảo toàn các mục ngôn ngữ không liên quan và dữ liệu chỉ dành cho ngôn ngữ đích trừ khi việc xóa bỏ được yêu cầu rõ ràng;
- không âm thầm đổi tên hoặc xóa các khóa hiện có chỉ vì mục đích đồng nhất về mặt phong cách.
Chỉ coi ngôn ngữ nguồn/mặc định đã khai báo của dự án là bản chuẩn chính quy nếu kho lưu trữ thực sự sử dụng mô hình đó.
Các bản dịch còn thiếu phải tuân theo chính sách dự phòng (fallback) đã được thiết lập của dự án. Không âm thầm tự bịa ra một chính sách dự phòng mới.
## 4. Bảo toàn phép nội suy biến, quy tắc số nhiều và cấu trúc thông điệp
Cấu trúc bản dịch là một phần của hợp đồng giao tiếp.
- Bảo toàn cùng các biến giữ chỗ (placeholders)/đối số bắt buộc trên tất cả các biến thể ngôn ngữ.
- Không dịch tên biến giữ chỗ, mã định dạng (format tokens), thẻ đánh dấu (markup) hoặc cú pháp điều khiển.
- Sử dụng cơ chế số nhiều/lựa chọn/ICU của dự án khi ngữ pháp phụ thuộc vào số lượng, giống, cách (case) hoặc các biến thiên nhạy cảm với ngôn ngữ khác.
- Tránh ghép các câu từ những mảnh được dịch rời rạc khi trật tự từ hoặc ngữ pháp có thể thay đổi theo từng ngôn ngữ.
- Tránh việc nối chuỗi (string concatenation) giả định theo trật tự từ hoặc khoảng trắng của tiếng Anh.
- Bảo toàn các thẻ đánh dấu, ký tự thoát (escaping) và ngữ nghĩa ngắt dòng có chủ đích.
Nếu một thông điệp nguồn thay đổi các đối số hoặc cấu trúc thông điệp của nó, hãy xác minh mọi ngôn ngữ bị ảnh hưởng thay vì chỉ cập nhật văn bản nguồn đang hiển thị.
## 5. Giữ cho các giá trị nhạy cảm với bản địa nhận biết được ngôn ngữ (locale-aware)
Khi giao diện được thay đổi có chứa các giá trị nhạy cảm với bản địa, hãy sử dụng các tiện ích định dạng nhận biết ngôn ngữ hiện có của dự án cho các hạng mục liên quan:
- ngày và giờ;
- số và tỷ lệ phần trăm;
- tiền tệ;
- đơn vị đo lường;
- thời gian tương đối;
- định dạng danh sách;
- các phân loại số nhiều.
Không gắn cứng các ký tự phân cách, quy ước dấu thập phân, thứ tự ngày tháng, vị trí đặt ký hiệu tiền tệ hoặc các giả định số nhiều chỉ dành cho tiếng Anh khi hành vi nhận biết ngôn ngữ được kỳ vọng.
## 6. Bảo vệ hành vi lựa chọn ngôn ngữ và dự phòng (fallback)
Khi tác vụ chạm đến việc chuyển đổi ngôn ngữ, khởi tạo, lưu giữ trạng thái hoặc dự phòng:
- bảo toàn danh sách ngôn ngữ được hỗ trợ và các quy tắc chuẩn hóa của dự án;
- xác minh hành vi của ngôn ngữ mặc định;
- xác minh việc lưu giữ trạng thái nếu dự án có lưu lại lựa chọn ngôn ngữ của người dùng;
- xác minh rằng các ngôn ngữ không được hỗ trợ hoặc bị thiếu sẽ giảm cấp (degrade) thông qua luồng dự phòng dự kiến;
- tránh tình trạng giao diện pha trộn nhiều ngôn ngữ do thiếu khóa hoặc dữ liệu ngôn ngữ lưu đệm (cache) đã cũ;
- đảm bảo các tài nguyên ngôn ngữ tải trễ (lazy-loaded) được chờ (awaited) hoặc đồng bộ hóa chính xác khi áp dụng.
Không thay đổi thứ tự ưu tiên phát hiện ngôn ngữ nếu không có yêu cầu rõ ràng.
## 7. Chất lượng bản dịch
Khi tạo mới hoặc chỉnh sửa các bản dịch:
- bảo toàn ý nghĩa, chủ đích, giọng điệu và thuật ngữ sản phẩm thay vì dịch máy móc từng từ một;
- sử dụng ngữ cảnh giao diện xung quanh để giải quyết các nhãn ngắn có nghĩa mơ hồ;
- bảo toàn tên sản phẩm đã được phê duyệt, thuật ngữ kỹ thuật và các quyết định trong bảng thuật ngữ (glossary);
- giữ nguyên vẹn các biến giữ chỗ và thẻ đánh dấu;
- tránh thêm vào các tuyên bố, ý nghĩa, mức độ lịch sự hoặc chức năng không có trong bản gốc;
- gắn cờ cho các cách diễn đạt không chắc chắn, nhạy cảm về văn hóa, pháp lý, quan trọng về an toàn hoặc nhạy cảm về thương hiệu để con người xác nhận thay vì tỏ ra chắc chắn giả tạo.
Nếu kho lưu trữ có chứa bảng thuật ngữ, tệp thuật ngữ, bộ nhớ dịch thuật (translation memory) hoặc các bản dịch đã được thiết lập từ trước, hãy ưu tiên bằng chứng đó hơn là một phương án thay thế mới được tạo ra.
Hãy đọc `references/i18n-review-checklist.md` khi thực hiện việc bổ sung ngôn ngữ diện rộng, rà soát bản dịch hoặc thay đổi bản địa hóa hướng tới phát hành.
## 8. Kiểm tra rủi ro về giao diện và bố cục
Văn bản đã bản địa hóa có thể làm thay đổi bố cục ngay cả khi bản dịch hoàn toàn chính xác.
Đối với giao diện bị ảnh hưởng, hãy xem xét khi có liên quan:
- các nhãn dài hơn và việc ngắt xuống nhiều dòng;
- chiều rộng màn hình di động hẹp và bố cục đáp ứng (responsive layouts);
- quy tắc ngắt dòng cho chữ CJK (Trung-Nhật-Hàn) và độ bao phủ ký tự (glyph coverage);
- việc cắt ngắn văn bản (truncation) và dấu ba chấm lửng (ellipsis);
- các nút bấm, tab, huy hiệu (badges), hộp thoại, bảng biểu và các khung chứa có chiều rộng cố định;
- phông chữ dự phòng (font fallback);
- các nhãn hỗ trợ tiếp cận (accessibility labels);
- hướng viết từ phải sang trái (RTL), việc lật gương giao diện và các thuộc tính CSS/bố cục logic khi phạm vi công việc có bao gồm ngôn ngữ RTL.
Không thêm phần việc chuyên biệt cho RTL khi không có ngôn ngữ RTL nào được hỗ trợ hoặc được yêu cầu, nhưng đừng bỏ qua nó khi ngôn ngữ RTL là một phần của tác vụ.
Sử dụng bước xác minh trực quan hoặc kiểm tra giao diện khi văn bản thay đổi có khả năng ảnh hưởng đến bố cục. Chỉ riêng việc kiểm tra tệp ngôn ngữ thành công không chứng minh được rằng giao diện hiển thị đúng.
## 9. Xác minh bằng các bước kiểm tra gốc của dự án
Ưu tiên sử dụng các trình xác thực i18n, bài kiểm thử, công cụ linter, bản dựng (builds) và bước kiểm tra giao diện hiện có của kho lưu trữ trước tiên.
Xác minh tập hợp con có liên quan trong số các mục sau:
- tính đầy đủ/tương đương của các khóa ngôn ngữ;
- các bản dịch bị thiếu hoặc để trống;
- sự tương đương về biến giữ chỗ/đối số;
- cấu trúc số nhiều/lựa chọn (plural/select);
- hành vi dự phòng (fallback);
- việc chuyển đổi và lưu giữ ngôn ngữ;
- định dạng nhận biết ngôn ngữ;
- không xuất hiện thêm các chuỗi văn bản hiển thị cho người dùng bị gắn cứng (hardcoded) mới trong phạm vi được thay đổi;
- tình trạng hoạt động tốt của build/type/lint/test;
- hành vi bố cục đối với các màn hình bị ảnh hưởng.
Đối với các danh mục ngôn ngữ dạng JSON thuần túy, có thể sử dụng `scripts/check_json_locales.py` như một bước kiểm tra tất định bổ sung. Kịch bản này kiểm tra các khóa JSON trùng lặp, sự tương đương khóa giữa các tệp, kiểu dữ liệu của giá trị, các chuỗi rỗng và sự tương đương của các biến giữ chỗ đặt tên trong ngoặc nhọn phổ biến/đối số ICU. Việc phát hiện biến giữ chỗ được chủ đích giữ ở phạm vi hẹp và mang tính phỏng đoán (heuristic); hãy xác nhận lại các điểm lệch được báo cáo đối chiếu với cú pháp thông điệp thực tế của dự án. Đây không phải là bước đánh giá dịch thuật về mặt ngữ nghĩa và không thay thế bộ công cụ gốc của dự án.
Đừng tuyên bố toàn bộ kho lưu trữ đã hoàn thiện i18n chỉ dựa trên việc kiểm tra một tệp hẹp hoặc kiểm tra tĩnh.
## 10. Tiêu chí hoàn thành
Một thay đổi i18n chỉ được coi là hoàn tất khi, đối với phạm vi được yêu cầu:
- các chuỗi văn bản hiển thị cho người dùng dự kiến đã sử dụng cơ chế bản địa hóa của dự án;
- các tài nguyên ngôn ngữ bắt buộc đã được cập nhật;
- các biến giữ chỗ và cấu trúc thông điệp vẫn tương thích;
- hành vi định dạng/dự phòng/chuyển đổi ngôn ngữ có liên quan được bảo toàn;
- bước xác minh gốc của dự án vượt qua thành công, hoặc các giới hạn được báo cáo rõ ràng;
- các lỗi hồi quy bố cục có khả năng xảy ra đã được kiểm tra khi giao diện bị ảnh hưởng;
- sự mơ hồ chưa được giải quyết về bản dịch hoặc ngôn ngữ sản phẩm được báo cáo lại thay vì đoán mò.
Giữ cho báo cáo cuối cùng ngắn gọn. Nêu rõ hành vi ngôn ngữ nào đã thay đổi, những ngôn ngữ/tài nguyên nào đã được chạm tới, bước xác thực nào thực sự đã chạy và bất kỳ giới hạn nào còn tồn đọng về dịch thuật hoặc giao diện.
FILE:references/i18n-review-checklist.md
# Danh sách Kiểm tra Đánh giá i18n
Sử dụng tài liệu tham khảo này khi bổ sung ngôn ngữ trên diện rộng, đánh giá bản dịch hoặc thực hiện công việc bản địa hóa hướng tới phát hành. Chỉ áp dụng các mục có liên quan đến dự án và phạmび được yêu cầu.
## Độ bao phủ
- Kiểm kê các bề mặt hiển thị cho người dùng nằm trong phạm vi.
- Xác nhận mọi ứng viên dịch thuật dự kiến đều được biểu diễn bằng cơ chế i18n của dự án.
- Phân biệt việc chủ đích giữ nguyên ngôn ngữ nguồn với văn bản vô tình chưa được dịch.
- Báo cáo các bề mặt động/bên ngoài/phi văn bản không thể xác minh được từ tài nguyên trong kho lưu trữ.
## Tính toàn vẹn của tài nguyên
- Các khóa bắt buộc đều tồn tại trong các ngôn ngữ thuộc phạm vi của tác vụ.
- Không có mục ngôn ngữ không liên quan nào bị xóa hoặc bị viết lại.
- Kiểu dữ liệu của giá trị khớp nhau ở những nơi định dạng tài nguyên yêu cầu chúng phải khớp.
- Các bản dịch để trống là có chủ đích hoặc đã được báo cáo.
- Các tài nguyên ngôn ngữ tự động sinh ra chỉ được tạo lại thông qua câu lệnh được dự án phê duyệt.
## Hợp đồng thông điệp (Message contracts)
- Các biến giữ chỗ có tên và các đối số ICU/select được bảo toàn.
- Thẻ đánh dấu, ký tự thoát, mã định dạng và các chỗ ngắt dòng có chủ đích vẫn hợp lệ.
- Các nhánh số nhiều/lựa chọn tuân theo quy tắc của thư viện và ngôn ngữ trong dự án.
- Các câu không được ghép nối từ những mảnh rời rạc giả định theo trật tự từ của ngôn ngữ nguồn.
## Chất lượng ngôn ngữ
- Ý nghĩa và chủ đích hướng tới người dùng khớp với bản gốc.
- Thuật ngữ nhất quán với ngôn ngữ sản phẩm hiện có và các quyết định trong bảng thuật ngữ.
- Các nhãn ngắn được diễn giải bằng ngữ cảnh màn hình/hành động chứ không dịch cô lập.
- Giọng điệu, mức độ trang trọng, quy tắc viết hoa và dấu câu phù hợp với ngôn ngữ đích và giọng văn hiện có của sản phẩm.
- Tên thương hiệu/sản phẩm và các thuật ngữ chủ đích giữ nguyên không bị thay đổi.
- Sự mơ hồ có rủi ro cao được nêu ra để con người xác nhận.
## Hành vi ngôn ngữ bản địa
Khi áp dụng, hãy xác minh:
- ngôn ngữ mặc định;
- việc chuyển đổi ngôn ngữ tường minh;
- việc lưu giữ trạng thái qua các lần tải lại trang/khởi động lại;
- cơ chế dự phòng khi gặp ngôn ngữ không được hỗ trợ;
- cơ chế dự phòng khi thiếu khóa;
- hành vi của tài nguyên tải trễ (lazy-loaded);
- định dạng ngày/giờ/số/tiền tệ/đơn vị đo lường;
- việc chuẩn hóa mã ngôn ngữ chẳng hạn như `en-US` so với `en` theo quy tắc của dự án.
## Giao diện và khả năng tiếp cận (Accessibility)
Khi bị ảnh hưởng, hãy kiểm tra:
- hiện tượng tràn khung (overflow) trên màn hình hẹp;
- việc tự động xuống dòng, cắt ngắn văn bản và các khung chứa có chiều cao cố định;
- các nút bấm/tab/huy hiệu khi bản dịch dài hơn;
- việc ngắt dòng chữ CJK và bộ ký tự phông chữ (font glyphs);
- các nhãn dành cho trình đọc màn hình/hỗ trợ tiếp cận;
- hướng viết RTL và việc lật gương giao diện chỉ khi các ngôn ngữ RTL nằm trong phạm vi công việc.
## Bằng chứng và các giới hạn
Một bước kiểm tra tài nguyên vượt qua thành công chỉ chứng minh được những gì nó thực sự kiểm tra. Tự bản thân nó không chứng minh được:
- chất lượng bản dịch;
- việc chuyển đổi ngôn ngữ lúc chạy thực tế (runtime);
- tính đúng đắn về mặt thị giác;
- độ bao phủ hoàn toàn đối với nội dung nội tuyến/động/phi văn bản;
- nội dung bên ngoài/CMS là chính xác.
Hãy nêu rõ những giới hạn đó khi chúng đóng vai trò quan trọng.
FILE:scripts/check_json_locales.py
#!/usr/bin/env python3
"""Deterministic structural checks for JSON locale catalogs.
Checks:
- duplicate object keys while parsing
- missing/extra leaf paths relative to a source locale
- source/target leaf type mismatches
- blank target strings
- common named placeholder / ICU argument parity
This intentionally does not judge translation quality and is not a general
hardcoded-string scanner.
"""
from __future__ import annotations
import argparse
import json
import re
import sys
from pathlib import Path
from typing import Any, TypeAlias
ARG_RE = re.compile(r"\{\s*([A-Za-z_][A-Za-z0-9_.-]*)\s*(?:[,}])")
PathPart: TypeAlias = str | int
JSONPath: TypeAlias = tuple[PathPart, ...]
class JSONObjectPairs(list):
"""Marker type preserving JSON object pairs so duplicates remain detectable."""
def _object_pairs_hook(pairs: list[tuple[str, Any]]) -> JSONObjectPairs:
return JSONObjectPairs(pairs)
def path_label(path: JSONPath) -> str:
"""Render an unambiguous JSON-style path without conflating dots in keys."""
if not path:
return "$"
pieces: liststr = []
for part in path:
if isinstance(part, int):
pieces.append(f"[{part}]")
else:
pieces.append(f"[{json.dumps(part, ensure_ascii=False)}]")
return "$" + "".join(pieces)
def _normalize_json(value: Any, path: JSONPath = ()) -> Any:
if isinstance(value, JSONObjectPairs):
out: dict[str, Any] = {}
seen: setstr = set()
for key, child in value:
if key in seen:
raise ValueError(f"duplicate key at {path_label(path + (key,))}")
seen.add(key)
out[key] = _normalize_json(child, path + (key,))
return out
if isinstance(value, list):
return [
_normalize_json(child, path + (index,))
for index, child in enumerate(value)
]
return value
def load_json(path: Path) -> Any:
try:
with path.open("r", encoding="utf-8") as f:
raw = json.load(f, object_pairs_hook=_object_pairs_hook)
return _normalize_json(raw)
except (OSError, json.JSONDecodeError, ValueError) as exc:
raise ValueError(f"{path}: {exc}") from exc
def flatten(value: Any, path: tuple[str, ...] = ()) -> dict[tuple[str, ...], Any]:
"""Flatten JSON objects using tuple paths so literal dots in keys stay distinct."""
out: dict[tuple[str, ...], Any] = {}
if isinstance(value, dict):
for key, child in value.items():
out.update(flatten(child, path + (key,)))
else:
outpath = value
return out
def value_kind(value: Any) -> str:
if isinstance(value, bool):
return "boolean"
if value is None:
return "null"
if isinstance(value, str):
return "string"
if isinstance(value, (int, float)):
return "number"
if isinstance(value, list):
return "array"
return type(value).__name__
def arguments(value: Any) -> setstr:
if not isinstance(value, str):
return set()
return set(ARG_RE.findall(value))
def check_pair(source_path: Path, target_path: Path, allow_extra: bool) -> int:
source = flatten(load_json(source_path))
target = flatten(load_json(target_path))
findings: list[tuple[str, str]] = []
source_keys = set(source)
target_keys = set(target)
for key in sorted(source_keys - target_keys):
findings.append(("ERROR", f"missing key: {path_label(key)}"))
if not allow_extra:
for key in sorted(target_keys - source_keys):
findings.append(("WARN", f"extra key: {path_label(key)}"))
for key in sorted(source_keys & target_keys):
src = source[key]
dst = target[key]
label = path_label(key)
src_kind = value_kind(src)
dst_kind = value_kind(dst)
if src_kind != dst_kind:
findings.append(
("ERROR", f"type mismatch at {label}: source={src_kind}, target={dst_kind}")
)
continue
if isinstance(dst, str) and dst.strip() == "":
findings.append(("WARN", f"blank target string: {label}"))
src_args = arguments(src)
dst_args = arguments(dst)
if src_args != dst_args:
missing = sorted(src_args - dst_args)
extra = sorted(dst_args - src_args)
details: liststr = []
if missing:
details.append(f"missing={missing}")
if extra:
details.append(f"extra={extra}")
findings.append(("ERROR", f"argument mismatch at {label}: {', '.join(details)}"))
print(f"SOURCE: {source_path}")
print(f"TARGET: {target_path}")
if not findings:
print("PASS: no structural findings")
return 0
for severity, message in findings:
print(f"{severity}: {message}")
errors = sum(1 for severity, _ in findings if severity == "ERROR")
warnings = sum(1 for severity, _ in findings if severity == "WARN")
print(f"SUMMARY: {errors} error(s), {warnings} warning(s)")
return 1 if errors else 0
def main() -> int:
parser = argparse.ArgumentParser(
description="Check JSON locale catalogs for structural parity."
)
parser.add_argument("source", type=Path, help="source/default locale JSON")
parser.add_argument("targets", nargs="+", type=Path, help="target locale JSON file(s)")
parser.add_argument(
"--allow-extra",
action="store_true",
help="do not warn about target-only keys",
)
args = parser.parse_args()
try:
statuses = [check_pair(args.source, target, args.allow_extra) for target in args.targets]
except ValueError as exc:
print(f"ERROR: {exc}", file=sys.stderr)
return 2
return 1 if any(status != 0 for status in statuses) else 0
if __name__ == "__main__":
raise SystemExit(main())
FILE:README.md
# i18n-change-workflow
Agent Skill cục bộ trong kho lưu trữ giúp thực hiện các thay đổi i18n/l10n một cách an toàn.
Vị trí đề xuất:
`.agents/skills/i18n-change-workflow/`
Trình kiểm tra JSON tùy chọn được chủ đích thiết kế hẹp và mang tính tất định. Nó phát hiện các khóa JSON bị trùng lặp và các điểm lệch cấu trúc, cộng với các điểm lệch biến giữ chỗ dạng ngoặc nhọn phổ biến theo phương pháp phỏng đoán (heuristic); nó không dịch văn bản hay tuyên bố đảm bảo tính hoàn chỉnh về mặt ngữ nghĩa/thị giác.