# Build in public: hai tuần qua ClarkCant đã thay đổi những gì

Đi lại lịch sử git từ 22/9 tới 5/10/2026 một cách thật thà: inbox cho những việc đang chờ bạn, settings quay về trong hội thoại, widget mà Clark bấm được, và những phần vẫn chưa được chứng minh.

Duy Nguyen /zuey/ · 2026-10-05T09:34:33.345Z

Source: https://clarkcant.cc/vi/blog/build-in-public-recent-updates

Mình tự hứa sẽ build ClarkCant công khai, mà build in public thì phải khoe cả đoạn giữa lộn xộn, chứ không chỉ cái ảnh chụp lúc ra mắt. Nên đây: hai tuần qua thật sự đã đổi những gì, đọc thẳng từ git, và những gì vẫn chưa xong.

Chút bối cảnh trước. Commit đầu tiên của repo là ngày 16/9/2026. Tới 5/10 thì đã có 685 commit. Nghe nhiều với chưa đầy ba tuần, và đúng là nhiều. Trong đó 150 commit có dòng co-author là Claude, một AI coding agent, cũng hơi buồn cười khi nhớ lại mấy lời mình than trong bài về tối giản. Nhưng con số đó tự nó chẳng nói lên gì. Commit không phải tính năng. Cái quan trọng là một người dùng bình thường có cảm nhận được gì không, nên mình sẽ kể theo hướng đó.

Số liệu đếm bằng git log trên nhánh main của ClarkCant, từ 00:00 ngày 22/9/2026 tới 5/10/2026 theo giờ Asia/Saigon. 358 commit, không tính merge commit. Trong đó 135 commit có tiền tố feat và 114 commit có tiền tố fix theo quy ước conventional commit; phần còn lại là docs, test, refactor, ci, chore và perf. 166 số pull request khác nhau xuất hiện ở cuối tiêu đề commit trong khoảng đó. Toàn bộ lịch sử từ commit đầu tiên ngày 16/9/2026 có 685 commit, trong đó 64 là merge.

```json
[
  {
    "id": "commits",
    "label": "Commit, không tính merge",
    "value": 358
  },
  {
    "id": "feat",
    "label": "Commit feat",
    "value": 135
  },
  {
    "id": "fix",
    "label": "Commit fix",
    "value": 114
  },
  {
    "id": "prs",
    "label": "Số pull request được nhắc tới",
    "value": 166
  },
  {
    "id": "total",
    "label": "Tổng commit từ 16/9",
    "value": 685
  }
]
```

Đếm bằng git log trên nhánh main, từ 00:00 ngày 22/9 tới 5/10/2026, giờ Asia/Saigon.

Biểu đồ tròn 358 commit không tính merge từ 22/9 tới 5/10/2026, chia theo tiền tố conventional commit: feat 135, fix 114, docs 63, test 25, refactor 14, còn lại 7 (ci 3, chore 2, perf 1, build 1).

```json
[
  {
    "label": "feat",
    "commits": 135
  },
  {
    "label": "fix",
    "commits": 114
  },
  {
    "label": "docs",
    "commits": 63
  },
  {
    "label": "test",
    "commits": 25
  },
  {
    "label": "refactor",
    "commits": 14
  },
  {
    "label": "ci, chore, perf, build",
    "commits": 7
  }
]
```

Có một điều con số nói khá thật: số commit fix gần bằng số commit feat. Phần lớn hai tuần này không phải thêm đồ mới, mà là bắt mọi thứ nói thật, chạy giống nhau trên Windows, hoặc thôi vẽ sai chỗ. Mình thấy tỷ lệ đó ổn. Làm cho xong thì trông nó như vậy đấy.

Nhịp độ cũng chẳng đều. Ba ngày đầu của khoảng này dồn hơn 200 commit, rồi gần như im re bốn ngày, rồi lại chạy. Build in public thì phải khoe cả mấy ngày im lặng.

