Nâng gói: tra Context7 trước khi đổi phiên bản
Luật UI của repo bắt "luôn dùng bản mới nhất". Trang này nói cách nâng mà không làm vỡ CI một cách lặng lẽ: tra tài liệu của bản đích trước, kiểm bằng thứ CI không kiểm sau.
Trạng thái 14.10.2026
.mcp.json(servercontext7) đã có trong repo. Mỗi máy phải duyệt một lần ở phiên đầu tiên; chưa duyệt thì không có toolmcp__context7__*.- Môi trường cloud chưa có
mcp.context7.comtrong allowlist mạng (việc của chủ repo, xem cuối trang). Tới khi thêm, phiên cloud không có Context7: hook chặn một lần rồi cho qua kèm cảnh báo nêu gói chưa tra. - Chưa có khoá API: Context7 chạy ẩn danh, hạn mức thấp hơn, vẫn dùng được.
npm run check:lockfilesvànpm run check:hookschỉ chạy ở CI sau khi khai vào jobchangescủaci-checks.yml(gói thay đổi 14.10.2026 trả đoạn khai cho người tích hợp).
Vì sao: TypeScript 7, xanh ở máy, đỏ ở CI
Ngày 03.10.2026 đợt nâng major đưa TypeScript lên 7. Ở www và mam, typegen, tsc và build đều xanh ở máy; rồi npm ci ERESOLVE ở CI vì @react-router/dev 7.18 khai peer typescript ^5 || ^6. typescript-eslint sập ở ba site khác vì gói TS 7 không còn JS API. Cả hai điều đó nằm sẵn trong changelog và bảng peer. Thứ thiếu là thói quen đọc trước khi nâng (known-risks).
Kiểm lại 14.10.2026 thấy chuyện còn khó thấy hơn: mọi job build và bước deploy cài bằng npm ci --legacy-peer-deps || { warning; npm install; }. --legacy-peer-deps bỏ qua peer, nên một xung đột y hệt TS 7 không làm đỏ job build nào. Thử trên mẫu typescript-eslint 8.71 + TypeScript 7.0: bản nghiêm exit 1 ERESOLVE, bản --legacy-peer-deps exit 0.
Quy trình
| Bước | Làm gì | Bắt cái gì |
|---|---|---|
| 1 | Context7: resolve-library-id rồi query-docs cho changelog / migration guide của bản đích | breaking change, API bị bỏ |
| 2 | Đọc bảng peer: npm view <gói>@<bản> peerDependencies, và của các gói khai peer lên gói mình nâng | kiểu lỗi TS 7 |
| 3 | Đối chiếu .claude/memory/deps-upgrade-holdbacks.md | nâng nhầm gói đang giữ có lý do |
| 4 | Nâng packages/* cùng lượt với site dùng nó; họ gói nâng cùng một số | hai bản @types/react |
| 5 | npm ci --dry-run --legacy-peer-deps=false ở mọi thư mục đổi, hoặc npm run check:lockfiles | xung đột peer, lockfile lệch |
| 6 | typecheck + build ở mọi thư mục đổi | vite build không kiểm type |
Giữ một gói ở bản cũ thì ghi lý do bằng khoá "//<tên gói>" ngay trong package.json (npm bỏ qua khoá bắt đầu bằng //), và thêm một dòng vào file memory. Ví dụ đang có: //typescript ở www, mam, play/frontend, video2; //mermaid ở docs.
Hai luật đi kèm: wrangler ở lại package.json của sáu Worker (CI deploy bằng npx --no-install wrangler), và vòng QC đêm không bao giờ nâng gói (skill nightly-qc).
Gói đang giữ bản cũ (14.10.2026)
| Gói | Ở đâu | Vì sao | Gỡ khi |
|---|---|---|---|
| TypeScript 5 | www, mam | @react-router/dev 7.x chỉ nhận TS 5/6 | nâng cả họ React Router lên 8 (8.4 đã nhận TS 7), một việc major |
| TypeScript 6 | play/frontend, video2 | typescript-eslint 8.71 khai <6.1.0, cần JS API | typescript-eslint nhận TS 7 |
| mermaid 11 | docs | vitepress-plugin-mermaid 2.0.17 chỉ nhận 10/11 | plugin nhận 12 |
Bản đầy đủ, kèm những mục đã hết hạn và vì sao: .claude/memory/deps-upgrade-holdbacks.md. Trước 14.10.2026 danh sách này chỉ nằm trên máy chủ repo, nên phiên cloud và vòng đêm chưa từng thấy nó, trong khi skill nightly-qc vẫn trỏ tới nó như thể nó có trong repo.
Hook deps-docs-guard
scripts/hooks/deps-docs-guard.mjs, hook PreToolUse của Claude Code (Edit|Write|MultiEdit|Bash). Khi lượt sắp chạy đổi phiên bản một gói mà tác nhân đang chạy lệnh chưa tra Context7 cho chính gói đó, nó chặn một lần và nói ba việc phải làm.
| Bị coi là đổi phiên bản | Không bị coi |
|---|---|
sửa package.json thêm hay đổi bản ở dependencies, devDependencies, peerDependencies, optionalDependencies, overrides; gỡ một override | đổi scripts, khoá //, version của chính gói, chuyển section giữ nguyên bản, gỡ một gói |
npm/pnpm/yarn install|i|add|update|up|upgrade <gói>, cả trong for … do, if … then, bash -c, xargs | npm ci, npm install trần, -g, --no-save, xargs -I{} npm install --prefix {} |
ncu -u (qua npx, npm exec, dlx), npm audit fix, pnpm up --latest, npm pkg set devDependencies.<gói>= | chữ "npm i" trong commit message hay heredoc; npm update trần; npm pkg set scripts.* |
ghi package.json bằng shell: sed -i/perl -i có dáng phiên bản, >, tee, sponge, mv/cp đè, node -e … writeFileSync | sed -n, cat, jq chỉ đọc; sed -i sửa scripts |
"Đã tra" nghĩa là gì. Trong transcript của chính tác nhân đó có một lượt gọi resolve-library-id / query-docs thoả cả ba điều:
- Là khối
tool_usethật, không phải tên tool nằm trong danh sách tool hoãn. - Nhắc tới gói trong
libraryName,libraryIdhayquery. Gói có scope tính theo scope (@react-router/devkhớp/remix-run/react-router,@sentry/cloudflarekhớpSentry),@types/xtính theox, gói cùng họ tính theo thư viện đã tra (traReactphủreact-dom). Trườnglanguagekhông được đọc. - Có kết quả thật: không
is_error, không mở đầu bằngInvalid API keyhay câu lỗi tương tự, và dài từ 200 ký tự (kết quả thật ngắn nhất trong 50 lượt tra của repo là 665 ký tự).
Subagent chỉ được tính lượt tra trong transcript của nó. Lệnh không nêu được tên gói (ncu -u, audit fix, --latest, ghi package.json bằng shell mà không có cặp "tên": "bản") chỉ đòi một lượt tra thành công bất kỳ, và luôn in ghi chú rằng hook không kiểm được tra đúng gói.
Chống kẹt: chặn tối đa một lần cho mỗi tác nhân trong phiên (file đánh dấu theo session_id + agent_id); không đọc được transcript hay không ghi được file đánh dấu thì cho qua. File đánh dấu chỉ được xét sau transcript: đã tra đủ thì cho qua im lặng, chưa tra thì lần sau cho qua kèm một dòng systemMessage nêu đúng gói còn thiếu. Nhờ vậy "đã tuân thủ" và "đang lách" không bao giờ ra cùng một câu.
Bẫy khi viết hook này, hai thế hệ, cùng một loại "gác xanh mà chưa kiểm gì":
- Tìm chuỗi thô. Transcript chứa sẵn danh sách tool hoãn và schema tool. Trong 74 transcript của repo, 64 có chuỗi
__query-docs, chỉ 1 có lượt gọi thật. - Đếm lượt gọi bất kỳ (bản đầu của chính hook này, rà soát 14.10.2026 bắt được). Trong một phiên workflow, phiên cha tra tài liệu Sentry; subagent không tra gì;
npm i -D typescript@7của subagent qua im lặng, đúng sự cố TS 7. Tệ hơn,query-docsgửi kèm"language":"TypeScript", nên một phép "input có nhắc tên gói không" ngây thơ cũng cho lượt tra Sentry phủ góitypescript. Và một lượt tra trảInvalid API keyvẫn được tính như đã đọc.
Quét transcript 79 MB (không lượt tra nào, phải đọc hết): khoảng 200 ms cả tiến trình hook; lệnh không đổi phiên bản dừng trước khi đọc transcript, khoảng 60 ms.
npm run check:hooks (scripts/check-hooks.mjs) kiểm chính các hook: lệnh trỏ tới file có thật, mọi file hook đều được khai, node --check sạch, --self-test xanh (deps-docs-guard: 80 mẫu). Trước 14.10.2026 không gì kiểm hook, và một hook hỏng chỉ là "lỗi không chặn" mà Claude Code in ở chế độ verbose. Lệnh hook dùng đường dẫn tương đối thì gác in ghi chú; cả ba cách viết "$CLAUDE_PROJECT_DIR/…", "${CLAUDE_PROJECT_DIR}/…" và "$CLAUDE_PROJECT_DIR"/… đều được nhận.
Context7: dựng ở máy và ở cloud
.mcp.json ở gốc repo:
{ "mcpServers": { "context7": { "type": "http", "url": "https://mcp.context7.com/mcp",
"headers": { "CONTEXT7_API_KEY": "${CONTEXT7_API_KEY:-}" } } } }Không có bí mật nào trong file. ${CONTEXT7_API_KEY:-} thay bằng chuỗi rỗng khi biến không đặt, và máy chủ Context7 nhận header rỗng như ẩn danh (thử trực tiếp 14.10.2026).
Phải có :-. Viết ${CONTEXT7_API_KEY} không mặc định thì khi biến không đặt, Claude Code chỉ in cảnh báo trong phần chẩn đoán của claude mcp list rồi gửi nguyên chữ ${CONTEXT7_API_KEY} làm khoá. claude mcp list vẫn báo Connected, và mọi lượt gọi trả về một kết quả bình thường có nội dung Invalid API key, không cờ lỗi nào. Khoá sai cũng ra đúng câu đó.
| Ở đâu | Làm gì |
|---|---|
| Máy | Phiên đầu tiên trong repo hỏi duyệt server context7 từ .mcp.json: đồng ý. Chủ repo đã có Context7 qua connector tài khoản (mcp__<uuid>__*); hai nguồn cùng có cũng không sao, hook tính cả hai. |
| Máy, có khoá | Đặt CONTEXT7_API_KEY trong shell profile; hoặc ghi đè riêng máy mình: claude mcp add --scope local --transport http context7 https://mcp.context7.com/mcp --header "CONTEXT7_API_KEY: <khoá>" (scope local thắng scope project, không vào git). |
| Cloud | .mcp.json theo clone. Thêm mcp.context7.com vào allowlist mạng (mức Custom). Khoá, nếu có: biến môi trường CONTEXT7_API_KEY trong hộp thoại môi trường. |
Kiểm nhanh: claude mcp list phải ra context7 ... ✔ Connected, rồi gọi thử resolve-library-id một lần: nội dung trả về phải là danh sách thư viện, không phải Invalid API key.
Việc chỉ chủ repo làm được
- Cloud: thêm
mcp.context7.comvào allowlist mạng của môi trường ở claude.ai/code (Network access: Custom, giữ "also include default list"). Xem cloud-dev. - Máy: duyệt server
context7ở lần đầu được hỏi (hoặc từ chối nếu chỉ muốn dùng connector). - Tuỳ chọn, khoá API: tạo khoá ở bảng điều khiển Context7, đặt vào biến
CONTEXT7_API_KEY(shell profile ở máy, hộp thoại môi trường ở cloud). Không bao giờ ghi khoá vào.mcp.json. - Tuỳ chọn, tự duyệt: thêm
"enabledMcpjsonServers": ["context7"]vào.claude/settings.jsonđể mọi máy khỏi bị hỏi. Đây là đổi cấu hình tin cậy cho mọi người clone repo, nên không làm thay chủ repo. - Tích hợp CI: khai
check:hooksvàcheck:lockfilesvào jobchangesvàcheck:guards.
Xem thêm: skill deps-upgrade, design-system, guardrails & vòng đêm.