Daniel Nguyen
← Tất cả dự án

Trang portfolio này được làm như thế nào

Portfolio này được làm như một dự án production nhỏ: AI agent viết theo một brief bằng văn bản, một leak gate fail-closed kiểm tra mọi bản build, và Lighthouse đo kết quả.

Personal tool
0 rò rỉ lọt raquét fail-closed ở mọi bản build
Mảng
Chính portfolio này
Thời gian
2026
Vai trò
Thiết kế, xây dựng và đưa lên production
Mã nguồn
GitHub
  • Astro
  • TypeScript
  • CSS
  • Canvas 2D
  • SVG
  • Node test runner
  • Lighthouse

Các phần ghép với nhau thế nào

Trang portfolio này được làm như thế nàoIntake agents read company repos read-only and split each area into a public note and a private denylist; writer agents use only the notes, and the leak gate checks the build against the denylists before any upload.Company reposbên ngoàiIntake agentsworkerPublic noteskho dữ liệuPrivate denylistskho dữ liệuWriter agentsworkerStatic builddịch vụLeak gatekiểm tra, cảnh báoDirect uploadbên ngoàiread-onlypattern-level factsevery identifier seenone written briefEN + VI + diagramswhole outputterms, local onlyonly on 0 matches
Intake agents read company repos read-only and split each area into a public note and a private denylist; writer agents use only the notes, and the leak gate checks the build against the denylists before any upload.
  • Bên ngoài
  • Worker
  • Kho dữ liệu
  • Dịch vụ
  • Kiểm tra, cảnh báo

Vấn đề

Phần lớn công việc tốt nhất của tôi nằm trong các repository riêng của công ty, có NDA. Tôi muốn kể chi tiết về những việc đó, có sơ đồ và số liệu, mà không để lộ một tên bảng, tên khách hàng hay tên module nội bộ nào.

Tôi cũng muốn làm trang này theo đúng cách tôi làm phần mềm production: AI agent viết phần lớn nội dung, còn test, gate và quyết định của tôi giữ cho kết quả đúng.

Kết quả là một site Astro tĩnh, song ngữ Anh và Việt, có 14 case study lúc ra mắt, một mô phỏng isometric bằng canvas ở phần đầu trang, các sơ đồ SVG vẽ từ những file JSON nhỏ, và hai widget tương tác. Astro là dependency duy nhất.

Tôi đã làm gì

Mỗi phần intake ra hai file: ghi chú public và denylist riêng

Các phiên AI agent đọc từng repository của công ty ở chế độ chỉ đọc. Với mỗi mảng, agent viết hai file: một bản tóm tắt public ở mức pattern, và một denylist riêng chứa mọi định danh đặc thù của công ty mà nó thấy, như bảng, module, repo, domain, khách hàng, tên người. Repository không bị sửa gì. Đổi lại là thêm việc cho mỗi mảng, nhưng lời nhắc “cẩn thận” giờ là một danh sách mà máy kiểm tra được.

Các writer agent chạy song song theo một brief chung

Năm content-writer agent chạy song song từ một brief cố định giọng văn, cấu trúc mục, schema frontmatter và quy tắc về số liệu. Mỗi agent tạo một trang tiếng Anh, một trang tiếng Việt và một spec sơ đồ. Agent không hỏi tôi được, nên mọi câu hỏi còn mở được ghi vào file kèm một giá trị mặc định đề xuất, và tôi trả lời theo từng đợt. Agent còn sửa CV của tôi ở những chỗ lịch sử repo nói khác: một claim hóa ra thuộc về repo khác nên đã được ẩn, và một con số mâu thuẫn được đưa ra để tôi chốt lại. Tôi chấp nhận review chậm hơn để agent không bao giờ bị kẹt.

Leak gate fail-closed, viết test trước

Bước build chạy Astro build rồi quét toàn bộ output với khoảng 550 term trong denylist riêng. Thiếu thư mục denylist hoặc không nạp được term nào thì gate thoát với lỗi, nên nó không thể vô tình cho qua. Chỉ cần một match là build fail và deploy không chạy. Việc so khớp dựa trên token nguyên vẹn: văn bản được tách ra từng từ, kể cả ở ranh giới camelCase và dấu câu, dấu tiếng Việt được bỏ đi, rồi các cụm ngắn gồm vài từ liền nhau được so với từng term đã chuẩn hóa. Cách viết có dấu cách, snake_case hay camelCase của cùng một term đều bị bắt, còn một term nằm lẫn trong một từ dài hơn, không liên quan thì không.

Bản build đầy đủ đầu tiên báo 60 match. Tôi xem xét từng cái. Hai cái là cụm từ thật trong nội dung, tôi viết lại. Số còn lại là false positive, và mỗi loại có một quy tắc mới, viết dưới dạng test fail trước: allowlist cho danh tính public của chính tôi, phân biệt định danh code ASCII với từ tiếng Việt bình thường có dấu, bỏ qua markup của framework như danh sách class, và tên biến JS đã minify trùng với các mã ba ký tự. Site có 17 test tự động, 14 cho leak scan và 3 cho một plugin nhỏ dàn layout các mục của case study, tất cả đều được thấy fail trước khi có code.

Chỉ deploy từ máy local

Denylist không bao giờ rời khỏi máy tôi, nên một bản build CI từ Git không chạy được gate. Mỗi lần deploy là build, quét và upload trực tiếp từ máy local. Tôi bỏ sự tiện lợi của push-to-deploy để giữ các term riêng tư ở lại trong máy.

Đo trước, rồi sửa đúng chỗ số liệu chỉ ra

Với một trang case study trên mobile, điểm Performance của Lighthouse tăng từ 79 lên 87–98 tùy lần chạy, First Contentful Paint giảm từ 3,9 s xuống khoảng 1,5–2 s, nhờ inline CSS và tải web font mà không chặn việc vẽ trang. Layout shift trên desktop giảm từ 0,111 xuống 0,003 nhờ font dự phòng local được chỉnh kích thước khớp với web font. SEO tăng từ 92 lên 100 sau khi thêm robots.txt, sitemap tự sinh và một trang 404 thật.

Kết quả

Site lên production với 0 match ở leak gate, Accessibility 100 và Best Practices 100 trên mọi trang đã audit, và một kiểm tra tự động trên cả 14 sơ đồ cho thấy không còn nhãn nào chồng lên nhau sau khi tôi thêm xử lý tránh va chạm.

Có hai chuyện không suôn sẻ. Ở lần chạy đầu, một CLI deploy tự chuyển project sang một chế độ hosting khác, thêm adapter, dependency và file cấu hình. Chưa có gì được deploy. Tôi hoàn tác toàn bộ, kiểm tra lockfile không còn package lạ, rồi tạo lại project ở chế độ tĩnh thuần với CLI được ghim ở một bản cũ hơn. Ngoài ra, một câu ở phần đầu trang lúc đầu khi đọc lướt có thể bị hiểu theo nghĩa ngược lại, nên tôi thay bằng một câu mang nghĩa tích cực.

Nếu làm lại

Tôi sẽ viết leak scan và những test đầu tiên trước khi có bất kỳ nội dung nào, để vấn đề lộ ra dần theo từng trang thay vì 60 cái cùng lúc. Tôi cũng sẽ chạy các công cụ deploy lạ trong một thư mục tạm trước, vì lần chạy đầu tiên có thể thay đổi project nhiều chẳng kém một lần deploy.