Biểu đồ cột số commit không tính merge mỗi ngày theo giờ Asia/Saigon: 22/9: 87, 23/9: 57, 24/9: 62, 25/9: 2, 26/9: 0, 27/9: 1, 28/9: 5, 29/9: 34, 30/9: 46, 1/10: 6, 2/10: 15, 3/10: 15, 4/10: 17, 5/10: 11. Tổng 358. Ba ngày đầu (22 tới 24/9) chiếm 206 commit; từ 25/9 tới 28/9 gần như im ắng.

```json
[
  {
    "name": "22/9",
    "commits": 87
  },
  {
    "name": "23/9",
    "commits": 57
  },
  {
    "name": "24/9",
    "commits": 62
  },
  {
    "name": "25/9",
    "commits": 2
  },
  {
    "name": "26/9",
    "commits": 0
  },
  {
    "name": "27/9",
    "commits": 1
  },
  {
    "name": "28/9",
    "commits": 5
  },
  {
    "name": "29/9",
    "commits": 34
  },
  {
    "name": "30/9",
    "commits": 46
  },
  {
    "name": "1/10",
    "commits": 6
  },
  {
    "name": "2/10",
    "commits": 15
  },
  {
    "name": "3/10",
    "commits": 15
  },
  {
    "name": "4/10",
    "commits": 17
  },
  {
    "name": "5/10",
    "commits": 11
  }
]
```

Đếm bằng git log --no-merges, nhóm theo ngày tác giả.

## Các mốc chính, theo thứ tự

