# Dựng software factory AI chạy song song và ship được thật

[Muhammad Farooq](https://homus.dev/speakers/muhammad-farooq) · [Prompt Engineering](https://homus.dev/orgs/prompt-engineering)

> Software factory là một vòng lặp: năm thứ vỡ khi chạy agent song song, pipeline triage, worker, verifier, reviewer, integrator, và chi phí thật.

Topics: Software Factory, Coding Agents, Agents, Workflow

Canonical: https://homus.dev/talks/how-to-build-a-self-improving-ai-software-factory-that-actually-ships

## 1. Software factory thật ra là một vòng lặp

Video được Upstash tài trợ.

Muhammad Farooq mở đầu bằng một nhận xét: ai cũng nói về software factory, nhưng gần như không ai định nghĩa nó thật sự trông ra sao khi viết thành code. Trong video này anh làm hai việc: mổ xẻ kiến trúc chính xác của một software factory, rồi tự build một cái và cho nó chạy trên các issue thật.

Anh đưa ra một ví dụ nhanh trước. Anh trỏ factory của mình vào một repo có 10 issue GitHub thật. Mỗi agent được dựng lên trong một cloud sandbox riêng, viết code bằng Claude Code, rồi kết quả được chấm bằng một bộ test ẩn mà agent không hề thấy.

Về bản chất, theo Farooq, một software factory chỉ là một vòng lặp vận hành (operating loop). Issue đi vào từ backlog của bạn, một coding agent nhặt một issue và viết bản sửa. Sau đó một hệ thống độc lập kiểm tra phần việc đó, và một con người quyết định có merge hay không.

![Sơ đồ software factory là một vòng lặp: backlog issue đi vào, coding agent nhặt issue #2](https://homus.dev/photos/hO4ft4tGOJI-0043.jpg)

"Software factory = a loop": các issue trong backlog (#3 GET /stats/:slug, #4 HEAD requests, #5 Support custom slugs, #6 Links that expire) đi vào vòng lặp, và coding agent nhặt issue #2 để viết code.

## 2. Bản nhỏ nhất: một prompt cho một issue

Phiên bản đơn giản nhất của vòng lặp này là một prompt duy nhất. Bạn giao cho một agent một issue và bảo nó sửa bug, viết test và mở pull request. Như vậy đã là một software factory tí hon, và với một ticket thì nó chạy được.

```
Fix issue #2.
Write tests.
Open a pull request.
```

![Một prompt ba dòng giao issue #2 cho agent, agent trả về pull request #27 kèm test](https://homus.dev/photos/hO4ft4tGOJI-0103.jpg)

"A single prompt": issue #2 (URL không có scheme vẫn được chấp nhận) đi vào agent, agent viết test và mở pull request #27. Một software factory tí hon, và nó chạy được.

Vấn đề là tốc độ. Một agent đi qua backlog từng ticket một, nên 10 issue nghĩa là phải chờ 10 lượt chạy nối đuôi nhau. Nước đi hiển nhiên là chạy 10 agent cùng lúc, song song, và đó chính là thời điểm mọi thứ bắt đầu vỡ.

## 3. Ví dụ URL shortener và lỗi đầu tiên: chung một thư mục

Trong video, factory sẽ làm việc trên một URL shortener tí hon. Farooq dùng ví dụ này để cho thấy cái gì vỡ khi bạn chạy nhiều agent song song. Hiện tại URL shortener này chấp nhận cả link thiếu `https`, và điều đó gây ra redirect hỏng.

Lỗi đầu tiên là **shared folder** (chung thư mục). Nếu mọi agent làm việc trong cùng một bản checkout trên máy, mọi thay đổi của chúng chồng chất vào cùng một chỗ. Trong một lần thử của anh, ba agent dùng chung một thư mục đã tạo ra một commit duy nhất chạm tới sáu file, trộn lẫn ba issue khác nhau.

Cách sửa là mỗi agent một sandbox cô lập, để mỗi worker có bản sao code riêng và máy riêng của nó.

Lỗi "shared folder": ba agent chung một thư mục đẻ ra một commit chạm sáu file của ba issue. Cách sửa là mỗi agent có bản sao code và máy riêng.

## 4. Hai agent giành một issue, và agent tự chấm điểm mình

Lỗi thứ hai là **double-booking**: hai agent cùng chộp đúng một issue vào cùng một lúc. Cách sửa là một hệ thống claim. Trước khi bắt đầu làm, agent gắn một label "claimed" lên issue GitHub, và mọi agent khác sẽ bỏ qua issue đó. Trong lượt chạy thật, label này có tên `factory:claimed`.

Lỗi tiếp theo khi chạy agent song song là **cái bẫy tự chấm điểm** (self-grading trap). Nếu bạn để một agent tự chấm bài của chính nó, báo cáo của nó không nói lên được nhiều. Ví dụ, trong các lượt chạy thử của Farooq, các worker agent lần nào cũng báo test đã pass, kể cả một lần mà verifier thấy test thật ra đang fail.

![Danh sách năm thứ vỡ; worker report luôn ghi testsPass true trong khi verifier thấy npm test fail](https://homus.dev/photos/hO4ft4tGOJI-0243.jpg)

Bên trái là năm thứ vỡ khi chạy song song: shared folder, double-booking, self-grading, training cutoff, PR flood. Báo cáo của worker lần nào cũng ghi `{ testsPass: true }` ("said pass every time"), trong khi verifier chạy lại thì thấy `npm test: fail`.

Vì vậy cách sửa là dùng một verifier riêng. Bạn dựng một sandbox mới tinh, checkout branch của worker và tự chạy test. Tiếp theo là một reviewer agent riêng, đọc diff thật của thay đổi.

## 5. Training cutoff và cơn lũ pull request

Một vấn đề khác nằm ở training cutoff. Model chỉ biết những API nó đã thấy trong dữ liệu huấn luyện, trong khi framework thay đổi liên tục. Cách sửa là cho agent một công cụ tra tài liệu sống (live documentation), để nó tra tài liệu hiện hành trước khi viết code.

Vấn đề cuối cùng là **PR flood**. Mấy agent này rất thích mở pull request. Cách sửa là factory chỉ được phép mở pull request, còn nút merge thì nằm hẳn trong tay quyết định của con người.

Ghép đủ năm thành phần này lại với nhau, theo Farooq, về cơ bản bạn đã có một software factory. Phần còn lại của video là một ví dụ cho thấy tất cả khớp vào nhau thế nào.

Năm thành phần của một software factory, mỗi cái sinh ra để chữa một thứ vỡ khi chạy agent song song.

## 6. Agent chạy lâu trên cloud: Upstash Box là gì

Để một software factory chạy được, bạn cần các agent chạy lâu (long-running) trên cloud. Phần sandbox, Farooq dùng Upstash Box.

Box là gì? Hãy nghĩ về nó như một chiếc máy tính nhẹ trên cloud, được build riêng để chứa agent. Khi bạn dựng một box bằng một lệnh gọi API, bạn không chỉ nhận một container trơn rồi phải tự nối tool vào. Mỗi box có sẵn file system cô lập riêng, một bash shell, git, Node, và các agent harness cài sẵn như Claude Code hoặc Codex. Hoặc bạn có thể thả vào vòng lặp agent tự viết của mình nếu muốn.

Anh cho xem nó trông thế nào trong code, và nói có bốn primitive cụ thể làm cho một factory trở nên khả thi.

## 7. Primitive 1: boot nhanh và snapshot ổ đĩa

Primitive đầu tiên là boot cực nhanh và snapshot ổ đĩa. Khi gọi create, chỉ khoảng hai giây rưỡi sau anh đã có một máy Linux đang chạy.

Trong một software factory, bạn không muốn 10 agent song song, mỗi con tự chạy `git clone` rồi `npm install` lại từ đầu. Thay vào đó, anh cài dependency một lần trên một base box rồi chụp snapshot. Snapshot đóng băng đúng trạng thái ổ đĩa của box thành một image dùng lại được. Từ một snapshot đó, anh boot thêm năm box song song, và box nào thức dậy cũng đã có sẵn dependency nằm trên đĩa. Cấu hình một lần, nhân bản nhiều lần.

![Terminal demo: box sẵn sàng trong 2.5 giây, snapshot lưu trong 2.4 giây, 5 box từ snapshot sẵn sàng trong 7.7 giây](https://homus.dev/photos/hO4ft4tGOJI-0523.jpg)

Demo "fast boot + snapshots": box sẵn sàng trong 2.5 giây (Linux aarch64, Node v25.9.0, đĩa 5.0G); cài `zod` một lần rồi lưu snapshot trong 2.4 giây; khởi động 5 box từ snapshot song song mất 7.7 giây, và cả 5 đều đã có `zod`, không cài lại gì.

## 8. Primitive 2: cô lập file system và tạm dừng box

Primitive thứ hai là cô lập file system và tạm dừng (pause), và theo Farooq phần này rất quan trọng. Ví dụ, nếu anh ghi một file trong box 1 rồi tìm nó trong box 2, file không có ở đó. Mỗi agent có ổ đĩa riêng của mình.

Điểm hay là nếu một agent đang chờ con người trả lời, bạn có thể pause box. Việc này đóng băng trạng thái ổ đĩa và ngừng tính tiền compute. Khi resume, file vẫn nằm đúng chỗ agent để lại. Nói cách khác, bạn vừa cô lập được agent, vừa dừng được hoá đơn trong lúc chờ người.

## 9. Primitive 3 và 4: tường lửa egress, chạy thật trên cloud

Primitive tiếp theo là tường lửa cho network egress. Nếu bạn để agent chạy code không đáng tin hoặc cài package, bạn không muốn nó nói chuyện với các server ngẫu nhiên trên Internet. Upstash cho phép khoá network egress theo từng box. Ví dụ, trên box này anh chỉ đưa npm registry vào allowlist, nên mọi kết nối đi bất cứ nơi nào khác đều bị chặn. Đứng ở góc độ bảo mật, điều này rất quan trọng.

![Demo egress allowlist: box chỉ tới được registry.npmjs.org, example.com và api.github.com bị chặn](https://homus.dev/photos/hO4ft4tGOJI-0643.jpg)

Demo "egress firewall": chạy `npx tsx demo/06_egress_allowlist.ts`, box được tạo với `networkPolicy` chỉ cho phép `registry.npmjs.org`. Gọi tới npm registry trả HTTP 200, còn `example.com` và `api.github.com` đều "no connection".

Primitive thứ tư là thực thi từ xa thật sự. Với agent chạy lâu, bạn muốn chúng chạy remote, nhất là khi chúng chạy theo một chu kỳ định sẵn trong lúc laptop của bạn đã tắt. Mọi thứ ở đây chạy trên server cloud của Upstash, không phải trên laptop của anh. Khi anh hỏi Claude Code bên trong box rằng nó đang chạy ở đâu, nó báo một máy Linux từ xa với IP khác hẳn chiếc MacBook của anh. Trên màn hình, laptop của anh báo "Darwin arm64", còn bên trong box là Linux.

## 10. Bài thử: TinyLinks với 10 issue và bộ test ẩn

Xong phần primitive, Farooq giới thiệu ví dụ sẽ chạy. Đó là TinyLinks, một dịch vụ URL shortener nhỏ có 10 issue GitHub đang mở. Bốn issue là bug, năm là tính năng mới, và một issue được cố tình viết mơ hồ, chỉ để xem factory có đủ khôn để bỏ đi khi ticket chẳng có nghĩa gì không.

Để chấm bài của factory, anh viết một hidden test cho mỗi issue. Các test này nằm trên máy anh, bên ngoài repo, nên agent hoàn toàn không thể thấy chúng. Đó là cách anh kiểm xem bản sửa có thật sự chạy không, thay vì tin lời agent.

![Danh sách hidden test h01 đến h09 nằm ngoài repo và nội dung hidden test cho issue #2](https://homus.dev/photos/hO4ft4tGOJI-0743.jpg)

Hidden test giữ bên ngoài repo, mỗi issue một file (`h01.test.ts` đến `h09.test.ts`), nằm trên máy của anh nên các worker không thấy. Bên dưới là test cho issue #2: URL không có scheme phải bị từ chối hoặc được chuẩn hoá, và nếu tạo được thì header `location` phải bắt đầu bằng http.

Hiện tại trên `main`, app fail cả sáu baseline check của anh và pass không trên chín hidden test.

## 11. Pipeline bốn stage: triage, worker, verifier, reviewer

Đây là cách pipeline của factory được nối với nhau. Đầu tiên anh chuẩn bị một base box: clone TinyLinks, cài toàn bộ dependency, rồi nối Node, GitHub token, và Claude Code chạy model Sonnet 5 vào box này. Sau đó anh chụp snapshot base box.

Từ đó, factory claim từng issue GitHub và issue đi qua bốn stage riêng biệt, chạy trong ba box hoàn toàn mới, box nào cũng boot từ snapshot vừa tạo:

- **Triage.** Một box mới thức dậy, đọc issue GitHub và quyết định yêu cầu đã đủ rõ để build chưa, hay nên từ chối ticket.

- **Worker.** Nếu triage duyệt, một worker box mới viết code sửa lỗi, thêm unit test, và factory push branch của nó lên.

- **Verifier.** Factory không bao giờ tin báo cáo test của worker. Một verifier box mới boot từ snapshot sạch, checkout code trên branch của worker và tự chạy bộ test.

- **Reviewer.** Ngay trong box của verifier, một reviewer agent soi git diff thật từng dòng, để bắt những bug logic mà unit test bỏ sót.

Chỉ khi cả test lẫn reviewer cùng đồng ý, factory mới mở pull request.

![Pipeline snapshot, triage, worker, verifier và reviewer chung box, cùng log sự kiện các stage](https://homus.dev/photos/hO4ft4tGOJI-0843.jpg)

Pipeline: từ snapshot sang triage, worker, rồi verifier và reviewer dùng chung một box ("verifier + reviewer, same box"). Issue #2 mang label `factory:claimed`, qua triage thì được "build". Trong log, issue #10 bị triage loại vì yêu cầu không nêu trường dữ liệu hay endpoint cụ thể nào, không có tiêu chí nghiệm thu để agent làm.

## 12. Hai chi tiết kiến trúc: typed handoff và live docs

Để factory chạy ổn định, có hai chi tiết kiến trúc Farooq muốn nói tới.

Chi tiết thứ nhất là **typed object handoff**. Các stage không chuyền cho nhau những đoạn lịch sử chat lộn xộn. Chúng giao tiếp hoàn toàn qua các object JSON có cấu trúc. Worker bắt buộc phải trả về theo một schema cố định, gồm trạng thái, một bản tóm tắt ngắn, danh sách chính xác các file đã sửa, có thêm test hay không, và một boolean cho biết test có pass hay không.

![Schema zod WorkReport với status, summary, filesChanged, testsAdded, testsPass và schema Review](https://homus.dev/photos/hO4ft4tGOJI-1003.jpg)

Schema `WorkReport` viết bằng zod: `status` (done hoặc blocked), `summary`, `filesChanged`, `testsAdded`, `testsPass`. Ngay dưới là schema `Review` với `verdict` (approve hoặc request_changes) và `issuesFound`.

Chi tiết thứ hai là **live docs qua cơ chế inject key ở proxy**. Để giải bài toán dữ liệu huấn luyện cũ, anh tích hợp Context7, công cụ tìm kiếm trong tài liệu chính thức đang cập nhật chỉ bằng một web request. Anh đặt một lệnh CLI nhỏ tên `docs` vào mọi box, và dặn worker gọi nó bất cứ khi nào không chắc 100% về một API.

Anh lưu ý cách bảo mật vận hành ở đây: API key của Context7 được Upstash gắn vào request đi ra, ở bên ngoài box. Agent bên trong box đọc được tài liệu nhưng không bao giờ thật sự nhìn thấy API key.

![Sơ đồ worker-8 gọi Context7 qua Upstash, key được gắn bên ngoài box bằng attachHeaders](https://homus.dev/photos/hO4ft4tGOJI-1043.jpg)

"Key injected outside the box": worker-8 gọi Context7 đi qua Upstash, và key được gắn vào header ở phía ngoài. Trong code, `networkPolicy` chỉ cho phép `context7.com`, còn `attachHeaders` mang API key, nên agent không cầm key.

## 13. Chạy factory trên 10 issue

Giờ Farooq cho factory chạy trên cả 10 issue, với năm pipeline song song mỗi lúc. Bên trái màn hình là log sự kiện trực tiếp từ từng stage. Bên phải, bạn xem các sandbox được dựng lên rồi dỡ xuống ngay trong console của Upstash. Trên GitHub, các issue lần lượt nhận label claim từng cái một.

Anh nhảy vào bên trong một worker box đang chạy. Ở đây agent không chắc về một API của Node, nên bạn thấy nó chạy lệnh `docs` để kiểm xem `URLSearchParams` hoạt động thế nào trên bản Node hiện tại, trước khi sửa file.

Tiếp theo là worker xử lý bug HTTPS. Nó đọc đoạn code kiểm tra URL hiện có, viết lại phần kiểm tra để parse thành URL object và bắt buộc protocol phải là HTTP hoặc HTTPS, rồi viết unit test mới, chạy chúng, và branch được commit rồi push lên.

## 14. Kết quả, và vì sao vẫn cần reviewer

Cả lượt chạy 10 issue xong trong bốn phút rưỡi (trên màn hình: 4 phút 34 giây). Factory mở chín pull request và cố tình bỏ qua issue mơ hồ duy nhất ("#10 Make the stats better" bị triage loại), đúng hành vi anh mong muốn. Khi anh chạy bộ test bên ngoài trên chín branch đó, cả chín đều pass hidden test.

Kết quả lượt chạy chính trên TinyLinks, theo video đăng ngày 09/10/2026.

Nhớ lúc trước anh nói cần cả verifier lẫn reviewer chứ? Anh cho xem một trace thấy rõ vì sao. Trong lượt chạy đó, worker nói test của nó pass. Verifier độc lập chạy bộ test và cũng pass. Ngay cả hidden test của anh cũng pass. Nhưng reviewer agent vẫn từ chối pull request.

Khi đọc diff, reviewer nhận ra rằng nếu hai link được tạo trong cùng một mili giây, danh sách link trả về sai thứ tự, và reviewer đã tự tái hiện được bug. Unit test của worker không bao giờ bắt được bug đó vì dữ liệu test dùng các slug đã được sắp xếp sẵn. Vậy test pass không có nghĩa là code đúng. Một agent review code riêng, tách khỏi worker, đã bắt được một bug về thứ tự mà mọi test khác bỏ lọt, và đó là lý do bạn cần verifier và reviewer độc lập.

![Nguyên văn phát hiện của reviewer về lỗi thứ tự khi hai link tạo cùng mili giây](https://homus.dev/photos/hO4ft4tGOJI-1243.jpg)

"What the reviewer found (verbatim)", trong một lượt chạy tập dượt: `all()` giờ sắp theo `createdAt` rồi lấy slug làm tiêu chí phụ; vì `createdAt` chỉ chính xác tới mili giây và slug là ngẫu nhiên, các link tạo cùng mili giây (zebra, apple, mango, kiwi, banana) bị trả về theo thứ tự chữ cái. Test pagination dùng slug tăng dần nên không bao giờ lộ ra lỗi này. Verifier pass, reviewer từ chối.

## 15. Merge conflict và stage thứ năm: integrator

Và đây là phần thú vị nhất. Cả chín pull request đã verify đều chạy hoàn hảo khi đứng riêng. Nhưng khi anh thử merge chúng vào `main`, bảy trên chín lập tức vướng merge conflict. Lý do: cả chín agent đều bắt đầu từ đúng một base snapshot, và phần lớn đều thêm unit test của mình vào cùng một file test. Không agent nào biết các agent khác tồn tại.

Vì thế anh phải thêm stage thứ năm vào factory: **integrator**. Integrator là thêm một Upstash box nữa, kéo các PR đã duyệt vào một integration branch chung, mỗi lần một PR. Mỗi khi `git merge` gặp conflict, một agent Claude Code trong box integrator mở các conflict marker ra, gộp cả hai phía để không mất dòng code hay test nào, rồi chạy lại toàn bộ test suite sau từng lần merge để chắc chắn code gộp lại vẫn chạy.

Trong ví dụ này, cả chín PR đều đi qua integrator: hai cái merge sạch, bảy cái còn lại do agent giải conflict.

![Integrator gộp cả 9 PR vào demo/after; app check 6 trên 6 và 9 trên 9 hidden test pass](https://homus.dev/photos/hO4ft4tGOJI-1423.jpg)

Sau integrator, "all 9 PRs in" branch `demo/after`: 2 merge sạch, 24 test pass. Lệnh `./demo/04_app_check.sh demo/after AFTER` cho thấy cả sáu check của app đều đạt (ví dụ POST một URL không scheme trả 400), và 9 trên 9 hidden test pass.

Bước cuối cùng là con người ra quyết định ở khâu merge. Anh mở pull request cho bug HTTPS: thấy verifier đã pass và reviewer cũng đã đồng ý, anh chỉ việc bấm merge. Ngay sau lần merge đó, `main` từ chối đúng cách các link thiếu https.

## 16. Chi phí thật của cả lượt chạy

Farooq nói về chi phí thực tế (theo video đăng ngày 09/10/2026). Lượt chạy factory với 10 issue qua bốn stage tốn khoảng 3,67 USD token của Claude. Stage integrator, giải bảy conflict và chạy lại test suite, tốn 6,37 USD token.

Còn compute của Upstash Box chỉ khoảng 0,01 USD cho cả ngày, tính cả mọi lượt chạy thử của anh. Lý do: một coding agent dành gần như toàn bộ thời gian để chờ API của LLM stream token về, và Upstash chỉ tính tiền theo thời gian CPU thực sự chạy, nên chi phí sandbox lúc này gần như chỉ là số lẻ làm tròn.

![Hoá đơn tinylinks factory: factory run 3,67 USD, integrator 6,37 USD, Box compute cả ngày khoảng 0,01 USD](https://homus.dev/photos/hO4ft4tGOJI-1523.jpg)

"What it cost": factory run với 10 issue 3,67 USD, integrator với 7 conflict 6,37 USD, và compute của Box cho cả ngày, gồm mọi lượt chạy thử, khoảng 0,01 USD.

Anh lưu ý thêm một điều: Upstash có kèm lượng dùng model built-in tới một mức trần mỗi tháng. Với các lượt chạy factory nặng, bạn sẽ muốn truyền API key của chính mình vào, hoặc nâng mức trần hàng tháng đó lên.

## 17. Lời khuyên và tóm tắt

Nếu bạn muốn tự build software factory, Farooq nhắc vài điều. Thứ nhất, sandbox giữ cho các agent không giẫm lên file của nhau, nhưng nó không làm cho code đúng. Chất lượng code vẫn đến từ test, verifier và reviewer của bạn. Thứ hai, agent song song có thể tạo ra Git conflict khi chúng chạm vào các file dùng chung như test suite hay router, nên hãy lên kế hoạch cho một bước integrator.

Tóm lại, một software factory chỉ là một vòng lặp: issue đi vào, một agent build bản sửa, một box riêng verify nó, và một con người merge. Mỗi mảnh của kiến trúc tồn tại vì agent song song sẽ vỡ nếu thiếu nó. Bạn cần mỗi agent một box từ snapshot, một khoá claim cho issue, một verifier độc lập, tra cứu tài liệu sống, một integrator để giải merge conflict, và một con người ở cuối.

Bản tóm tắt cuối video: vòng lặp bốn bước và sáu thành phần giữ cho agent song song không vỡ.

Anh đã để link code đầy đủ của factory và tài liệu Upstash Box trong phần mô tả, để bạn thử trên repo của chính mình. Anh mời người xem kể trong phần bình luận họ đang chạy coding agent song song thế nào và cái gì thật sự vỡ. Anh mong video có ích, cảm ơn mọi người đã xem và hẹn gặp lại ở video sau.

## Nguồn và liên kết

- Tên gốc: How to Build a Self-Improving AI Software Factory That Actually Ships
- Sự kiện: [YouTube](https://homus.dev/events/youtube)
- Video gốc: [https://www.youtube.com/watch?v=hO4ft4tGOJI](https://www.youtube.com/watch?v=hO4ft4tGOJI)

- Kênh YouTube Prompt Engineering (Muhammad Farooq): [youtube.com/channel/UCDq7SjbgRKty5TgGafW8Clg](https://www.youtube.com/channel/UCDq7SjbgRKty5TgGafW8Clg)

- Code của software factory trong video (README có bảng kết quả lượt chạy, cách bốn stage và integrator hoạt động, và hướng dẫn tự chạy): [github.com/PromtEngineer/software-factory](https://github.com/PromtEngineer/software-factory)

- Tài liệu Upstash Box: [upstash.com/docs/box](https://upstash.com/docs/box)

- Context7, tài liệu cập nhật cho AI agent: [context7.com](https://context7.com)

Nguồn: https://homus.dev/talks/how-to-build-a-self-improving-ai-software-factory-that-actually-ships · cập nhật 2026-10-18