Dòng thời gian các mốc của ClarkCant lấy từ lịch sử git, cũ nhất trước, giờ Asia/Saigon. 16/9/2026 12:25: commit đầu tiên. 28/9 22:30: triết lý sản phẩm được ghi vào AGENTS.md (#202). 29/9 17:34: issue GitHub gắn nhãn có thể đi tới draft pull request (#252). 29/9 23:00: kiểu dáng Orb, sáu kiểu có sẵn cộng Custom (#271). 30/9 00:29: inbox báo kết quả chưa chắc chắn, nhắc việc và automation tới hạn (#268), 01:25 thêm hoãn và tắt bớt thông báo (#272). 30/9 16:39: xoá hội thoại theo policy của người dùng (#365). 30/9 21:45: Theme Lab và author CLI (#368). 2/10 từ 10:10: trình soạn thảo, bảng tính, tạo ảnh AI, công cụ render media và bản đồ offline (#383, #384, #392, #390, #398). 4/10 11:57: mỗi lượt ghi ai khởi động, kèm tuỳ chọn được hỏi với lượt do máy khởi động (#446). 4/10 12:56: Clark thực hiện được hành động widget đưa ra, bằng giọng nói từ 18:53 (#440, #457). 5/10 03:19: widget đóng gói thành npm archive đã kiểm tra, CLI và SDK build thành gói npm, ba app tham chiếu sẵn sàng cho npm nhưng chưa publish (#465, #472, #473). 5/10 11:59: model dự phòng khi model đã chọn từ chối (#467). 5/10 12:47: đợt làm giao diện điềm đạm hơn (#474). 5/10 13:43: lệnh slash trả về thẻ lệnh (#480).



Ngày giờ là author date của các commit tương ứng, theo giờ Asia/Saigon.

## Một chỗ cho những việc đang chờ bạn

Clark chạy việc ngầm, mà việc chạy ngầm có cái tật rất khó chịu: nó xong, hoặc kẹt, đúng lúc bạn không nhìn. Inbox chỉ trả lời đúng hai câu hỏi. Có gì đang chờ mình quyết không? Và mấy việc mình không để mắt tới thì kết quả ra sao?

Phần mình quan tâm nhất là trường hợp không chắc chắn. Khi một task chạy ngầm thực thi một lệnh tác động ra ngoài máy bạn, push code, gửi tin nhắn, xoá dữ liệu, thanh toán, thì giờ nó được ghi vào sổ trước khi chạy. Nếu lệnh bị cắt ngang hay quá giờ, tụi mình không giả vờ là xong, cũng không lặng lẽ chạy lại. Nó thành một thông báo trong inbox, nói rõ cái gì chưa chắc, rằng chưa có gì bị chạy lại, và bạn nên kiểm tra phía bên kia trước. Nhàm chán. Nhưng đúng thứ mình muốn nếu đó là tiền của mình.

Bạn có thể hoãn một thông báo, hoặc tắt riêng một loại thông báo hẹp rồi bật lại sau. Và inbox không phải màn hình chính thứ hai: mỗi mục đều trỏ về hội thoại của nó, quyết trong inbox hay trên thẻ trong chat cũng là cùng một quyết định.

## Clark thôi im lặng khi thất bại

Trước đây nếu model bạn chọn từ chối một lượt, bạn nhận một lỗi, hết. Giờ Clark trả lời chính tin nhắn đó bằng model dùng được tiếp theo, và ghi ngay dưới câu trả lời rằng đây là model dự phòng, bạn đã chọn model nào, và vì sao nó không trả lời. Lựa chọn của bạn trong Settings không bị sửa; model từ chối chỉ bị bỏ qua vài phút rồi thử lại. Nếu model nào cũng từ chối, thông báo sẽ kể tên từng model kèm lý do, và nói tin nhắn của bạn vẫn được giữ.

Có một fix nhỏ mà mình tự hào kỳ lạ: sau khi bạn duyệt một lệnh, Clark tiếp tục bằng một câu ngắn do app tự viết. Câu đó trước đây bị lưu như thể bạn gõ, nên transcript hiện trong bong bóng của bạn những chữ bạn chưa từng viết. Giờ nó được đánh dấu là do host viết và hiện thành một dòng nhẹ ở giữa. Chuyện nhỏ thôi. Nhưng một app chat mà nhét chữ vào miệng mình thì mình không tin nổi.

Mã TypeScript từ packages/contracts/src/surfaces.ts, từ dòng 903: một marker có phiên bản với kind host-continuation và version 1, kèm hàm isHostWrittenMessage trả về true khi tin nhắn có trường hostWritten. Chú thích trong file nói client gặp kind hay version lạ vẫn coi tin nhắn là do host viết, để chữ không bao giờ quay lại thành lời của người dùng.



Mã thật từ commit #478.

## Settings quay về trong hội thoại

Commit mới nhất lúc mình viết bài này thêm các lệnh slash trả về thẻ lệnh: /new mở hội thoại mới mà vẫn giữ cái cũ, /sessions liệt kê việc chạy ngầm và hội thoại cũ để mở lại, /login và /logout đăng nhập, đăng xuất provider AI ngay trong thẻ, /thinking chọn mức thinking, còn /background chạy một yêu cầu song song với hội thoại. Không phải lục tìm trang settings nữa. Bạn gõ, hoặc bạn hỏi, và control hiện ra ngay chỗ bạn đang đứng.

Cùng ngày đó là một đợt chăm chút lớn, 110 commit nhỏ squash vào một pull request. Mấy cái bạn sẽ thấy ngay: một chuỗi dài các bước đang làm giờ gộp thành một dòng, ghi bao nhiêu bước đã chạy và bao nhiêu bước lỗi, thay vì hai chục cái biên nhận chồng lên trên câu trả lời. Bạn chọn được font giao diện và font code. Markdown được đánh dấu ngay khi đang gõ. Cửa sổ desktop mở lại đúng chỗ bạn để. Và rất nhiều nhãn tiếng Anh sót lại trong giao diện tiếng Việt cuối cùng cũng nói tiếng Việt.

## Widget mà Clark, và giọng nói của bạn, bấm được

Ngày 2/10 có một loạt app tham chiếu: trình soạn thảo văn bản, bảng tính, tạo ảnh bằng AI, công cụ render media, và một bản đồ chạy trên basemap offline, chỉ tải tile khi có policy bạn cho phép. Hai ngày sau, widget khai báo được những hành động nó đưa ra, và Clark thực hiện giúp bạn được. Ví dụ bảng tính cho phép định dạng vùng đang chọn, trình soạn thảo cho phép thay thế. Nó vẫn đi qua đúng cái cổng như mọi thứ khác: execution policy của bạn, sổ ghi hiệu ứng, và một câu trả lời thật thà khi widget không mở hoặc không phản hồi kịp. Tối hôm đó, cùng đường đi ấy chạy được khi bạn nói thành lời.

Với ai muốn tự làm widget, author CLI giờ đóng gói được widget thành npm archive đã kiểm tra, và từ chối gói nào chứa file trông giống credential. Author CLI và SDK giờ build thành gói npm, kèm workflow release chạy smoke test đúng các archive đó trước. Còn ba app thí điểm (Quick Notes, CSV Explorer, Media Converter) thì nói rõ luôn: mới sẵn sàng cho npm, chưa lên npm. Lệnh publish chỉ in ra câu lệnh cần chạy, tự nó không upload gì cả.

## Automation làm tới nơi tới chốn, và nói rõ ai yêu cầu

Một yêu cầu thường trực bạn đặt trong hội thoại giờ có thể đưa một issue GitHub được gắn nhãn đi tới tận draft pull request: làm trong worktree riêng, chạy test, commit, push bằng token do node làm trung gian, rồi báo địa chỉ pull request về hội thoại. Nếu GitHub không gọi tới được máy bạn, node sẽ tự đi hỏi repo, nhiều nhất năm phút một lần.

Một khi máy móc cũng khởi động được việc, bạn cần biết lượt nào là của bạn, lượt nào không. Giờ mỗi lượt đều ghi ai khởi động: bạn, một MCP client, relay, CLI, automation hay một máy đã ghép đôi. Thẻ phê duyệt và nhật ký hoạt động đều ghi ai đã yêu cầu. Mặc định cách ra quyết định không đổi gì, nhưng bạn có thể bật tuỳ chọn để được hỏi trước khi một lượt do máy khởi động gây ra hiệu ứng rủi ro.

## Những gì chưa xong, nói thẳng

README vẫn ghi rõ ở phần trạng thái: đây là bản bootstrap, chưa phải bản phát hành. Tụi mình giữ một sổ conformance cho 18 hạng mục scope của bản foundation beta. Mười hạng mục là PASS. Tám hạng mục là PARTIAL, và hạng mục nào cũng ghi rõ còn thiếu gì.

Mấy cái quan trọng nhất với người dùng bình thường: Google Calendar chưa từng nói chuyện với Google, vì chưa có OAuth client đăng ký và chưa có tài khoản Google đứng sau test; nó mới chạy trọn vẹn với một API giả lập trên chính máy đó. Live voice chưa từng mở một phiên thật, vì chưa có tài khoản provider. Browser Use mới chỉ điều khiển các trang do chính repo phục vụ, chưa từng đụng trang bên thứ ba cần đăng nhập. Computer Use chưa có bundle đã ký và máy chạy test chưa có container engine, nên driver native chưa chạy lần nào. Còn cài package qua chat thì mới ghi lại đúng artifact, chưa build cây dependency.

Mình thà bạn đọc ở đây còn hơn tự phát hiện ra theo cách khó chịu. Không mục nào được đánh dấu xong cho tới khi có test chứng minh.

Bảng 18 hạng mục từ docs/conformance-traceability.md. PASS (10): V01 Giao diện hội thoại, V02 Runtime di động, V03 Runtime task/session bền vững, V04 Liên kết node tin cậy, V05 Cộng tác từ xa, V06 Danh bạ workspace, V07 Nền tảng capability, V11 Widget dựng sẵn, V16 Onboarding/cá nhân hoá, V18 Vận hành/bảo mật. PARTIAL (8): V08 Cài đặt qua hội thoại (Chưa build cây dependency), V09 Thiết lập credential/auth (Chưa có OAuth client đăng ký hay tài khoản thật), V10 Tích hợp mẫu (Google Calendar) (Chưa từng gọi tới Google thật), V12 Widget tự tạo (So khớp giọng nói và cú bấm cần phiên voice thật), V13 Pin (Chưa thử với nhà cung cấp trình phát thật), V14 Browser Use (Chưa từng chạy trên trang bên thứ ba cần đăng nhập), V15 Computer Use (Chưa có bundle đã ký, chưa có container engine), V17 Live voice (Chưa có tài khoản provider thật).

```json
[
  {
    "id": "V01",
    "item": "Giao diện hội thoại",
    "status": "PASS",
    "missing": ""
  },
  {
    "id": "V02",
    "item": "Runtime di động",
    "status": "PASS",
    "missing": ""
  },
  {
    "id": "V03",
    "item": "Runtime task/session bền vững",
    "status": "PASS",
    "missing": ""
  },
  {
    "id": "V04",
    "item": "Liên kết node tin cậy",
    "status": "PASS",
    "missing": ""
  },
  {
    "id": "V05",
    "item": "Cộng tác từ xa",
    "status": "PASS",
    "missing": ""
  },
  {
    "id": "V06",
    "item": "Danh bạ workspace",
    "status": "PASS",
    "missing": ""
  },
  {
    "id": "V07",
    "item": "Nền tảng capability",
    "status": "PASS",
    "missing": ""
  },
  {
    "id": "V08",
    "item": "Cài đặt qua hội thoại",
    "status": "PARTIAL",
    "missing": "Chưa build cây dependency"
  },
  {
    "id": "V09",
    "item": "Thiết lập credential/auth",
    "status": "PARTIAL",
    "missing": "Chưa có OAuth client đăng ký hay tài khoản thật"
  },
  {
    "id": "V10",
    "item": "Tích hợp mẫu (Google Calendar)",
    "status": "PARTIAL",
    "missing": "Chưa từng gọi tới Google thật"
  },
  {
    "id": "V11",
    "item": "Widget dựng sẵn",
    "status": "PASS",
    "missing": ""
  },
  {
    "id": "V12",
    "item": "Widget tự tạo",
    "status": "PARTIAL",
    "missing": "So khớp giọng nói và cú bấm cần phiên voice thật"
  },
  {
    "id": "V13",
    "item": "Pin",
    "status": "PARTIAL",
    "missing": "Chưa thử với nhà cung cấp trình phát thật"
  },
  {
    "id": "V14",
    "item": "Browser Use",
    "status": "PARTIAL",
    "missing": "Chưa từng chạy trên trang bên thứ ba cần đăng nhập"
  },
  {
    "id": "V15",
    "item": "Computer Use",
    "status": "PARTIAL",
    "missing": "Chưa có bundle đã ký, chưa có container engine"
  },
  {
    "id": "V16",
    "item": "Onboarding/cá nhân hoá",
    "status": "PASS",
    "missing": ""
  },
  {
    "id": "V17",
    "item": "Live voice",
    "status": "PARTIAL",
    "missing": "Chưa có tài khoản provider thật"
  },
  {
    "id": "V18",
    "item": "Vận hành/bảo mật",
    "status": "PASS",
    "missing": ""
  }
]
```

Nguồn: docs/conformance-traceability.md và packages/contracts/src/implementation-status.ts, ngày 5/10/2026.

Hai tuần là vậy đó. Fix nhiều hơn mình muốn thừa nhận, vài thứ mình thật sự vui, và một danh sách rõ ràng những gì vẫn cần thế giới thật chứng minh. Nếu bạn muốn hối mình ưu tiên cái nào, khảo sát bên dưới là cách nhanh nhất.

Phần nào chưa được chứng minh mà mình nên đem ra thế giới thật trước?

- Google Calendar với tài khoản thật
- Live voice với provider thật
- Browser Use trên trang thật có đăng nhập
- Cài widget thẳng từ npm
