cogover-custom-module
Skill13 starsChanged 33 days agoMITView on GitHub
Điều phối vòng đời Cogover Custom Module: khảo sát App/Object, chọn backend/frontend và dạng frontend (single page app, custom component, Federation Page), người dùng xác nhận trước khi code, schema duyệt bằng Excel, security rules, quy tắc nghiệp vụ theo Super Admin/role của người gọi, record trigger before/after-change, background job (enqueue, lịch cron, retry) hoặc Process, Custom Module Action cho node Process và tool AI Agent gọi (`defineAction`, schema `s`, `effect`, danh tính chạy), khởi chạy Process/AI Agent từ code (`processes.start`, `agents.start`, job nhận kết quả, mục `processes`/`agents` của identity policy, `deliverTo` để agent chủ động nhắn chat một nhân sự), push message (làm mới record, toast) và nhận push ở frontend, dùng `@cogover/client-sdk` (UI component, data hook, socket listener), gửi notification và email từ hộp thư được grant, đọc cơ cấu tổ chức (phòng ban, vị trí, chuỗi quản lý người duyệt), secret/credential cho fetch, crypto (SHA-256, HMAC, mã hoá AES/RSA, chữ ký RSA/ECDSA), inbound webhook (KEY/HMAC), đọc record chỉ field cần dùng (`fields`, `expandLookups` khi cần), số liệu đơn giản bằng `records.aggregate` và báo cáo/dashboard lấy từ saved report của $report-builder thay vì quét record, thiết kế trong ngân sách mỗi lần thực thi (`limits.usage()`, việc nặng chia trang sang background job), ghi hàng loạt bằng batch và gọi song song bằng `Promise.all`, xử lý HTTP `429`/`Retry-After` khi Workspace bận, lời gọi chạy đồng thời (`locks`, `expectedVersion`, `maxConcurrentRuns`), Project/policy/key, code/test theo template, E2E frontend trên trình duyệt, review và kiểm thử backend, review bảo mật và đo hiệu năng bằng sub-agent độc lập, publish qua Dev CLI, bàn giao. Không thay skill cấu hình Object/Process đơn lẻ.
- Reads credentials
In this repository23 other files it gives its agents.
- agent-builderSkill
- app-menu-managerSkill
- build-cogover-appSkill
- cogover-api-authSkill
- cogover-iconSkill
- cogover-overviewSkill
- create-cogover-objectsSkill
- dashboard-builderSkill
- document-templateSkill
- layout-scriptingSkill
- object-buttonSkill
- object-filterSkill
- object-formSkill
- object-history-trackingSkill
- object-infoSkill
- object-layoutSkill
- object-path-componentSkill
- object-recordSkill
- object-transition-ruleSkill
- process-creatorSkill
- record-emailSkill
- report-builderSkill
- user-permissionSkill
The file
Files
Cogover Custom Module
- Phiên bản:
1.24.0 - Ngày phát hành:
2026-10-24
Tổng quan và phạm vi
Skill điều phối toàn bộ vòng đời Custom Module. Dùng $cogover-overview làm kiến thức nền; không chuyển vòng về orchestrator khác khi yêu cầu đã thuộc Custom Module. Chỉ dùng public API, metadata Workspace và tài liệu sản phẩm để xác định khả năng; không yêu cầu mã nguồn/hạ tầng của Cogover.
Custom Module dành cho nghiệp vụ mà Cogover Object/Process chuẩn chưa đáp ứng đủ: logic phía server, giao diện riêng hoặc cả hai, vẫn dùng dữ liệu và quyền của Workspace.
| Loại | Chọn khi | Ví dụ |
| Custom Frontend Module | Cần màn hình hoặc thành phần giao diện riêng; API hiện có đã đủ dữ liệu và logic với quyền người dùng. Ba dạng: single page app, custom component nhúng layout Object, Federation Page trong ứng dụng Cogover | Dashboard toàn màn hình, khối kiểm tra mã giảm giá trên form đơn hàng, trang công cụ trong menu ứng dụng |
| Custom Backend Module | Cần xử lý phía server, tổng hợp/ghi Object, API riêng, tích hợp có thông tin bí mật do Workspace giữ (secret/credential), quy tắc kiểm tra/điều chỉnh record trước khi lưu và xử lý tiếp sau khi lưu áp cho mọi nguồn ghi (record trigger), việc nền dài hoặc chạy theo lịch cron có retry (background job), nhận webhook từ hệ thống ngoài không có phiên người dùng (inbound webhook), ký/kiểm tra chữ ký, mã hoá/giải mã dữ liệu trao đổi với đối tác (crypto), làm mới record/toast cho người đang mở record (push message), gửi notification vào danh sách thông báo hoặc email từ hộp thư của Workspace (notification, email), dựa vào phòng ban, vị trí và chuỗi quản lý để chọn người duyệt, kiểm tra quyền duyệt (cơ cấu tổ chức), cung cấp thao tác ngắn có input/output định kiểu cho node Process hoặc tool của AI Agent gọi (Custom Module Action), hoặc khởi chạy Process, AI Agent từ code và xử lý kết quả trong background job | Tính giá, đồng bộ ERP hằng đêm, API tổng hợp tồn kho, chặn xoá đơn đã hoàn thành, nhận sự kiện thanh toán từ cổng thanh toán, ký và mã hoá request gửi ngân hàng, tính lại tổng đơn rồi tải lại trang record đang mở, báo quản lý trực tiếp khi có yêu cầu nghỉ phép, chỉ cho quản lý của người tạo duyệt đề nghị thanh toán, gửi báo giá kèm file từ hộp thư dùng chung, chấm điểm lead làm một bước của Process và một tool của AI Agent, gửi đơn sang Process phê duyệt rồi cập nhật đơn khi Process kết thúc, nhờ AI Agent phân loại ticket rồi lưu kết quả |
| Cả hai | Màn hình riêng cần gọi logic riêng phía backend | Màn hình xử lý đơn hàng gọi API tính giá và cập nhật đơn |
Mã frontend được tải xuống trình duyệt: logic nhạy cảm, quyền nâng cao và credential tích hợp phải nằm ở backend.
Số liệu tổng hợp không lấy bằng cách quét record. Số liệu đơn giản trên một Object mà backend tính theo điều kiện của lời gọi (đếm, tổng, trung bình, min/max, nhóm theo tối đa 3 field, không quá 5.000 nhóm, chấp nhận trễ khoảng 1 giây) dùng records.aggregate của @cogover/sdk (từ 0.13.0), chạy dưới danh tính của lời gọi; nhóm và metric ngoài count trên "id"/metric trên field số chỉ có với người gọi HTTP route hoặc data.asSystem(), không có với data.asUser() và data.object() khi lần chạy đọc dưới danh tính người dùng khác người gọi HTTP (trigger cho thay đổi do người dùng, job có người dùng, Development Session). Báo cáo, dashboard, KPI hoặc số liệu tổng hợp cần nhiều Object, nhóm theo kỳ, formula báo cáo hoặc do người dùng tự cấu hình và chia sẻ: trước hết kiểm tra bằng $report-builder có tạo được saved report tính số liệu đó không. Được thì agent đọc $report-builder để xây hoặc tái sử dụng báo cáo và phân quyền báo cáo đúng đối tượng dùng module; module đọc kết quả báo cáo bằng phiên của người dùng cuối thay vì quét record của Object (chậm, đặc biệt khi Object có từ hàng trăm record trở lên); chỉ cần xem bảng/biểu đồ chuẩn thì dùng dashboard qua $dashboard-builder. Chỉ quét record khi cả records.aggregate và báo cáo không biểu diễn được, kèm lý do; khi đó quét trong background job chia trang theo ngân sách của lần thực thi (dòng cuối bảng Chọn capability backend), không quét trong route phục vụ dashboard. Chi tiết và bảng chọn: Tận dụng báo cáo cho số liệu tổng hợp.
Ba dạng frontend khác nhau về bộ khung code và nơi hiển thị: single page app dùng stack tự chọn và mở qua /{slugSlot}/index.html; custom component và Federation Page bắt buộc dùng template React custom-frontend-module-template của Cogover, lần lượt nhúng vào layout Object qua item Federation component và hiển thị tại /{APP_SLUG}/c{N}/{PATH}. Chọn theo Các loại Custom Module và dạng frontend; lựa chọn phải được người dùng xác nhận theo mục 1A trước khi phát triển.
Môi trường cần sub-agent, shell/HTTP, công cụ build/test và trình duyệt khi làm frontend (chạy được script E2E tự động, ví dụ Playwright). Không có sub-agent: hoàn tất phần tư vấn/chuẩn bị làm được độc lập, nêu rõ điều kiện còn thiếu trước các bước bắt buộc giao agent, không tuyên bố đã đáp ứng yêu cầu phân công và không tự nhận vai reviewer để giả lập đánh giá độc lập.
Đầu vào
WORKSPACE_DOMAIN: hostname đầy đủ hoặc HTTPS origin. Chấp nhận tên viết nhầmWORKSAPCE_DOMAINvà chuẩn hóa vềWORKSPACE_DOMAIN; không tự nối domain suffix, không giữ path/query trong origin.- Workspace API Key. Credential, header và quy ước response/lỗi chung: theo $cogover-api-auth. Skill dùng cả
/bapi/v1(API Key Bearer: tạo phiên, metadata) lẫn/api/v1(phiên Web App: App, quản lý Project, gọi module); ngoại lệ riêng: mọi request/api/v1/ts-projects/...gửi cảx-req-type: 9lẫnx-req-service(3production,4quản lý,6preview), thiếux-req-type: 9thì service4bị Authorization Server xử lý như logout và thu hồi phiên; quản lý TS Project dùngPOSTcho cả đọc/list và trả object trực tiếp, không bắt buộc enveloper: 0; vớix-req-type: 9response là nguyên status/header/body của Runtime, không bọc{serviceVersion, service, id, type, body}, lỗi do Authorization Server sinh ra có headerx-proxy-error: 1. Cogover Dev CLI đọc key theo tênCOGOVER_API_KEYvới thứ tự nguồn credential riêng: CLI, session và bàn giao. - Yêu cầu người dùng: luồng nghiệp vụ, người sử dụng, dữ liệu vào/ra, thao tác đọc/ghi, UI, trigger/lịch/tích hợp, kết quả mong đợi. Chỉ làm rõ phần chưa biết có ảnh hưởng thiết kế.
- Sửa dự án: tra project ID/slug, thư mục source và version đang active; resolve đúng project trước khi sửa.
- Không ghi key/secret vào source,
cogover.json, frontend bundle, archive, log hay báo cáo. Không hardcode credential tích hợp kể cả trong source backend; chỉ dùng secret/credential của Project (bước 6A) và đọc quafetch({ credential }),secrets.gethoặc key{ secret: name }của thao táccrypto(HMAC, AES, RSA,sign); AES key và private key luôn lưu thành secret, không viết trong source. Giá trị secret, inbound key và HMAC secret do người dùng/quản trị viên nhập qua prompt ẩn hoặc stdin của CLI, không qua chat. Backend không mặc nhiên có mọi API của Node.js.
Tài liệu theo nhánh
Đọc bản hiện tại của mỗi skill được gọi và reference bắt buộc trước khi thao tác; truyền lại Workspace và yêu cầu đã xác định, không hỏi lặp đầu vào.
| Khi thực hiện | Đọc |
| Chọn loại module và dạng frontend, chuẩn bị xác nhận 1A | Các loại Custom Module và dạng frontend |
| Xác thực, quản lý Project hoặc bàn giao cURL | $cogover-api-auth và CLI, session và bàn giao |
| Thiết kế quyền, tạo security rules cho Object mới | $user-permission và Object Security Rules API, gồm rule giữ chỗ không chọn nhân sự |
| Quản lý backend, policy, key, version, production/preview | Custom Backend Module API Reference |
| Quản lý frontend, version và asset URL | Custom Frontend Module API Reference |
Lưu source của Project trên Git server của Workspace (cogover-dev git setup), quản lý repository, tài khoản Git, quyền và token; đặt tài liệu, ảnh, E2E vào repository nào | CLI, session và bàn giao và mục Bố cục repository; mục Repository của module và Git repository của Backend API Reference, Repository của module của Frontend API Reference |
| Viết hoặc sửa code backend, chọn API đặc biệt | Bảng Chọn capability backend bên dưới, rồi SDK usage guide đọc trước khi code (mẫu handler, chọn field đọc record, record liên kết, đếm và tính tổng record, filter, fetch, secret/credential, state, lock, push message, background job, Custom Module Action, khởi chạy Process và AI Agent, notification, email, cơ cấu tổ chức, danh tính, quy tắc theo role người gọi, TypeScript config, router, nhận webhook), rồi SDK API reference: nền tảng handler/router/request/response, errors và các mục capability đang dùng, gồm Push message khi cần làm mới record, toast hoặc message ngầm cho web client |
| Cần ví dụ chạy được cho một API SDK cụ thể | Repo cogover-sdk-samples: mỗi file src/samples/<area>/<name>.ts là một route minh họa một nhóm API (router, response, invocation, records, filters, identity, state, locks, fetch, schema, logging, errors, push, jobs, secrets, crypto, inbound webhook, cơ cấu tổ chức org, notification, email), src/triggers/ và src/jobs/ là trigger/job mẫu, GET / là catalog kèm cURL; README (Anh/Việt) có bảng tra API → file. Đọc file sample tương ứng trước khi code một API chưa quen; API chưa có dòng trong bảng của README thì theo usage guide và quick start, không suy đoán từ sample khác; kiểm tra phiên bản @cogover/sdk ghim trong package.json của repo khớp bản dự án (từ 0.12.0 mới có sample org, notification, email; từ 0.13.0 mọi lệnh đọc trong sample có fields và nhóm đọc record có sample fields: "*", expandLookups, records.aggregate; Custom Module Action, processes, agents cần bản ghim từ 0.15.0, deliverTo của agents.start từ 0.16.0, và dòng tương ứng trong bảng README); sample notification/email gửi tới người thật nên chỉ chạy trên Workspace thử nghiệm, cần notification channel của Workspace và mục email trong identity policy theo README của repo; không sao chép cogover.json, Object mẫu sample_*, sample ghi/xoá record hay lịch mẫu vào project nghiệp vụ |
Đọc record (records.get/getMany/list): chọn fields, tên hoặc field của record liên kết, đếm/tổng hợp đơn giản bằng records.aggregate, nâng project lên SDK 0.13.0 | Đọc record hiệu quả (route mẫu đọc opportunity/account từ fields tối thiểu, expandLookups, tới records.aggregate có nhóm, cùng cách xử lý lookup name rỗng), rồi các mục Chọn field cần đọc, Đọc record liên kết, Đếm và tính tổng record trong SDK usage guide và Chọn field, Record liên kết, Aggregate trong SDK API reference; chọn giữa records.aggregate và saved report theo Tận dụng báo cáo |
Ghi nhiều record hoặc gọi nhiều SDK call trong một lời gọi: gộp bằng records.getMany/batchInsert/batchUpdate, chạy call độc lập song song bằng Promise.all; client xử lý HTTP 429 có Retry-After khi Workspace bận | Ghi record hiệu quả, mục Lời gọi song song của SDK API reference |
Thiết kế route, trigger, job hoặc dashboard xử lý nhiều record hay nhiều lời gọi; gặp RateLimitError/RATE_LIMITED; đo mức đã dùng | Giới hạn của một lần thực thi và Đọc giới hạn trong code trong SDK API reference, mục Quyền và giới hạn của SDK usage guide; usage/operations của Preview chính xác một version và durationMs/usage của lần chạy job trong Backend API Reference; chạy local theo CLI, session và bàn giao |
| Khởi tạo/code/test backend local | Backend quick start |
| Viết record trigger before-change/after-change, chạy trigger local, publish/activate và kiểm thử bằng thay đổi bản ghi thật | Record trigger quick start, mục Record trigger trong SDK usage guide và SDK API reference |
| Việc nền dài, chạy trễ, chạy theo lịch cron, retry lỗi tạm thời, enqueue từ route/trigger/webhook | Background job quick start, mục Chạy việc nền bằng job trong SDK usage guide và Background job trong SDK API reference; xem run/lịch và enqueue thủ công theo mục Background job của Backend API Reference hoặc cogover-dev jobs |
Cho node Process hoặc tool AI Agent gọi logic của module (Custom Module Action): khai báo defineAction, schema s, effect, danh tính chạy, lỗi bên gọi nhận; nối action vào Process hoặc AI Agent | Mục Cho Process và AI Agent gọi code của bạn trong SDK usage guide, Custom Module Action trong SDK API reference (giới hạn schema, context action/invocation.source, quy tắc thực thi, bảng lỗi bên gọi nhận); danh mục action GET /api/v1/ts-projects/actions và mã lỗi theo mục Custom Module Action của Backend API Reference; node Process theo Custom Module Action Task của $process-creator, tool AI Agent theo mục CUSTOM_MODULE của $agent-builder |
Khởi chạy Process (Normal Flow) hoặc lượt chạy AI Agent từ route, trigger after-change, job hoặc action và xử lý kết quả bằng job; cho AI Agent chủ động nhắn chat một nhân sự (deliverTo) | Mục Khởi chạy Process và AI Agent từ code trong SDK usage guide, Process và AI Agent trong SDK API reference (job onComplete/onResult, idempotency, giới hạn, lỗi; deliverTo và kết quả delivery ở mục Gửi tin nhắn chat cho một nhân sự); mục processes/agents của identity policy theo Process và AI Agent của Backend API Reference; thiết kế Normal Flow và biến input/output bằng $process-creator (Normal Flow), agent bằng $agent-builder |
| Gọi API ngoài có token, lưu/xoay secret, ký bằng secret | Secrets và credentials quick start, mục Dùng secret và credential trong SDK usage guide, Secret và credential và Xác thực bằng credential trong SDK API reference; quản lý theo mục Secret của Backend API Reference hoặc cogover-dev secrets |
| Băm, ký/kiểm tra HMAC, token ngẫu nhiên, so sánh an toàn; mã hoá/giải mã AES hoặc RSA, ký/xác minh RSA hoặc ECDSA (API ngân hàng, JWT) | Crypto quick start, mục Mã hoá, giải mã và ký dữ liệu trong SDK usage guide và Mã hoá và chữ ký trong SDK API reference (định dạng key ở mục Key, bảng JWT alg ở mục crypto.sign) |
| Nhận webhook từ hệ thống ngoài không có phiên Cogover | Inbound webhook quick start, mục Nhận webhook trong SDK usage guide và Inbound webhook trong SDK API reference; tạo/rotate/revoke inbound access và cách bên gửi gọi theo mục Inbound access và Gọi inbound webhook của Backend API Reference hoặc cogover-dev inbound |
| Gửi notification (danh sách thông báo, web/mobile push, bản sao email theo notification channel) hoặc email từ hộp thư dùng chung/hộp thư cá nhân của actor, ghi nhận email trên timeline record | Mục Gửi notification và email trong SDK usage guide, Notification và Email trong SDK API reference; grant hộp thư gửi theo mục Người gửi email của Backend API Reference; notification channel là record của Object notification_channel, resolve record ID bằng $object-record và ý nghĩa kênh theo Thông báo ở Tầng 2; danh mục thông báo (categories) là record của Object notification_category, truyền theo code (ổn định giữa các Workspace) thay vì record ID, tra code bằng $object-record |
| Đọc phòng ban, vị trí, nhân sự của người gọi; chuỗi quản lý, chọn người duyệt, kiểm tra quyền duyệt hoặc thuộc phòng ban | Cơ cấu tổ chức quick start, mục Đọc phòng ban, vị trí và quản lý trong SDK usage guide và Cơ cấu tổ chức trong SDK API reference; resolve hoặc chuẩn bị dữ liệu phòng ban, nhân sự, vị trí, cấp quản lý (level) và phòng ban chính (is_primary) để thiết kế và kiểm thử bằng $user-permission (Departments, Personnels, Positions API) |
| Khởi tạo/build/test frontend single page app | Frontend quick start |
| Khởi tạo/code/test custom component nhúng layout | Custom component quick start cùng README.vi.md, .agents/skills/custom-module-foundation, .agents/skills/custom-module-form-builder và src/types/form-builder.d.ts trong template đã clone |
| Khởi tạo/code/test Federation Page | Federation Page quick start cùng README.vi.md, .agents/skills/custom-module-foundation và .agents/skills/custom-module-api trong template đã clone |
| Frontend gọi backend | Full-stack integration |
Dùng @cogover/client-sdk ở frontend: UI component, data hook (chỉ custom component và Federation Page), nhận push của backend (useSocketListener; single page app dùng createSocketListener từ @cogover/client-sdk/socket), kiểm tra phiên bản SDK của nền tảng | Dùng @cogover/client-sdk ở frontend |
| Viết và chạy E2E frontend, tạo phiên trình duyệt test | Kiểm thử E2E frontend, $cogover-api-auth khi người dùng không cung cấp tài khoản; persona theo Kiểm thử quyền runtime |
| Review code và kiểm thử end-to-end backend ghi dữ liệu | Review và kiểm thử backend độc lập |
| Review bảo mật code backend, frontend, dependency, repository và gói publish trước khi publish | Review bảo mật |
| Đặt ngưỡng hiệu năng; đo route, action, trigger, job, báo cáo hoặc frontend với dữ liệu đại diện và sửa theo vòng | Kiểm thử hiệu năng |
| Báo cáo, dashboard, KPI hoặc số liệu tổng hợp từ một hoặc nhiều Object | Tận dụng báo cáo cho số liệu tổng hợp (chọn records.aggregate hay saved report), $report-builder để kiểm tra khả năng và tạo saved report; $dashboard-builder khi dashboard chuẩn đáp ứng |
| Preview/apply, ghi hàng loạt, decimal, concurrency hoặc ghi field mà backend của App chuẩn hay hệ thống khác cũng cập nhật | Batch writes và checkpoint, gồm mục Ghi số liệu do backend khác duy trì, cùng contract state/locks/records trong SDK |
Nhiều lời gọi của module chạy cùng lúc (route, webhook, trigger after-change, job) cùng sửa một record/state; cần chạy lần lượt (maxConcurrentRuns) hoặc client gửi lại sau timeout/429 | Mục Lời gọi đồng thời của cùng module, Lời gọi module chạy đồng thời và Cập nhật module |
Hai API reference là nguồn chuẩn cho HTTP contract; quick start chỉ là cách làm với CLI và sample. Tất cả là snapshot tài liệu sản phẩm tiếng Việt ngày 2026-09-06, riêng ba tài liệu về các dạng frontend, custom component và Federation Page ngày 2026-09-15, Record trigger quick start, Background job quick start và hướng dẫn Đọc record hiệu quả ngày 2026-09-29, Backend quick start, Frontend quick start, Backend API Reference và Frontend API Reference ngày 2026-10-01 (gồm Git repository của Workspace và repository của module), Secrets/credentials quick start ngày 2026-09-22, Crypto quick start ngày 2026-09-23, hai quick start inbound webhook và cơ cấu tổ chức ngày 2026-09-24, hướng dẫn Ghi record hiệu quả cùng các mục về lời gọi đồng thời, 429/Retry-After, maxConcurrentRuns và lời gọi song song ngày 2026-10-24; SDK usage guide và SDK API reference theo @cogover/sdk 0.16.0 (snapshot ngày 2026-10-13; tài liệu mô tả ngân sách nền tảng áp cho mỗi lần thực thi, mặc định HTTP route, Custom Module Action và trigger after-change 100 capability call, 200 lời gọi cục bộ (crypto, org không có withDisplay), 10.000 record đọc, 10.000 record ghi; trigger before-change 20 capability call, 50 lời gọi cục bộ, 4.000 record đọc, không ghi; background job lần lượt 1.000, 1.000, 50.000, 50.000; vượt thì RateLimitError có details.budget; limits.usage()/ctx.limits đọc mức đã dùng; giá trị có thể khác giữa môi trường nên code đọc limit từ limits.usage() thay vì ghi cứng; từ 0.13.0 mọi lệnh đọc records.get/getMany/list bắt buộc fields (danh sách slug hoặc "*"), kiểu kết quả chỉ gồm field đã chọn, lookup trả name: "" và system.createdBy.name là "" trừ khi bật expandLookups, có records.aggregate, và typecheck cần TypeScript từ 5.0; project publish bằng SDK cũ mà đọc không có fields bị từ chối với fields is required; record trigger và records.getMany cần SDK từ 0.6.0, push message từ 0.7.0, background job, secret/credential, crypto hash/HMAC/random và inbound webhook từ 0.8.0, mã hoá AES/RSA và chữ ký RSA/ECDSA (aesEncrypt, aesDecrypt, rsaEncrypt, rsaDecrypt, sign, verify) từ 0.9.0, cờ Super Admin và danh sách role của người gọi (invocation.user.membership.isSuperAdmin, roles) từ 0.10.0, cơ cấu tổ chức (org) từ 0.11.0, notification và email (notifications.send, email.send, email.senders) từ 0.12.0, fields bắt buộc, expandLookups và records.aggregate từ 0.13.0, limits (limits.usage(), ctx.limits) từ 0.14.0, Custom Module Action (defineAction, schema builder s, named export actions), processes và agents từ 0.15.0, deliverTo của agents.start và kết quả delivery (kiểu AgentDeliveryTarget, AgentDelivery) từ 0.16.0; chạy trigger local cần Dev CLI từ 0.10.0, các lệnh secrets, inbound, jobs cần Dev CLI từ 0.13.1, truyền isSuperAdmin/roles vào script local và đọc org trên local cần Dev CLI từ 0.14.0, thử notifications.send/email.send trên local cần Dev CLI từ 0.15.0, bản xếp hai thao tác này là ghi nên session --allow-writes=false từ chối; chạy local áp cùng ngân sách theo loại lần thực thi cần Dev CLI từ 0.19.0 và SDK từ 0.14.0 (--no-enforce-limits chỉ đếm, không từ chối), jobs runs/jobs run hiện durationMs và usage của lần thử gần nhất từ Dev CLI 0.19.0, xếp processes.start/agents.start là thao tác ghi (session --allow-writes=false từ chối), áp trần 20 processes.start và 5 agents.start mỗi lần gọi handler và từ chối onComplete/onResult trên local với NOT_SUPPORTED từ Dev CLI 0.20.0, lệnh git để lưu source trên Git server của Workspace từ Dev CLI 0.21.0), SDK cài đặt khác phiên bản thì đối chiếu thêm tài liệu public đi kèm package. Với custom component và Federation Page, các skill trong .agents/skills của template đã clone là nguồn chuẩn cho quy ước code (expose, routing, Link, asset, API client, i18n); skill này quyết định workflow, phê duyệt, kiểm thử và publish. Template thay đổi thì theo bản đã clone, không theo trích dẫn trong quick start. ID, field slug, policy và dữ liệu mẫu chỉ minh họa, không thay dữ liệu thật của Workspace. Không suy ra API từ tên gọi hoặc dùng API không được hỗ trợ.
Chọn capability backend
Chọn theo nhu cầu thực tế của contract ở bước 1 mục 5; mỗi capability có giới hạn riêng trong SDK API reference, ngoài ra mọi route, trigger, job và action chạy trong ngân sách chung của một lần thực thi (dòng cuối bảng). Mọi capability ngoài route đều là một phần của version backend (áp dụng khi activate) trừ secret/credential và inbound access được cấu hình trên Project và dùng ngay cho version active; grant hộp thư gửi email và mục processes/agents nằm trong identity policy nên phải duyệt cho từng version.
| Nhu cầu | Dùng | Lưu ý, không dùng khi |
| Trả kết quả ngay trong HTTP response cho người dùng hoặc frontend | HTTP route (createRouter) hoặc defineScript một endpoint | Việc quá vài giây, có thể vượt ngân sách của route (dòng cuối bảng), phải sống sót qua lỗi tạm thời hoặc restart: route chỉ kiểm tra input rồi jobs.enqueue và trả 202 với runId |
| Chỉ Super Admin hoặc người giữ role nhất định được thực hiện thao tác nghiệp vụ (duyệt, mở khoá, điều chỉnh) | invocation.user.membership.isSuperAdmin và invocation.user.membership.roles ({ id, name }, sắp theo tên); SDK từ 0.10.0 | Chỉ là quy tắc nghiệp vụ trong code: không thay security rules, quyền record/field vẫn theo danh tính người gọi và không có danh sách quyền của role; so theo role id (tên đổi được, ID khác nhau giữa Workspace); user là null với danh tính system/inbound; record trigger và background job có thể nhận thay đổi role chậm khoảng một phút |
| Quy tắc bắt buộc trước khi record được lưu, áp cho mọi nguồn ghi | Record trigger before-change | Chỉ đọc, fail-closed, ≤ 2 giây; không ghi record, fetch, state, lock, push, notification, email (kể cả email.senders()), jobs.enqueue, secrets.get, processes, agents hay thao tác crypto bằng key { secret }; đọc org được |
| Xử lý tiếp ngay sau khi record được lưu | Record trigger after-change | Best-effort, có thể chạy lặp; việc dài hơn timeoutMs hoặc cần state/locks thì enqueue background job |
Việc nền dài chia trang, gọi hệ thống ngoài có retry, chạy trễ (delayMs/runAt tối đa 30 ngày), chạy theo lịch cron | Background job (defineJob, named export jobs, jobs.enqueue, schedule cron năm trường với múi giờ IANA) | Workspace phải bật job; mỗi lần thử ≤ 60 giây, ít nhất một lần nên handler idempotent; run theo lịch và run từ webhook chạy danh tính system (policy allowInternalSystem), run do quản trị viên enqueue qua CLI/management API chạy dưới danh tính Super Admin đã enqueue; không tự làm luồng có người tham gia (chờ phê duyệt bằng User Task) hay AI Agent: khởi chạy Process/AI Agent và nhận kết quả bằng job (dòng Khởi chạy Process hoặc AI Agent) |
| Node Process hoặc tool AI Agent cần gọi logic của module (tính, kiểm tra, đọc/ghi dữ liệu theo quy tắc riêng) và nhận ngay kết quả có cấu trúc cho bước sau | Custom Module Action: defineAction (key, label, description, exposeTo "process"/"agent", effect "read"/"write", input/output bằng schema builder s, timeoutMs 1.000–8.000, mặc định 8.000) liệt kê trong named export actions (tối đa 50); SDK từ 0.15.0 | Đồng bộ, tối đa 8 giây, ngân sách của HTTP route: việc dài hơn thì jobs.enqueue với action.runId làm idempotency key rồi trả ngay; effect: "read" từ chối mọi thao tác ghi (jobs.enqueue, processes.start cũng vậy), "write" làm tool AI Agent mặc định cần phê duyệt; schema chỉ tạo bằng s (không pattern, lồng tối đa 3 cấp, 50 property mỗi object, input + output ≤ 16 KiB) và cùng label/mô tả hiển thị với mọi thành viên Workspace nên không chứa bí mật; bên gọi chỉ nhận mã lỗi và thông báo cố định, không nhận message của handler, nên kết quả dự kiến (không tìm thấy, không đủ điều kiện) trả trong output; danh tính do node/tool chọn (người khởi tạo Process, nhân sự chỉ định, danh tính của agent, hoặc không có người dùng cần allowInternalSystem); không dùng thay route cho HTTP client/frontend, không thay trigger cho quy tắc áp mọi nguồn ghi; không gọi module bằng node Send HTTP Request hay HTTP tool |
| Khởi chạy Process (phê duyệt, luồng có User Task người dùng tự cấu hình) hoặc lượt chạy AI Agent (phân loại, tóm tắt, soạn thảo, chủ động nhắn chat một nhân sự) từ code và xử lý kết quả | processes.start(processInfoId, { input, instanceName, idempotencyKey, onComplete }), processes.get; agents.start(agentId, { instruction, variables, records, resultSchema, runAs, approvalPolicy, timeoutSeconds, idempotencyKey, onResult, deliverTo }), agents.get; SDK từ 0.15.0, deliverTo từ 0.16.0 | Chạy nền: start trả ngay instanceId/runId, kết quả giao cho job của version active qua onComplete/onResult, không chờ trong route; identity policy phải có mục processes/agents (thiếu thì PermissionDeniedError PROCESSES_NOT_ALLOWED/AGENTS_NOT_ALLOWED), execution không có người dùng cần allowSystem và agent runAs: "agent", approvalPolicy: "autoApprove" cần allowAutoApprove và execution có người dùng; Process phải là Normal Flow đã kích hoạt và xuất bản; tối đa 20 processes.start và 5 agents.start mỗi lần thực thi, 1.000 Process mỗi ngày UTC mỗi project, maxRunsPerDay của agent (mặc định 200); deliverTo: { personnelId, conversation, title } cho agent gửi tin vào AI chat của agent với nhân sự đó (người nhận phải được dùng agent và có seat AI Agent, không cần mục policy riêng), kết quả gửi ở delivery, tách khỏi status của lượt chạy; không có trong before-change, action "read" và session chỉ đọc; Dev CLI tạo lượt chạy thật, gửi tin deliverTo thật, nhưng từ chối onComplete/onResult (NOT_SUPPORTED) |
| Gọi API ngoài cần token trong HTTP header | fetch(url, { credential }) với credential loại BEARER/BASIC/HEADER và host được phép | Code không thấy giá trị; request tới host ngoài danh sách, tự đặt cùng header hoặc tên không active bị FETCH_BLOCKED; không có trong before-change |
| Cần chính giá trị bí mật: ký, đặt vào body hay định dạng khác | secrets.get (secret OPAQUE) hoặc truyền { secret: name } làm key của crypto (HMAC, AES, RSA, sign/verify) để ký, mã hoá, giải mã mà không đọc giá trị | ≤ 20 thao tác secret mỗi invocation, giá trị cache trong invocation; không ghi vào log/record/state/response; Development Session mặc định bị từ chối (SECRETS_NOT_ALLOWED); không có trong before-change |
| Băm nội dung, ký/kiểm tra HMAC, token và nonce ngẫu nhiên, so sánh chữ ký | crypto.sha256, hmacSha256, randomBytes, randomUUID, timingSafeEqual | Chỉ SHA-256; hash không che được giá trị dễ đoán và không dùng lưu mật khẩu, cần giữ bí mật thì mã hoá (dòng dưới); so sánh chữ ký/token luôn bằng timingSafeEqual; ký/kiểm tra trên đúng chuỗi byte gửi đi (request.rawBody); dùng được cả trong before-change trừ key { secret } |
Mã hoá/giải mã dữ liệu trao đổi với hệ thống ngoài; ký hoặc xác minh chữ ký khoá công khai (API ngân hàng, JWT RS*/PS*/ES*) | crypto.aesEncrypt/aesDecrypt (AES-GCM mặc định, CBC), rsaEncrypt/rsaDecrypt (OAEP-SHA256 mặc định, OAEP-SHA1, PKCS1), sign/verify (RSA PKCS #1 v1.5, RSA-PSS, ECDSA) với key { secret: name }; SDK từ 0.9.0 | AES key và private key luôn là secret OPAQUE, không viết trong source; mode/padding/IV/tag/định dạng chữ ký/encoding phải khớp tài liệu bên kia; RSA chỉ mã hoá giá trị nhỏ (190 byte với key 2048 bit, OAEP-SHA256), dữ liệu lớn mã hoá bằng AES rồi chỉ mã hoá AES key bằng RSA; giải mã thất bại ném DECRYPTION_FAILED, verify trả false thay vì ném; key từ secret không có trong before-change |
| Hệ thống ngoài gọi vào không có phiên người dùng Cogover | Inbound webhook: route dưới /hooks/ + inbound access KEY hoặc HMAC do quản trị viên tạo (bước 6A) | Cogover xác thực trước handler (401 khi sai); handler kiểm tra invocation.identity === "inbound", giữ ngắn, chống trùng theo event ID rồi enqueue job; record dùng danh tính system (policy); Workspace session không thay được inbound credential |
| Làm mới record đang mở, toast, message ngầm cho custom component | Push message (push.refreshRecords, push.toast, push.message) | Best-effort, không thu hồi; không có trong before-change và Development Session chỉ đọc; chỉ tới người đang mở web client, không lưu lại: việc người nhận cần xử lý sau thì dùng notification |
| Báo cho người dùng một việc cần xử lý sau (yêu cầu duyệt, nhắc hạn, kết quả xử lý), lưu trong danh sách thông báo và gửi qua web push, mobile push, bản sao email theo kênh | notifications.send (to 1–200 personnel ID, exclude: ["actor"], channel là record ID của notification_channel, categories là tối đa 10 code hoặc record ID của notification_category (SDK từ 0.17.0), link, sender, email: false, idempotencyKey); SDK từ 0.12.0 | Không cần grant trong identity policy; best-effort, không thu hồi; tối đa 5.000 người nhận mỗi giờ mỗi project; personnel chưa có tài khoản bị bỏ qua (skippedPersonnelIds); bản sao email gửi từ địa chỉ thông báo của Cogover và tính vào hạn mức email thông báo hằng ngày theo gói; không có trong before-change, preview và Development Session chỉ đọc |
| Gửi email thật từ hộp thư của Workspace tới khách hàng hoặc nhân sự, ghi nhận trên timeline record, đính kèm file của record | email.send với from: { mailbox: id } (hộp thư dùng chung) hoặc from: "actor" (hộp thư cá nhân của người khởi đầu execution), record, recordEmailFields, attachments, idempotencyKey; email.senders() chọn hộp thư theo địa chỉ; SDK từ 0.12.0 | Hộp thư phải được grant trong mục email của identity policy đã duyệt cho đúng version (workspaceMailboxIds, allowActorMailbox), nếu không ném EMAIL_SENDER_NOT_GRANTED; execution không có người dùng cần allowInternalSystem: true và không dùng được "actor"; tối đa 50 người nhận, 10 file/20 MB, 500 email mỗi giờ mỗi project; lời gọi xong khi Cogover tiếp nhận, lỗi gửi sau đó không báo lại; không có trong before-change, email.send bị từ chối trong preview và Development Session chỉ đọc |
| Biết người gọi thuộc phòng ban/vị trí nào, liệt kê thành viên, tìm quản lý hoặc chuỗi người duyệt, kiểm tra ai quản lý ai hoặc ai thuộc phòng ban | org (me, personnel.get/getMany/managerChain, departments.get/tree/ancestors/members/managers/managerChain, positions.list/get/members, isManagerOf, isInDepartment); SDK từ 0.11.0 | Chỉ đọc, dùng được cả trong before-change; cấu trúc lấy từ bản cơ cấu đã cache, thay đổi có hiệu lực sau vài giây; withDisplay: true mới có tên, email, avatar (thêm lượt đọc, trang tối đa 200; đọc có withDisplay là capability call, không có là lời gọi cục bộ); field khác của nhân sự đọc qua data.object("personnel"); job, inbound webhook và trigger do hệ thống ghi cần allowInternalSystem: true (IDENTITY_NOT_GRANTED); level của membership là cấp quản lý (0 nhân viên, 1 cao nhất), không phải độ sâu cây |
| Đọc record để xử lý nghiệp vụ | records.get/getMany/list với fields là danh sách slug tối thiểu code dùng; "*" chỉ khi thực sự cần mọi field được đọc; expandLookups (true hoặc { lookup: [...] | "*" }) chỉ khi cần tên hoặc field của record đích; SDK từ 0.13.0 | Thiếu fields ném ValidationError trước khi gửi; truy cập field không yêu cầu lỗi compile; "*" là mọi field danh tính đang dùng được đọc (với data.asUser()/data.asSystem() chỉ các field identity policy cho phép; data.object() không cần policy), dễ vượt giới hạn 1 MiB mỗi lời gọi với getMany/list; mỗi record trả về, kể cả record liên kết của expandLookups, tính vào ngân sách record đọc, list giữ chỗ bằng limit trước khi gửi; lookup không mở rộng trả name: "" (field reference vẫn có tên), system.createdBy.name là ""; mở rộng tốn thêm lượt đọc Object đích, một cấp, tối đa 1.000 record liên kết mỗi lời gọi, kiểm quyền theo danh tính gọi; khi mở rộng, createdBy.name là field name của record nhân sự (có thể là mã sinh tự động), tên hiển thị lấy bằng org.personnel.get với withDisplay: true; record.new/record.old của trigger đã có tên lookup |
| Số liệu đơn giản trên một Object theo điều kiện của lời gọi (đếm, tổng, trung bình, min/max, theo nhóm) để trả về hoặc quyết định trong backend | records.aggregate (where, metrics 1–20, groupBy 1–3 field, limit tối đa 5.000 nhóm); SDK từ 0.13.0 | Một capability call, không đọc record (không tính ngân sách record đọc), dưới danh tính của lời gọi. groupBy chỉ nhận field choice (single_choice, multi_choices, radio_button, checkbox), boolean, lookup_normal, date/date_time và số, không nhận text hay reference; count/countDistinct nhận field số, ngày, lựa chọn, boolean, lookup_normal, text, email, phone, auto_number, không nhận reference, file, url, formula, rollup_summary; không record khớp thì count/sum là 0, avg/min/max là null. data.asUser() và data.object() khi đọc dưới danh tính người dùng khác người gọi HTTP (trigger cho thay đổi do người dùng, job có người dùng, Development Session) không group được và chỉ có count trên "id", count/sum/avg/min/max trên field số: nhóm hoặc metric khác ở đó dùng data.asSystem() (cần grant trong identity policy); dữ liệu trễ khoảng 1 giây, countDistinct xấp xỉ: không dùng cho quy tắc cần chính xác tại thời điểm ghi; nhiều Object, nhóm theo kỳ, formula hoặc báo cáo người dùng tự cấu hình thì dùng saved report (dòng dưới); gọi được trong before-change nhưng làm chậm thao tác ghi |
| Số liệu tổng hợp (đếm, tổng, trung bình, min/max, trung vị theo nhóm hoặc kỳ) từ một hoặc nhiều Object cho báo cáo, dashboard, KPI | Saved report xây bằng $report-builder với ACL theo đối tượng dùng module (bước 2B); frontend gọi /api/v1/report service 200 (setting: true) bằng phiên người dùng cuối, backend chỉ xử lý phần nghiệp vụ khác | Không quét record khi báo cáo tính được. Backend chưa gọi được báo cáo bằng phiên người dùng cuối (script không nhận cookie/authorization, SDK chưa có API chạy saved report): khi backend cần số liệu để ra quyết định thì dùng records.aggregate nếu số liệu thuộc phạm vi dòng trên, còn lại chọn phương án theo Giới hạn khi backend cần số liệu báo cáo; không nhận số liệu client gửi làm căn cứ quyết định cần bảo vệ |
| Lưu tiến độ, checkpoint nhỏ; chặn chạy đồng thời | Project state (CAS expectedVersion) và distributed locks | Không transaction, không exactly-once; không lưu secret vào state; không có trong trigger context |
| Route, trigger, job, action hoặc dashboard xử lý nhiều record hay nhiều lời gọi; biết lần thực thi còn bao nhiêu ngân sách | Thiết kế theo ngân sách của một lần thực thi (mặc định route, action và after-change 100 capability call, 200 lời gọi cục bộ, 10.000 record đọc, 10.000 record ghi; before-change 20, 50, 4.000, không ghi; job 1.000, 1.000, 50.000, 50.000) và đọc mức đã dùng bằng limits.usage()/ctx.limits (SDK từ 0.14.0, không tốn lời gọi) | Ngân sách thuộc lần thực thi: chờ hay chạy lại không giúp. Số tổng dùng records.aggregate hoặc saved report, không quét record; việc lớn chia trang trong background job và enqueue trang kế khi remaining sắp hết; capability call chạy lần lượt kể cả với Promise.all, nên nhiều lời gọi vẫn phải lọt 8 giây của route; không ghi cứng con số, đọc limit từ usage() |
Workflow
Điều phối sub-agent
Agent chính giữ yêu cầu, quyết định kiến trúc, schema đã duyệt, contract tích hợp và nghiệm thu cuối; tự làm bước 4–6 và 9–10 (trừ E2E trình duyệt do sub-agent E2E chạy, review và kiểm thử backend do sub-agent Review backend chạy, review bảo mật do sub-agent Review bảo mật chạy, đo hiệu năng do sub-agent Hiệu năng chạy), tổng hợp các yêu cầu duyệt còn thiếu. Phân công theo nhánh thực sự cần, mỗi nhánh một sub-agent riêng:
| Sub-agent | Phạm vi |
| Khảo sát | Bước 1 mục 2–3 và bảng sự kiện → thay đổi của bước 1 mục 6, chỉ đọc cấu hình và metadata Workspace. Spike ghi dữ liệu test của mục 6 giao sub-agent riêng, khác sub-agent Backend, sau khi người dùng đồng ý phạm vi ghi |
| Thiết kế dữ liệu/Excel | Bước 2 mục 1–4 khi cần bổ sung schema; khác sub-agent khảo sát |
| Security | Bước 2A cho từng Object mới, dùng $user-permission |
| Process | Bước 3, dùng $process-creator, gồm node Custom Module Action gọi action của module và Normal Flow do module khởi chạy bằng processes.start; phần phụ thuộc backend hoàn tất sau bước 9 |
| AI Agent | Bước 3 khi action mở cho agent hoặc module khởi chạy agent: dùng $agent-builder tạo/tái sử dụng agent, skill, tool CUSTOM_MODULE và phân quyền, kiểm thử bằng chat; phần phụ thuộc backend hoàn tất sau bước 9 |
| Báo cáo | Bước 2B khi contract có chỉ số lấy từ saved report: đọc $report-builder, xây hoặc tái sử dụng Report Type/saved report, đặt ACL theo đối tượng dùng module, chạy service 200 bằng phiên persona xác minh số liệu và quyền, bàn giao payload và mẫu response đã redact |
| Backend | Bước 7–8 phần backend: khởi tạo local, code HTTP handler, record trigger, background job, Custom Module Action, khởi chạy Process/AI Agent, inbound webhook, notification/email, logic theo cơ cấu tổ chức, unit tests/coverage, kiểm thử tích hợp |
| Frontend | Bước 7–8 phần frontend theo dạng đã xác nhận ở 1A: khởi tạo local, code, unit tests/coverage, tự kiểm tra trên browser khi phát triển. Custom component/Federation Page: clone template và đọc .agents/skills trước khi code |
| E2E frontend | Bắt buộc khi có frontend: viết E2E test tự động từ contract và tiêu chí nghiệm thu, chạy trên trình duyệt ở bước 8 (các ca chạy được trên local) và bước 9 (Workspace); tạo phiên test bằng $cogover-api-auth khi người dùng không cung cấp tài khoản; không sửa source ứng dụng. Theo Kiểm thử E2E frontend |
| Review backend | Bắt buộc khi backend có record trigger, background job, Custom Module Action effect: "write" hoặc route ghi dữ liệu: bước 8 review code tại commit sẽ publish, bước 9 kiểm thử end-to-end trên version đã activate bằng script đặt ở e2e/ của repository backend; không sửa source ứng dụng, không commit, không publish/activate, không sửa tay số liệu, không xoá record. Theo Review và kiểm thử backend độc lập |
| Review bảo mật | Bắt buộc với mọi project có code: bước 8 review code backend, frontend, dependency, repository và gói publish tại commit sẽ publish, review phần chênh lệch của mỗi commit publish sau đó; phát hiện Nghiêm trọng/Cao chặn publish; không sửa source, không commit, không thử khai thác trên Workspace. Theo Review bảo mật |
| Hiệu năng | Bắt buộc khi route, action, trigger hoặc job xử lý nhiều record, có trigger before-change, báo cáo/dashboard hoặc frontend tải nhiều dữ liệu: bước 9 đo trên version đã activate với dữ liệu đại diện, so với ngưỡng trong contract, tối đa 2 vòng sửa; không sửa source, không commit, không load test. Theo Kiểm thử hiệu năng |
- Backend và frontend không giao chung một sub-agent. Sub-agent E2E tách khỏi sub-agent Frontend/Backend: không sửa source ứng dụng; sub-agent Frontend không sửa, nới hay skip test E2E để test pass; agent chính không tự nhận vai E2E. Sub-agent Review backend cũng khác sub-agent Backend; sub-agent Backend không sửa ca kiểm thử hay kỳ vọng để kết quả PASS. Sub-agent E2E và Review backend chỉ ghi vào
e2e/của repository được giao và không commit; agent chính xem lại rồi commite2e/; sub-agent Frontend/Backend không sửa và không stagee2e/. Sub-agent Review bảo mật và Hiệu năng cũng khác sub-agent Backend/Frontend, chỉ báo lỗi kèm bằng chứng; sub-agent phụ trách sửa, số vòng sửa theo reference của từng vai. Tái sử dụng sub-agent phụ trách xuyên suốt bước 7–9 và các vòng sửa lỗi; không tạo sub-agent cho nhánh đã bỏ qua. - Gói giao việc: yêu cầu và tiêu chí nghiệm thu; loại module và dạng frontend đã được người dùng xác nhận ở 1A; đường dẫn skill/reference cần đọc; Workspace origin; metadata đã có; phạm vi đọc/ghi và file được sửa; đầu ra bàn giao; trạng thái phê duyệt và dependency còn chờ. Nhánh triển khai thêm metadata Object/field đã xác minh, project ID/slug/slot, contract route/input/output/error và fixture được sửa. Không đòi kết quả khảo sát hoặc project chưa tạo làm đầu vào của chính bước khảo sát. Chỉ truyền cách truy cập credential an toàn hoặc tên profile.
- Thứ tự: khảo sát → agent chính kiểm tra bằng chứng, chốt phạm vi → gửi đề xuất loại module/dạng frontend (1A), song song thiết kế Excel nếu cần và schema không phụ thuộc lựa chọn (không bắt đầu workbook khi còn thiếu metadata ảnh hưởng schema) → người dùng xác nhận 1A và duyệt workbook → agent chính ghi và đọc lại schema. Có Object mới: tiếp → sub-agent Security hoàn tất 2A → agent chính nghiệm thu. Có chỉ số lấy từ báo cáo: sub-agent Báo cáo hoàn tất 2B sau khi schema liên quan đã ghi (song song 2A khi báo cáo không phụ thuộc rule mới) → agent chính nghiệm thu → khởi tạo/code. Trong lúc chờ chỉ khảo sát, thiết kế contract, ma trận quyền và kế hoạch test; không dựng source, scaffold, interface code hay test bằng fixture để đi trước.
- Sau 2A và 2B (nếu có), chốt contract chung rồi cho các nhánh độc lập chạy song song trong thư mục riêng; sub-agent E2E viết test song song với code frontend. Frontend có thể dùng fixture theo contract khi chờ backend nhưng chưa thay test tích hợp thật. Điều phối lịch test ghi khi dùng chung dữ liệu để các agent không reset hoặc sửa fixture của nhau.
- Chỉ giao quyền thao tác Workspace đã được phép; delegation không thay thế duyệt Excel, duyệt Process hoặc duyệt cấu hình AI Agent.
- Nhận lại source, lệnh build/test, coverage riêng từng project, kết quả kiểm thử và dependency còn thiếu; kiểm tra bằng chứng, giao lại lỗi cho sub-agent phụ trách, nghiệm thu tích hợp. Thông báo “xong” không phải bằng chứng PASS.
1. Khảo sát và chọn frontend/backend
- Đọc $cogover-overview rồi các reference tính năng của miền nghiệp vụ liên quan. Danh mục App trong overview không phải danh sách App đã cài.
- Giao sub-agent khảo sát mục 2–3; truyền yêu cầu nghiệp vụ, phạm vi khảo sát và kết quả đọc overview. Sub-agent dùng $app-menu-manager cùng
$cogover-api-authliệt kê App thật trên Workspace (GET /api/v1/apps, phân trangpage/limittheo contract), đọc detail/menu của App liên quan; ghi ID, slug, standard/custom, trạng thái, phạm vi quyền, chức năng có sẵn. App bị ẩn/thiếu quyền hoặc list chưa đủ trang không chứng minh App chưa cài: phân biệt chưa có, inactive và chưa xác minh được. - Cùng sub-agent dùng $object-info đọc Objects, fields, options, metadata, related lists liên quan; có automation cũ thì khảo sát Process theo
$process-creatortrước khi đề xuất thêm. Yêu cầu có số liệu tổng hợp: chỉ số đơn giản trên một Object mà backend tính theo điều kiện của lời gọi thì kết luậnrecords.aggregate; các chỉ số còn lại đọc$report-builder, liệt kê Report Type và saved report liên quan (service215,220,229, chỉ đọc) và đánh giá khả năng báo cáo cho từng chỉ số theo mục 2 của Tận dụng báo cáo. Kết quả khảo sát: App/Menu và chức năng có sẵn; mapping Object/field/option/quan hệ đã xác minh; Process liên quan; báo cáo có sẵn và khả năng báo cáo cho từng chỉ số; phần thiếu dự kiến; nguồn bằng chứng API/metadata, phạm vi phân trang và điểm chưa xác minh kèm lý do; không chứa credential. - Agent chính kiểm tra báo cáo, giao lại phần thiếu hoặc mâu thuẫn trước khi chốt quyết định phụ thuộc vào phần đó. Lập bảng: yêu cầu → khả năng chuẩn và bằng chứng trên Workspace → phần thiếu → nơi xử lý (cấu hình chuẩn/báo cáo/dashboard/frontend/backend/Process). Số liệu tổng hợp: ưu tiên saved report (có sẵn hoặc tạo mới); chỉ chọn quét record khi báo cáo không biểu diễn được, ghi lý do cụ thể; chỉ cần xem bảng/biểu đồ chuẩn thì dashboard qua
$dashboard-builderlà chức năng chuẩn. Chọn loại module theo phần thiếu thực tế: chức năng chuẩn đáp ứng trọn vẹn thì giải thích và dùng skill chuyên trách, không tạo project rỗng cho đủ bước; người dùng yêu cầu rõ trải nghiệm riêng thì vẫn thiết kế phần custom cần thiết. Có frontend: chọn dạng single page app, custom component hoặc Federation Page theo bảng chọn nhanh trong Các loại Custom Module và dạng frontend, dựa trên nơi hiển thị, mức tương tác với form, yêu cầu đồng nhất giao diện và stack sẵn có; hỏi phần chưa rõ thay vì suy đoán. - Chốt contract nghiệp vụ dự kiến: routes/methods, input/output, caller (thao tác chỉ dành cho Super Admin hoặc role nhất định: tên role, role ID đã xác minh trên Workspace qua
$user-permissionvà phản hồi khi bị từ chối), dữ liệu thay đổi và tiêu chí nghiệm thu. Record trigger (bước 3): từng trigger gồm Object,timing,operations,fields,changedFields/when/runWhen,writableFields, mã lỗi và message; ghi rõ quy tắc bắt buộc đặt ở before-change và việc chỉ xử lý tiếp đặt ở after-change. Trước khi chọn Object đặt trigger, liệt kê mọi đường làm đổi giá trị được kiểm soát, mỗi đường có trigger hoặc lý do bỏ qua: ghi trực tiếp record; tạo/sửa/xoá record con làm đổi rollup hoặc formula của record cha (tính lại rollup/formula là thao tác ghi bảo trì, không chạy trigger, nên quy tắc dựa trên tổng của record cha cần trigger trên Object con với đủcreate,update,delete; ví dụ hạn mức trên tổng tiền đơn hàng rollup từ dòng hàng cần before-change trên dòng hàng); chuyển trạng thái do người dùng, Process hoặc backend của App chuẩn ghi. Ưu tiên từ chối ở bước người dùng thao tác (ví dụ gửi duyệt) thay vì ở bước Process ghi: tài liệu không nêu Process xử lý thế nào khi before-change từ chối thao tác ghi của nó (chưa kiểm chứng; dự kiến node đó lỗi, không tự rẽ sang nhánh từ chối), nên vẫn chặn ở bước Process thì thiết kế nhánh xử lý và kiểm thử cách lỗi hiển thị ở bước 9. Background job (bước 3): từng job gồmkey, payload dự kiến,timeoutMs,maxAttempts,schedule(cron năm trường, múi giờ IANA) hoặc nơi enqueue (route/trigger/webhook/job khác), quy tắc idempotency key và điều kiện dừng khi tự enqueue. Custom Module Action (bước 3): từng action gồmkeyổn định,label,description(bắt buộc khi mở cho agent: viết cho AI Agent đọc, nêu khi nào dùng, input và kết quả),exposeTo,effect, input/output (property, kiểus.*, optional,describe, giới hạn),timeoutMs, Process/node hoặc agent/tool sẽ dùng, danh tính chạy dự kiến (Process: người khởi tạo, nhân sự chỉ định hay không có người dùng; agent: danh tính của agent và có cần kiểm tra theo người đang chatinitiatorPersonnelId), kết quả dự kiến trả trong output thay vì ném lỗi và việc dài chuyển sang job. Khởi chạy Process/AI Agent từ code: Process Normal Flow (Process IDPI...đã xác minh, biến input/output) hoặc agent ID, instruction/resultSchema,runAs,approvalPolicy, job nhận kết quả, quy tắcidempotencyKeyvà mụcprocesses/agentscần thêm vào identity policy. Tích hợp ngoài: tên và loại secret/credential (OPAQUE, BEARER, BASIC, HEADER), host được phép, nơi dùng trong code; giá trị do người dùng/quản trị viên nhập ở bước 6A. Mã hoá hoặc chữ ký với hệ thống ngoài: thao tác và bên giữ từng key (ai giữ private key, AES key dùng chung do ai cấp), thuật toán theo tài liệu bên kia (AES mode, cách truyền IV và tag nối hay tách,aad; RSA padding và digest MGF1; thuật toán chữ ký,derhayieee-p1363), encoding (base64,base64url,hex), chuỗi byte được ký, tên secret chứa key và mẫu ciphertext/chữ ký của bên kia để kiểm chứng tương thích. Inbound webhook: route dưới/hooks/, bên gửi, chế độ KEY hay HMAC (header chữ ký, prefix, timestamp header, dung sai, encoding theo tài liệu bên gửi), định danh sự kiện để chống trùng và job xử lý tiếp. Notification: sự kiện gửi, người nhận (personnel ID lấy từorg, field record hoặc danh sách cố định; có loại người gây ra execution không), notification channel (record ID củanotification_channelđã xác minh trên Workspace hoặc không dùng kênh), tiêu đề, nội dung, link tới record, người gửi hiển thị (workspace/actor), có gửi bản sao email không và quy tắcidempotencyKey. Email: hộp thư gửi (ID hộp thư dùng chung do quản trị viên cung cấp hoặc hộp thư cá nhân của actor) cùng grant cần thêm vào mụcemailcủa identity policy, người nhận (địa chỉ, personnel, field email của record), record ghi nhận timeline, file field đính kèm, chữ ký và quy tắcidempotencyKey. Cơ cấu tổ chức: câu hỏi cần trả lời (quản lý gần nhất hay cả chuỗi, thuộc phòng ban hay cả phòng con), phòng ban dùng làm gốc (phòng ban của record hay phòng ban chính của nhân sự), có bỏ nhân sự chưa có tài khoản (accountOnly), cách xử lý chuỗi rỗng,vacantDepartmentIds,truncated, và phòng ban/nhân sự test đã xác minh qua$user-permission. Số liệu tổng hợp: từng chỉ số →records.aggregate(Object,where,groupBy, metric, danh tính gọi), saved report có sẵn hoặc sẽ tạo (primary object, relation, group, aggregate, filter nền, filter theo phạm vi người gọi) hoặc quét record kèm lý do; đối tượng được xem báo cáo (role, phòng ban, vị trí) để đặt ACL; nơi gọi báo cáo: frontend bằng phiên người dùng cuối, hoặc phương án đã chọn khi backend cần số liệu để ra quyết định. Ngân sách: route, trigger hoặc job đọc/ghi nhiều record ước lượng capability call, lời gọi cục bộ, record đọc/ghi với khối lượng dữ liệu lớn nhất dự kiến so với ngân sách của loại lần thực thi; vượt hoặc sát ngân sách thì đổi thiết kế (records.aggregate, saved report, job chia trang tự enqueue) trước khi code, không trông vào retry. Hiệu năng (khi thuộc điều kiện của sub-agent Hiệu năng): khối lượng dữ liệu lớn nhất dự kiến và ngưỡng cho từng route/action/trigger/job/báo cáo/trang theo mục Ngưỡng; người dùng chưa nêu thì dùng ngưỡng đề xuất và xin xác nhận cùng 1A. Custom component: Object, layout, slug item Related List/field sẽ thao tác và expose key dự kiến. Federation Page: App/slug, danh sách route và mục menu sẽ gắn. Frontend: tiêu chí nghiệm thu dùng làm ca E2E, quy ước selector (role/label hoặcdata-testid), persona và nguồn phiên test (tài khoản người dùng cung cấp hay phiên tạo từ Workspace API key qua$cogover-api-auth). - Module ghi hoặc chặn ghi trên field mà backend của App chuẩn hay hệ thống khác cũng cập nhật (số tổng, số dư, bộ đếm, trạng thái): khảo sát và đo hành vi của backend đó trước khi chốt contract ở mục 5.
- Sub-agent khảo sát (chỉ đọc) lập bảng sự kiện → thay đổi cho từng field: sự kiện nào làm đổi, bên nào ghi và dưới danh tính nào, ghi delta theo giá trị đang lưu hay tính lại tuyệt đối, có kẹp về 0 không, field là rollup/formula hay giá trị lưu sẵn.
- Đọc transition rule và field bị khoá theo trạng thái của các Object liên quan; kiểm tra history tracking có bật không (không bật thì chỉ có giá trị hiện tại); đối chiếu bất biến số học trên dữ liệu hiện có; ghi rõ điểm nào là suy luận.
- Điểm chưa xác minh mà thiết kế phụ thuộc (backend có ghi đè giá trị module đã sửa không, giải phóng theo giá trị hiện tại hay tính lại, gán hay cộng dồn) đo bằng spike sau khi người dùng đồng ý phạm vi ghi: dữ liệu test có marker, snapshot record tổng hợp trước, mô phỏng bằng chính lệnh ghi module sẽ dùng, đọc lại ở vài mốc thời gian sau mỗi sự kiện, log máy đọc được, dọn và báo phần dư theo bước 9 mục 12.
- Chỉ chốt công thức, thời điểm module chạy và cách ghi khi bảng đã có bằng chứng; thiết kế ghi theo Ghi số liệu do backend khác duy trì.
- Tiêu chí nghiệm thu không giả định đường chuyển trạng thái hoặc field mà transition rule hay khoá field không cho phép; ca không đi được ghi
BLOCKEDkèm mã lỗi và cách kiểm chứng gián tiếp.
1A. Xác nhận loại module và dạng frontend với người dùng
Bắt buộc trước khi phát triển, kể cả khi yêu cầu ban đầu đã nói "tạo module" hoặc đã nêu sẵn một dạng.
- Agent chính gửi đề xuất gồm: loại module (frontend, backend hoặc cả hai); với frontend là dạng single page app, custom component hoặc Federation Page; lý do theo bảng chọn nhanh và bằng chứng khảo sát; dạng thay thế gần nhất và lý do không chọn; tác động của lựa chọn (template bắt buộc hay stack tự chọn, nơi hiển thị, cấu hình cần thêm trên layout hoặc menu, cách kiểm thử gồm E2E bằng sub-agent độc lập và nguồn phiên test, review bảo mật, đo hiệu năng cùng ngưỡng đề xuất khi thuộc điều kiện, giới hạn); nguồn số liệu tổng hợp (
records.aggregatetrong backend, saved report có sẵn, báo cáo sẽ tạo hoặc quét record kèm lý do), đối tượng được xem báo cáo và nơi gọi báo cáo (frontend bằng phiên người dùng cuối; backend cần số liệu để ra quyết định thì nêu giới hạn và phương án) khi có; câu hỏi còn mở. Người dùng đã chỉ định dạng: vẫn tóm tắt lựa chọn, nêu khác biệt một lần nếu phân tích cho thấy dạng khác phù hợp hơn, rồi theo quyết định của người dùng. - Chờ người dùng xác nhận rõ ràng trong hội thoại. Yêu cầu chung ban đầu, im lặng, đồng ý của sub-agent hoặc việc đã duyệt workbook không thay thế xác nhận này. Người dùng chọn phương án khác thì lập lại kế hoạch theo phương án đó trước khi tiếp tục.
- Trước khi có xác nhận chỉ làm: khảo sát bổ sung, contract, ma trận quyền, kế hoạch test và thiết kế workbook ở bước 2 mục 1–5 khi schema không phụ thuộc lựa chọn; có thể gửi yêu cầu xác nhận 1A cùng yêu cầu duyệt Excel nhưng mỗi mục phải được trả lời riêng. Không tạo Project, không clone template hoặc scaffold, không ghi schema, không tạo báo cáo, policy/key/Process, không viết code.
- Sau xác nhận: ghi loại module, dạng frontend, lý do và thời điểm xác nhận vào gói giao việc cho sub-agent và báo cáo bàn giao. Đổi dạng giữa chừng, ví dụ custom component sang Federation Page, phải xin xác nhận lại theo cùng cách kể cả khi đã có code.
2. Thiết kế dữ liệu và duyệt Excel
Agent chính xác định có cần bổ sung schema. Nếu cần, giao sub-agent thiết kế dữ liệu/Excel (vai chuyên gia thiết kế cơ sở dữ liệu) làm mục 1–4 với báo cáo khảo sát, mapping metadata, yêu cầu dữ liệu/quyền/truy vấn và phạm vi delta dự kiến; agent chính làm mục 5–7. Schema đã đủ thì ghi kết luận, bỏ qua workbook và tạo Object.
- Xác định thực thể, nguồn dữ liệu chuẩn, quan hệ một-nhiều/nhiều-nhiều, vòng đời, khóa nghiệp vụ, yêu cầu chống trùng, field bắt buộc, kiểu số/ngày/tiền, quyền và truy vấn chính. Tái sử dụng Object nền tảng và Object nghiệp vụ đã có; không tạo bản sao chỉ vì khác tên hiển thị.
- Phân loại: tái sử dụng nguyên trạng, bổ sung field/option/quan hệ hoặc tạo Object mới; chỉ thêm phần thiếu, ghi rõ lý do và tác động với dữ liệu/cấu hình đang dùng. Mỗi Object mới có ma trận Create/View/Edit/Delete: người dùng nào được thao tác trực tiếp, phạm vi record/field và action nào chỉ backend được thực hiện. Object do backend quản lý là bảng dữ liệu cho logic ứng dụng, có thể chặn toàn bộ hoặc một phần CRUD trực tiếp; không nhầm với Object nền tảng có sẵn.
- Schema đã đủ: sub-agent báo lại kết luận và lý do, không tạo workbook rỗng. Cần bổ sung: dùng $create-cogover-objects tạo workbook
.xlsxtheo hướng dẫn spreadsheet và validator của skill đó, sửa mọi lỗi trước khi bàn giao; phân biệt Object mới với delta trên Object hiện có, chỉ rõ lookup tới Object đã tồn tại để không tạo trùng. Bàn giao file.xlsxtại đường dẫn agent chính truy cập được, tóm tắt delta/lý do/tác động, ma trận quyền, mapping lookup, kết quả kiểm tra và các điểm còn cần quyết định; không tạo hoặc sửa schema trên Workspace. - Mỗi Object có đúng một record-name slug
name, type Short text hoặc Auto number; tiền dạng số dùng Decimal. Giữ slug/ID tham chiếu thật, không tạo lại field hệ thống. - Agent chính mở workbook, đối chiếu với yêu cầu, metadata đã khảo sát và bằng chứng kiểm tra; giao lại sub-agent thiết kế sửa nếu thiếu hoặc sai. Bắt buộc đưa file Excel đã rà soát cùng tóm tắt delta và ma trận quyền để người dùng duyệt trước khi ghi schema; yêu cầu chung “tạo module” không thay thế duyệt này. Trong lúc chờ chỉ hoàn thiện thiết kế và kế hoạch test, chưa tạo Object/field hoặc viết code.
- Sau khi workbook được duyệt: dùng
$object-infođọc lại state mới nhất, tạo Object/field theo dependency và đúng bản đã duyệt; quan hệ vòng thì tạo Object trước rồi thêm lookup sau khi có ID. Field chỉ backend ghi (bộ đếm, snapshot, trạng thái do backend cập nhật) tạo vớicreatable: 0vàmanual_modify_allow: falsetheo mục Cờ tạo và sửa thủ công của$object-info; backend ghi bằngdata.asSystem()hoặc trigger before-change gán quawritableFields(thiết lập nhập thủ công và quyền sửa field của bên ghi không áp dụng cho field do trigger sửa; field formula, auto number, rollup và field hệ thống vẫn không sửa được), không dùng security ruleexcludethay cho hai cờ này. Delta thay đổi do Workspace đã đổi thì chỉ xin duyệt lại phần thay đổi thực chất. - Đọc lại toàn bộ schema đã ghi, so với workbook; ghi mapping ID/slug/options dùng cho code và Process. Thao tác dở dang: liệt kê phần đã tạo, tra lại trước retry, không tự xóa để làm lại.
2A. Tạo security rules cho từng Object mới trước khi code
Bắt buộc khi bước 2 tạo Object mới; giao một sub-agent Security riêng. Chỉ tái sử dụng Object hoặc bổ sung field thì không tự tạo lại bộ rules hay đổi quyền ngoài phạm vi yêu cầu; đánh giá quyền hiện có và xử lý delta được yêu cầu bằng $user-permission.
- Agent chính giao mapping Object/field đã đọc lại, ma trận quyền đã chốt, phạm vi thao tác được phép và cách truy cập credential an toàn. Sub-agent đọc bản hiện tại của
$user-permission(mục “Object do backend quản lý và rule giữ chỗ”), Rule giữ chỗ không chọn nhân sự và$cogover-api-auth; đọc rules/Role liên quan trước khi ghi để tránh trùng khi retry và phát hiện quyền đã được cấp. Không tự mở rộng Role để làm test thành công. - Mỗi Object mới có bộ mặc định bốn rules riêng: một Create, một View, một Edit, một Delete, kể cả action không cho người dùng thực hiện trực tiếp; không gộp action, chỉ bổ sung rule cùng action khi audience, điều kiện record hoặc field scope khác nhau. Create dùng
type: 1với duy nhất scopecreatevà vẫn gửifilterrỗng theo$user-permission(server bắt buộc); ba action còn lại dùngtype: 2, chỉ cấp scope của action chính, các scope khácnone/no. Action chỉ backend được thực hiện: rule giữ chỗ active (status: 1) với đúngpersonnelFilters: [{"type": 1, "op": "include", "personnelId": null}]và scope hợp lệ của action. Audience này không khớp nhân sự nào: rule giữ slot action và duy trì mặc định từ chối nhưng không ghi đè quyền do active rule khác cấp, nên phải rà mọi active rule để chắc không rule nào cấp lại action đang muốn chặn. Không bỏ rule, thaynullbằng ID giả hoặc tắt rule giữ chỗ để biểu diễn chặn. - Sub-agent tạo rules trong phạm vi được phép; đọc lại danh sách không lọc trạng thái và detail của từng rule. Bàn giao mapping Object ID/slug → rule ID/action/type/status/audience/filter/scopes, đối chiếu ma trận quyền, kết quả kiểm thử trực tiếp theo
$user-permissionvà phần chưa kiểm thử. Request dở dang: đọc lại trước retry, không tạo bốn rule mới chồng lên bộ đã có. - Agent chính chờ sub-agent hoàn tất và nghiệm thu trước khi chuyển sang bước 7–8: mọi Object mới đủ bốn slot action active, rule giữ chỗ có
personnelIdlà JSONnull, phạm vi quyền khớp thiết kế, không có grant ngoài ý muốn từ rule khác và có bằng chứng đọc lại. Thiếu rule, cấu hình sai, thao tác lỗi hoặc sub-agent chưa hoàn tất thì chưa được code; không có công cụ sub-agent thì báo rõ dependency và dừng trước bước code. Kiểm tra cấu hình không thay thế kiểm thử runtime: thiếu credential/fixture thì ghi rõ giới hạn và theo dõi ca test còn thiếu. Ca backend dùng quyền hệ thống chạy sau khi backend sẵn sàng ở bước 8–9; không yêu cầu backend tồn tại để hoàn tất bước cấu hình này.
2B. Tạo hoặc tái sử dụng báo cáo cho số liệu tổng hợp
Bắt buộc khi contract ở bước 1 mục 5 có chỉ số lấy từ saved report và người dùng đã xác nhận 1A; giao một sub-agent Báo cáo riêng dùng $report-builder. Không có chỉ số tổng hợp thì bỏ qua.
- Chạy sau khi schema liên quan đã ghi và đọc lại ở bước 2, vì report field tham chiếu field thật. Tái sử dụng saved report khi cùng Report Type, field, group, aggregate và filter; không sửa saved report đang dùng chung theo cách làm đổi kết quả của người khác, cần cấu hình khác thì tạo mới.
- Sub-agent làm theo workflow của
$report-builder(report contract, graph, Report Type, report field, saved report, chạy service200và kiểm tra với ví dụ nghiệm thu); slug ổn định, mô tả nêu module sử dụng. Phân quyền theo Xây báo cáo và phân quyền: ACL của Report Type và saved report cho người dùng module xem/chạy theo role, phòng ban hoặc vị trí, chỉ người phụ trách được sửa/xoá, không cấp cho tất cả khi đối tượng hẹp hơn; ACL chưa rõ thì hỏi người dùng trước khi ghi. - Chạy service
200(setting: true) bằng phiên của persona trong ACL có phạm vi record hẹp và persona ngoài ACL: người trong ACL nhận đúng số liệu trong phạm vi quyền của họ, người ngoài ACL bị từ chối; kết quả khác thiết kế thì báo agent chính trước khi module dựa vào báo cáo. Đo thời gian chạy với dữ liệu đại diện; lưu payload đã chạy thành công và mẫu response đã redact làm fixture cho parser của module. - Người dùng module phải có quyền View Object/field nguồn; thiếu thì phối hợp sub-agent Security xử lý đúng delta được duyệt, không mở rộng quyền Object chỉ để báo cáo có số liệu.
- Agent chính nghiệm thu: Report Type ID, saved report ID/slug, mapping object field → report field ID, ACL đã đặt, payload, mẫu response, kết quả theo persona, thời gian chạy và hai link theo
$report-builder. Báo cáo không đáp ứng như đánh giá ở bước 1 thì chốt lại nguồn số liệu với người dùng trước khi chuyển sang quét record.
3. Chọn record trigger, background job, Custom Module Action hoặc Process khi cần chạy theo sự kiện, việc nền, lịch hoặc nối với Process/AI Agent
- Cần chạy theo sự kiện create/update/delete của record và quy tắc phải áp cho mọi nguồn ghi (giao diện web, API, import, Process, module khác): từ chối hoặc điều chỉnh giá trị trước khi lưu, hoặc xử lý tiếp ngay sau khi lưu (ghi record liên quan, gọi hệ thống ngoài) thì dùng record trigger của Custom Backend Module (
@cogover/sdktừ0.6.0): before-change cho quy tắc bắt buộc, after-change cho việc tiếp theo best-effort. Trigger là một phần của version backend, được áp dụng khi activate và không cần Process; thiết kế ở bước 1 mục 5, code/test ở bước 8, kiểm thử trên Workspace ở bước 9 theo Record trigger quick start. After-change gửi được notification/email cho người liên quan, ví dụ quản lý tìm bằngorg. Không đủ cho việc dài hơntimeoutMs, cần retry hay chạy theo lịch (background job bên dưới) và cho luồng nhiều bước có người tham gia, chờ phê duyệt bằng User Task, gọi AI Agent (Process). - Cần việc nền của backend: xử lý dài chia trang, gọi hệ thống ngoài có retry lỗi tạm thời, chạy trễ (
delayMs/runAttối đa 30 ngày) hoặc chạy theo lịch cron (schedulevới múi giờ IANA, mốc bị lỡ khi Project không active không chạy bù) thì dùng background job (@cogover/sdktừ0.8.0, Workspace đã bật job;JOBS_DISABLEDthì báo dependency): khai báodefineJob, liệt kê trong named exportjobs, enqueue từ route, trigger after-change, inbound webhook, job khác, CLI hoặc management API. Job là một phần của version backend: lịch có hiệu lực khi activate, activate version khác thay lịch, deactivate gỡ lịch. Run theo lịch và run enqueue từ webhook chạy danh tính system (policyallowInternalSystem: trueở bước 5); run enqueue từ route/trigger giữ danh tính người gọi/người sửa record; run do quản trị viên enqueue qua CLI hoặc management API chạy dưới danh tính Super Admin đứng sau Workspace API key hoặc phiên đã gọi (enqueuedBy: "management",actorPersonnelIdlà người đó). Job gửi được notification/email, ví dụ nhắc hạn hằng ngày. Không dùng job cho việc phải trả kết quả trong cùng HTTP response, cho luồng cần người tham gia (User Task)/AI Agent, hoặc để thay Process mà người dùng muốn tự cấu hình không cần code. Thiết kế ở bước 1 mục 5, code/test ở bước 8, kiểm thử trên Workspace ở bước 9 theo Background job quick start. - Process hoặc AI Agent cần gọi logic của module (tính toán, kiểm tra điều kiện, đọc/ghi dữ liệu theo quy tắc riêng) và dùng ngay kết quả có cấu trúc ở bước sau thì khai báo Custom Module Action (
@cogover/sdktừ0.15.0):defineActionliệt kê trong named exportactions, là một phần của version backend và được Process Builder/AI Agent Builder liệt kê khi version activate. Process dùng node Custom Module Action theo Custom Module Action Task (sub-agent Process); AI Agent dùng tool categoryCUSTOM_MODULEtheo mục CUSTOM_MODULE của$agent-builder(sub-agent AI Agent). Một action có thể mở cho cả hai (exposeTo). Action đồng bộ, tối đa 8 giây: việc dài hơn enqueue job. Process không có người khởi tạo (Scheduled, Triggered record/webhook) phải chọn danh tínhPERSONNELhoặcSYSTEMcho node (SYSTEMcầnallowInternalSystem, bước 5); Triggered webhook không lấy nhân sự chạy từ dữ liệu webhook. Action do AI Agent gọi chạy dưới danh tính của agent, có thể khác người đang chat. Không nối Process/AI Agent tới module bằng node Send HTTP Request hay HTTP tool. - Module cần khởi chạy Process (luồng có User Task, phê duyệt mà người dùng muốn tự cấu hình) hoặc AI Agent (phân loại, tóm tắt, soạn nội dung) từ route, trigger after-change, job hoặc action thì dùng
processes.start/agents.start(@cogover/sdktừ0.15.0): trả về ngay, kết quả giao cho background job khai báo trongonComplete/onResult. Process phải là Normal Flow đã kích hoạt và xuất bản, nhận dữ liệu qua biến được đánh dấu input và trả qua biến được đánh dấu output (thiết kế bằng sub-agent Process); agent cấu hình bằng sub-agent AI Agent. Identity policy cần mụcprocesses/agents(bước 5). Cần AI Agent chủ động nhắn chat cho một nhân sự (nhắc việc, báo điều agent phát hiện, người nhận trả lời tiếp với agent) thì thêmdeliverTovàoagents.start(@cogover/sdktừ0.16.0): agent tự soạn tin và tự quyết định có gửi hay không, tin tới AI chat của agent với người đó; nội dung cố định do code soạn sẵn thì dùngnotifications.send,email.sendhoặcpush.toast. - Các trường hợp còn lại cần Process: luồng nhiều bước có người tham gia, chờ phê duyệt, gọi AI Agent, hoặc automation/lịch/thông báo mà người dùng muốn tự cấu hình trên giao diện. Thông báo hoặc email gắn với logic của backend (người nhận tính từ cơ cấu tổ chức hay dữ liệu module, nội dung từ kết quả xử lý) gửi bằng
notifications.send/email.sendtrong backend; không gửi cùng một thông báo bằng cả backend và node Send Notification/Send Email của Process. Cần Process thì giao sub-agent Process riêng dùng $process-creator xác định event, conditions, timezone/lịch, chống lặp, output cần kiểm chứng và thiết kế Triggered Flow hoặc Scheduled Flow phù hợp. Custom Module xử lý trọn vẹn yêu cầu trong request hiện tại thì bỏ qua cả ba. Không dùng đồng thời record trigger và Triggered Flow cho cùng một quy tắc trên cùng Object, không dùng đồng thời job theo lịch và Scheduled Flow cho cùng một việc; ghi nơi xử lý vào bảng ở bước 1 mục 4. - Backend đăng ký trigger qua named export
triggers, action qua named exportactionsvà lịch quaschedulecủa job trong named exportjobs; không có cơ chế lịch nào khác. Không dùng timer trình duyệt hoặc tiến trình local thay cho lịch chạy trên Workspace; máy chủ local không chạy job hay cron. - Sub-agent gửi flow cụ thể để agent chính tổng hợp bước xác nhận của
$process-creator, tái sử dụng xác nhận đã có cho đúng flow. Đủ phê duyệt thì sub-agent tạo, đọc lại, kích hoạt và kiểm thử theo skill đó, trả Process ID/link, instance và bằng chứng hiệu ứng nghiệp vụ. - Process hoặc AI Agent phải gọi backend chưa publish: thiết kế ở bước này, tạo node/tool và kích hoạt sau khi version có action đã activate ở bước 9 (Process Builder và AI Agent Builder chỉ thấy action của version active; Process có node tham chiếu action chưa có được lưu nhưng không hợp lệ với mã
71và không kích hoạt được, tool bị từ chối vớir: 4047). Không kích hoạt flow có URL/ID giả hoặc dependency chưa sẵn sàng. - Process → backend chỉ qua node Custom Module Action (danh tính theo
runAscủa node); không dùng Project key (không gọi được production) hay session export ngắn hạn (không phải credential bền vững) trong node Send HTTP Request. Hệ thống ngoài cần gọi backend không có phiên người dùng thì dùng inbound webhook (bước 6A), không nhúng session export. Chưa có cơ chế được hỗ trợ thì báo chính xác dependency còn thiếu và hoàn thành các phần độc lập.
4. Tạo Project trên Workspace
- Đọc API reference đúng nhánh. Qua phiên Workspace với service
4, list đủ các trang cần thiết để kiểm tra trùng slug, rồi create hoặc tái sử dụng đúng project đã được chỉ định. CLI publish/activate không tạo Project. - Backend:
POST /api/v1/ts-projects. Frontend:POST /api/v1/ts-projects/frontend. Body gồmname,slug,descriptionkhi cần; không gửicreateGitRepository: server mặc định tạo kèm repository trên Git server của Workspace (fieldgit; tạo lỗi thì Project vẫn được tạo, cógit.error,git setupở bước 7 tạo bù). Slug bất biến theo[A-Za-z_][A-Za-z0-9_]{0,99}, không áp regex slug Process; ID do server cấp. - Đọc lại project vừa tạo. Ghi backend
projectId/slug; frontendprojectId/slug/slugSlot. Full-stack có hai project với cấu hình riêng. Ba dạng frontend dùng cùng loại Project và cùngslugSlot: single page app mở/{slugSlot}/index.html, custom component dùng{slugSlot}/Components/<Tên>trên layout, Federation Page dùng đoạnc{N}vớiNlấy từ_cm_{N}. Một project theo template có thể chứa đồng thời custom component và Federation Page; single page app là project riêng.
5. Cấu hình Project policy nếu cần
- Frontend thuần: bỏ qua, frontend không có identity-policy endpoint trong contract này. Backend dùng quyền caller mặc định, không gọi
data.asSystem()/data.asUser(), không gửi email, không khởi chạy Process/AI Agent và không có execution thiếu người dùng cần đọc dữ liệu hayorg(kể cả node Process chạy action với danh tínhSYSTEM): không tạo policy vô cớ. Notification không cần grant. Các API đặc biệt khác kiểm tra theo SDK contract; identity policy không mở được mọi capability. - Action bị chặn trực tiếp trên Object do backend quản lý (bước 2A): backend dùng
data.asSystem()với policy giới hạn đúng caller/Object/operation cần thiết. Code backend dùng quyền caller không tự vượt security rules; trước khi dùng quyền hệ thống phải kiểm tra người gọi có quyền thực hiện nghiệp vụ, policy không thay thế kiểm tra này. - Dùng quyền ủy quyền: đọc mục Identity policy trong Backend API Reference. Thiết kế schema version
2, giới hạn caller personnel, Object và operation cần thiết;data.asUsergiới hạn thêm personnel đích. Resolve ID/slug thật; khi sửa giữ grant ngoài phạm vi. - Gửi email (
email.send): thêm mụcemailtheo mục Người gửi email của Backend API Reference.workspaceMailboxIdschỉ gồm hộp thư dùng chung contract cần (tối đa 50, ID do quản trị viên cung cấp, không đoán),allowActorMailbox: truechỉ khi gửifrom: "actor"; mục phải cấp ít nhất một người gửi và policy có thể chỉ gồmemail. Grant chỉ áp dụng cho caller trongcallerPersonnelIds; job theo lịch, inbound webhook và trigger do hệ thống ghi cần thêmallowInternalSystem: truevà không dùng được"actor". Lưu policy không kiểm tra hộp thư tồn tại: hộp thư sai hoặc bị vô hiệu hoá chỉ lộ ra khi gửi (NotFoundErrorvớiresourcelà"mailbox"), nên sau khi duyệt xác minh bằngemail.senders()trước khi gửi thật. Khi sửa mụcemailgiữ nguyên grantdata.asSystem/data.asUserngoài phạm vi và ngược lại. - Khởi chạy Process/AI Agent (
processes.start,agents.start): thêm mụcprocessesvà/hoặcagentstheo mục Process và AI Agent của Backend API Reference.processes.processeslà selector (ALL,ALL_EXCEPT,ONLY) trênprocessInfoIdslà Process ID (PI...), không phải ID version hay lượt chạy;agents.agentstrênagentIds; ưu tiênONLYvới ID đã xác minh (tối đa 200 ID).allowSystem: truechỉ khi execution không có người dùng (job theo lịch, inbound webhook, nodeSYSTEM) cần khởi chạy; khi đó agent phảirunAs: "agent".agents.maxRunsPerDay(1–10.000, mặc định 200) là trầnagents.startmỗi ngày UTC của project;agents.allowAutoApprove(mặc địnhfalse) chỉ bật khi người dùng đồng ý cho agent chạy tool cần phê duyệt mà không hỏi, và không áp cho execution không có người dùng. Hai mục không phụ thuộccallerPersonnelIds; execution có người dùng vẫn bị Cogover kiểm tra quyền khởi chạy Process/chạy agent của chính người đó (PROCESS_START_DENIED,AGENT_RUN_DENIED).deliverTocủaagents.startkhông cần mục policy riêng và dùng được cả khi execution không có người dùng: Cogover kiểm tra người nhận là nhân sự của Workspace, được dùng agent và có seat AI Agent.processes.get/agents.getcũng cần mục tương ứng và chỉ đọc lượt chạy do project khởi tạo. Đổi một trong hai mục tạo revision mới, phải duyệt lại. - Custom Module Action: node Process chọn
SYSTEM(lời gọi không có người dùng) chỉ chạy khi identity policy đã duyệt của version active đặtallowInternalSystem: true, nếu không bị từ chốiSYSTEM_IDENTITY_NOT_ALLOWEDtrước khi chạy;allowInternalSystemáp cho cả Project (cả job theo lịch, inbound webhook), nên chỉ bật khi thực sự cần. Action chạy dưới danh tính người dùng dùng quyền của người đó (data.object()); version active chưa có policy được duyệt thì action chạy như route: lời gọi có người dùng không có grant nào, lời gọi không có người dùng bị từ chối. Action do AI Agent gọi chạy dưới danh tính của agent (nhân sự run-as của agent hoặc người chat với agent nội bộ): khi action chỉ được làm điều người dùng thật được phép, kiểm trainvocation.source.initiatorPersonnelIdtrong handler (ví dụdata.asUser(initiatorPersonnelId)với grantdata.asUsergiới hạn đúng Object/operation), từ chối khi thiếu;runAs: "PERSONNEL"của Process có thể lấy từ dữ liệu người dùng nhập, không phải bằng chứng ai đã yêu cầu. - Policy hợp lệ phải có ít nhất một trong
data.asSystem,data.asUser,email,fetch,processesvàagents; policy chỉ đặtallowInternalSystemmà không có grant nào là không hợp lệ. Execution không có người dùng chỉ đọcorghoặc chỉ gửi notification vẫn cần policy hợp lệ cóallowInternalSystem: true: dùng grant module thực sự cần, không thêm grant rộng chỉ để policy hợp lệ; không có grant nào cần thiết thì nêu với người dùng để chọn grant hẹp nhất hoặc chuyển việc sang execution có người dùng. - Lưu editable policy → đọc lại → activate policy cho nhu cầu phát triển. Editable policy khác snapshot bất biến đã duyệt cho từng version; mỗi lần sửa đưa editable policy về
DRAFT. - Sau publish có version
READY: approve policy cho đúng version rồi đọc snapshot, kiểm tra revision/hash/grants theo reference. Activate version không tự duyệt policy; policy đã approve trước đây không mặc nhiên áp dụng cho version mới. - Record trigger:
data.object()trong handler chạy dưới danh tính người tạo ra thay đổi với quyền của người đó, nên record người này không thấy sẽ nằm trongmissingIdscủagetMany; cấp quyền View cho Object tham chiếu hoặc dùngdata.asSystem()(before-change vẫn chỉ đọc) với policy đúng phạm vi. Thay đổi không có người dùng (invocation.identitylà"system") chỉ gọi đượcdata.object()khi identity policy đã duyệt đặtallowInternalSystem: true; nếu không, handler vẫn nhậnrecordsnhưng lời gọi dữ liệu vàorgnémPermissionDeniedErrorvới reasonIDENTITY_NOT_GRANTED. Xác định nguồn ghi thực tế của Object trước khi quyết định policy. - Background job và inbound webhook: run theo lịch, lời gọi inbound webhook (
invocation.identitylà"inbound",userlànull) và job được enqueue từ lời gọi đó đều thực hiệndata.object()và đọcorgdưới danh tính system, nên cần policy đã duyệt vớiallowInternalSystem: truevà grant giới hạn vào Object/thao tác thật sự dùng (gửi email thêm grant hộp thư); duyệt lại cho từng version mới trước khi activate, chỉ activate version chưa cấp quyền record. Run enqueue từ route hoặc trigger giữ danh tính người gọi/người sửa record; run do quản trị viên enqueue qua CLI/management API chạy như lời gọi module của chính Super Admin đó (data.object()theo quyền của họ, không cầnallowInternalSystem), nên run thử bằngcogover-dev jobs enqueuekhông chứng minh policy cho run theo lịch hoặc từ webhook;data.asUser()/data.asSystem()vẫn theo policy như thường. - Policy không cấp quyền chạy module nói chung và không thay thế quyền của user đích; permission ceiling của Project key chỉ thu hẹp quyền. Không thêm grant rộng để che lỗi
403.
6. Tạo Project key nếu backend local cần kết nối Workspace
- Frontend bỏ qua. Backend xử lý thuần túy có thể test bằng fixture không cần key; backend local cần gọi SDK capability thật hoặc chạy record trigger với bản ghi đã lưu thì cần key; thử handler after-change có ghi,
jobs.enqueue, push,notifications.send,email.send,processes.starthoặcagents.starttrên local cần key cho phép writes; chỉ đọcorg(vàemail.senders()) thì key chỉ đọc là đủ, khi đó chạy máy chủ local với--allow-writes=false(bắt buộc với key chỉ đọc). Development Session mặc định không đọc secret, không dùng credential và không dùng key{ secret }chocrypto(SECRETS_NOT_ALLOWED,FETCH_BLOCKED) trừ khi quản trị viên cho phép; máy chủ local không chạy job, lịch hay xác thực inbound, nên các phần này kiểm chứng trên version đã publish ở bước 9. - Đọc mục Project key; resolve caller personnel, hạn dùng và
permissionCeilingschema version1theo hành vi test. Cho phép writes/asSystem/asUser chỉ khi cần trong phạm vi bài toán. - Tạo key bằng
POST /api/v1/ts-projects/{projectId}/keys, hoặc tái sử dụng key còn hiệu lực đúng project/caller và quyền. Không rotate key đang được người khác dùng chỉ để lấy lại secret. projectKeychỉ trả một lần lúc create/rotate: chuyển trực tiếp vào credential store hoặc prompt ẩn của CLI qua kênh an toàn, không in raw response; lưu metadata key ID/caller/hạn dùng để kiểm chứng, không lưu secret vào tài liệu.cogover-dev login --profile <PROFILE>rồidoctortheo reference CLI. Không dùng Workspace API key thay Project key, hoặc ngược lại.
6A. Tạo secret, credential và inbound access nếu backend tích hợp hệ thống ngoài
Chỉ khi contract ở bước 1 mục 5 có tích hợp ngoài; các nhóm dưới đây là thao tác quản lý dùng Workspace API key/session với service 4 và quyền Workspace SuperAdmin (Project key không đủ; 403 hoặc SECRETS_DISABLED thì báo dependency, không tự nới quyền). Cách dùng trong code và giới hạn: Secrets và credentials quick start, Inbound webhook quick start và các mục Secret, Inbound access của Backend API Reference.
- Secret/credential: tạo theo đúng tên đã chốt (
[A-Za-z][A-Za-z0-9_]{0,63}, cùng tên dùng được giữa các môi trường) bằngcogover-dev secrets set <name> --kind opaque|bearer|basic|header [--header-name <Header>] [--allowed-host <host>]...(giá trị nhập qua prompt ẩn,--value-stdinhoặc--value-file; không truyền trên dòng lệnh, không in) hoặcPOST /api/v1/ts-projects/{projectId}/secrets. Credential (BEARER/BASIC/HEADER) bắt buộc danh sách host được phép không rỗng (host chính xác hoặc*.suffix); OPAQUE không có host và là loại duy nhất code đọc được. Secret thuộc Project, dùng ngay cho version active không cần publish; xoay bằngsecrets setcùng tên (giữ loại/host, tăngvalueVersion), không xoá rồi tạo lại. Agent chuẩn bị lệnh và xác minh bằngcogover-dev secrets listhoặcsecrets/list(chỉ metadata); người dùng/quản trị viên nhập giá trị thật. Dữ liệu thử nghiệm của quick start (httpbin.org, token demo) chỉ dùng trên Workspace test. Key chocryptolà secret OPAQUE: AES key 16/24/32 byte lưu dạng văn bản, base64 hoặc hex (code khai báoencodingtương ứng); RSA/EC key lưu văn bản PEM nhiều dòng (giá trị secret tối đa 8 KiB) qua--value-filerồi xoá file tạm, private key ở dạng PKCS #8 (hoặc PKCS #1, SEC1) chưa mã hoá, không nhậnENCRYPTED PRIVATE KEYhay key OpenSSH; RSA 2048–4096 bit, EC P-256/P-384/P-521; public key hoặc certificate của đối tác cũng nên lưu thành secret để xoay không cần publish. Cặp key do phía Cogover giữ được tạo ngoài Cogover bởi người dùng/quản trị viên; chỉ public key được gửi cho bên kia. - Inbound access: tạo sau khi version có route
/hooks/...đã activate ở bước 9, một access cho mỗi bên gửi,--route-prefixhẹp nhất có thể,--expires-at <ISO-8601>cho quyền tạm thời:cogover-dev inbound create <name> --mode key|hmac --route-prefix /hooks/<route> [--json]hoặcPOST /api/v1/ts-projects/{projectId}/inbound. Chế độ KEY:inboundKeychỉ trả về một lần khi create/rotate, chuyển cho bên gửi qua kênh an toàn, không in vào chat/log/báo cáo;rotatecấp key mới và vô hiệu key cũ ngay. Chế độ HMAC: secret dùng chung qua--hmac-secret-stdin, cấu hình--hmac-header,--hmac-prefix,--hmac-timestamp-header,--hmac-tolerance,--hmac-encodingkhớp cách ký của bên gửi (không timestamp thì chỉ ký raw body; có thì ký<timestamp>.<raw body>); đổi secret quaupdate, không rotate. Thu hồi bằngrevokelà vĩnh viễn và không đổi được chế độ. Ghi metadata (inbound ID, name, mode, route prefix, URL gốc kết thúc bằng/hooks/{inboundId}, hạn dùng) để kiểm thử ở bước 9 và bàn giao;inbound listchỉ trả metadata. - Credential chạy báo cáo từ backend: chỉ khi người dùng đã chọn phương án 3 của Giới hạn khi backend cần số liệu báo cáo (số liệu ở danh tính hệ thống): loại BEARER,
--allowed-hostchỉ là hostname Workspace, giá trị là Workspace API key của tài khoản tích hợp quyền tối thiểu do quản trị viên nhập qua prompt ẩn hoặc stdin; xoay bằngsecrets setcùng tên.
7. Khởi tạo local để build/test
- Có Object mới: chỉ giao khởi tạo sau khi agent chính đã nghiệm thu 2A và cung cấp mapping rules/quyền cho sub-agent backend/frontend.
- Backend (sub-agent backend, tiếp tục code/test ở bước 8): theo Backend quick start với starter public được liên kết;
cogover.jsoncó Workspace origin, Project ID/slug thật vàprojectType: "backend".src/main.tslà entrypoint: default export HTTP handler, named exporttriggerskhi có record trigger, named exportjobskhi có background job và named exportactionskhi có Custom Module Action (default export là tuỳ chọn với Project chỉ khai báo trigger/job/action, khi đó mọi URI gọi module trả404);local/chỉ là local runner (HTTP runner,trigger-runner.tsvà test chạy bằngnpm test), không import từsrc/và không upload. Project có trigger:npm ls @cogover/sdkphải từ0.6.0vàcogover-dev --versiontừ0.10.0; Project có job, secret/credential, crypto hoặc inbound webhook: SDK từ0.8.0và CLI từ0.13.1; dùngaesEncrypt/aesDecrypt,rsaEncrypt/rsaDecrypthoặcsign/verify: SDK từ0.9.0; đọcmembership.isSuperAdmin/roles: SDK từ0.10.0và CLI từ0.14.0khi chạy local (CLI cũ hơn không truyền hai giá trị này nên script local thấyisSuperAdmin: false,roles: []); dùngorg: SDK từ0.11.0(bản cũ hơn build báoorgkhông tồn tại) và CLI từ0.14.0; dùngnotifications/email: SDK từ0.12.0và CLI từ0.15.0; SDK từ0.13.0(bắt buộcfieldskhi đọc record, cóexpandLookupsvàrecords.aggregate) cần TypeScript từ5.0(npx tsc --version); nâng project cũ lên0.13.0thì sinh lạiworkspace.d.tsbằng generator của bản mới, thêmfieldsvào mọi lệnh đọc và sửa code dựa vào tên lookup hoặccreatedBy.name; dùnglimits/ctx.limits: SDK từ0.14.0; chạy local bị áp ngân sách của lần thực thi cần CLI từ0.19.0với SDK từ0.14.0(project SDK cũ hơn không bị áp ở local nhưng vẫn bị áp trên Cogover); dùngdefineAction/s,processeshoặcagents: SDK từ0.15.0(bản cũ hơn build báo không có export) và CLI từ0.20.0khi chạyprocesses.start/agents.starttrên local (bản này xếp hai lời gọi là thao tác ghi và áp trần 20/5 mỗi lần gọi như trên Cogover); dùngdeliverTocủaagents.starthoặc đọcdelivery: SDK từ0.16.0(bản cũ hơn không nhận tuỳ chọn này: typecheck báo lỗi,startnémValidationError). - Frontend single page app (sub-agent frontend, tiếp tục code/test ở bước 8): theo Frontend quick start với Vite TypeScript hoặc stack phù hợp yêu cầu;
base: "./", build radist/index.html, hash routing nếu có SPA.cogover.jsonphải cóprojectType: "frontend"(bỏ qua sẽ mặc định backend). - Frontend custom component hoặc Federation Page (sub-agent frontend, tiếp tục code/test ở bước 8): trước khi code, clone
https://github.com/cogover/custom-frontend-module-templatevào thư mục riêng của project, chạynpm ci, đọcREADME.vi.mdvà toàn bộ.agents/skills/*/SKILL.mdtrong bản đã clone (custom-module-foundationbắt buộc;custom-module-form-buildercùngsrc/types/form-builder.d.tscho custom component;custom-module-apikhi gọi API;custom-module-i18nkhi có đa ngôn ngữ) và báo lại agent chính các quy tắc ảnh hưởng contract. Không tự dựng project React khác, không sao chép rời rạc vài file của template. Giữ nguyên'./CustomApp': './src/App.tsx'trongexposes,base: './', route tương đối trongsrc/routes.tsx,Linknội bộ, asset trongsrc/assets; không hardcode_cm_N; không đưa key/secret vào biếnVITE_*..env.localchỉ chứaVITE_WORKSPACE_NAME(vàVITE_ENVIRONMENTkhi suffix domain khác mặc định củavite.config.ts), không commit.cogover.jsonđặt tại root template vớiprojectType: "frontend". Template chưa có unit-test runner: bổ sung runner phù hợp (ví dụ Vitest cùng Testing Library) đạt điều kiện coverage của bước 8;npm run lintvànpm run build(typecheck và Vite build) phải pass. - Mỗi sub-agent cấu hình unit-test runner và coverage phù hợp stack ngay khi khởi tạo: lệnh tái chạy được (ví dụ
npm run test:unit:coverage), ngưỡng line coverage >= 70% cho riêng project đó, exit code khác 0 khi có unit test thất bại hoặc coverage dưới ngưỡng. Backend theo starter: test nghiệp vụ đặt ngoàisrc/(publish đóng góisrc/) và ngoàilocal/(bộ công cụ local), ví dụtest/có trongincludecủatsconfig.json; sửa scripttestcủa starter đểnpm testchạy cả test nghiệp vụ; script coverage đo trênsrc/**/*.tstrừsrc/workspace.d.ts, ví dụ dùngnode --test --experimental-test-coveragevới--test-coverage-include/--test-coverage-exclude/--test-coverage-lines=70khi bản Node.js hỗ trợ (kiểm tranode --help), nếu không dùng công cụ tương đương;coverage/nằm trong.gitignore. Fake context của test ném lỗi khi code dùng danh tính ngoài thiết kế (ví dụdata.object()khi mọi lệnh phải quadata.asSystem()) và ghi lại lời gọi để test khẳng định số lời gọi, khoá và state. - Kiểm tra Node.js >= 20, CLI version/help và dependency thực tế;
auth sessioncần CLI >= 0.9.0;cogover-dev git ...cần CLI >= 0.21.0, Git và credential store của hệ điều hành (npm từ 12 cài CLI kèm--allow-scripts=keytar). Dùng Workspace origin đã xác định, không để CLI rơi về runtime mặc định. - Git mặc định cho mọi Project, trừ khi người dùng không muốn: khởi tạo xong thì
cogover-dev git setup, commit và push bản đầu; sau đó commit, push mỗi tính năng hoặc lần sửa lỗi đã qua test local và trước mỗi lần publish. Không lưu được thì báo một lần rồi làm tiếp, không chặn việc chính. Quy tắc: Lưu source trên Git server của Workspace. - Full-stack: hai thư mục/cấu hình độc lập, mỗi thư mục là một Project và một repository; proxy frontend local tới backend loopback theo Full-stack integration.
- Bố cục repository: mọi file của dự án nằm trong thư mục của một Project; thư mục cha chỉ chứa các thư mục Project, file đặt ở đó không thuộc repository nào và không được lưu. Tài liệu riêng một phía ở
docs/của repository đó, tài liệu chung ởdocs/của repository backend; ảnh dùng trong giao diện ở repository frontend (src/assets/với template), ảnh minh hoạ tài liệu đi cùng tài liệu (ví dụdocs/images/); E2E gọi API ởe2e/của repository backend, E2E trình duyệt ởe2e/của repository frontend; file phiên, artifact và log của lần chạy test vào.gitignore. Bảng đầy đủ, lệnh chạy E2E tách khỏi unit test và người commite2e/: Bố cục repository. - E2E frontend (sub-agent E2E, tiếp tục ở bước 8–9): thư mục
e2e/trong repository frontend, tách khỏisrc/, runner trình duyệt (khuyến nghị Playwright) với lệnh tái chạy được và exit code khác 0 khi có test fail, cấu hình qua biến môi trường không chứa secret, nguồn phiên test theo Xác thực trình duyệt test; script E2E được commit cùng repository frontend, file phiên, trace và báo cáo test nằm trong.gitignore,e2e/không vào ZIP publish.
8. Code và test local
Sub-agent backend và frontend làm phần của mình, viết unit tests cùng lúc code và tự sửa lỗi tới khi đạt điều kiện bên dưới; agent chính phối hợp kiểm thử xuyên hai project.
- Code theo contract nghiệp vụ và schema đã xác minh. Backend:
- Dùng public
@cogover/sdk, chỉ import package được hỗ trợ và local source; không đưa Node built-in, local runner, dynamic import hoặcrequire()vào code publish. Đọc SDK usage guide rồi SDK API reference cho router, data, identity và API tích hợp thực sự sử dụng; TypeScript config theo mục khuyến nghị trong usage guide. API chưa quen: tra bảng README của cogover-sdk-samples rồi đọc file sample tương ứng; sample là minh hoạ cách gọi, không thay contract nghiệp vụ. - Sinh
workspace.d.tstừ metadata Workspace bằngnpx cogover-generate-workspace-typestheo mục Khai báo Workspace. Đầu vàoobjects.jsonlà response nguyên trạng củaPOST /bapi/v1/objects/listtheo$object-info, vớislugsgồm các Object code dùng (thêm Object đích của lookup khi cần kiểu đầy đủ) vàincludeFields: true(includeOptions,includeMetaDatamặc địnhtrue); lệnh mẫu ở mục Sinh kiểu cho Workspace, nhưng agent đọc Workspace API key theo$cogover-api-auththay vì nạp.envvào shell; không commitobjects.json. Sinh lại sau mỗi thay đổi schema và sau khi nâng lên SDK0.13.0: file sinh bởi bản cũ khôngimport typeRecordReference/FileValue/UrlValue, nên lỗiCannot find name 'RecordReference'hoặc, vớiskipLibCheck: true, field lookup/file/URL âm thầm thànhanyvà mất kiểm trafields,expandLookups,aggregate. SDK Schema API chỉ đọc; tạo/sửa schema vẫn qua$object-info. - HTTP ngoài: ưu tiên import
fetchtừ SDK để local không âm thầm dùng native fetch. Project state không dùng lưu secret. Cần checkpoint hoặc concurrency thì đọc mục state/locks; lock không tạo transaction hay exactly-once. Job ghi field do backend khác duy trì: lấy khoá rồi mới đọc mọi input dùng để tính, ghi delta, lưu nhật ký state cho từng bước ghi và có đối soát theo Ghi số liệu do backend khác duy trì. - Dùng caller identity mặc định, kiểm tra input, chỉ đọc field cần thiết, xử lý kết quả thiếu/null, phân trang và lỗi từng phần.
- Đọc record (SDK từ
0.13.0, mẫu đầy đủ trong Đọc record hiệu quả): mọirecords.get/getMany/listtruyềnfieldslà danh sách slug tối thiểu code dùng, khai báo một lần thành hằng sốas constcạnh hàm dùng;"*"chỉ khi thực sự cần mọi field được đọc (ví dụ sao chép record), không dùng để tránh liệt kê. Không truyềnfieldsthì SDK némValidationErrortrước khi gửi.expandLookupschỉ bật khi cần tên hoặc field của record đích, chỉ cho đúng lookup đó và liệt kê field đích ({ customer: ["credit_limit"] }) thay vìtrue/"*"; mỗi Object đích là thêm lượt đọc, record đích không đọc được thì không được mở rộng (lookupname: "",referencegiữ tên đã lưu) và không cófields. Không mở rộng thì không dựa vàonamecủa lookup haysystem.createdBy.name(đều"", trừ fieldreference); chỉ cần ID thì đọcid; tên hiển thị của người tạo lấy bằngorg.personnel.getvớiwithDisplay: truevìcreatedBy.namekhi mở rộng có thể là mã sinh tự động. Số liệu đơn giản trên một Object dùngrecords.aggregatethay vìlistnhiều trang rồi cộng; chỉgroupByfield choice, boolean,lookup_normal, ngày, số; xử lý metricnull(avg/min/maxkhi không có giá trị) vàtruncated. Trong trigger cho thay đổi do người dùng và job có người dùng,data.object()có giới hạn củadata.asUser()(không group, chỉcounttrên"id"và metric trên field số): cần nhóm thì gọi quadata.asSystem()với grant tối thiểu và tự giới hạnwheretheo phạm vi nghiệp vụ. Không tinpersonnelIddo client gửi để tự nâng quyền. Thông báo API trả client dùng tiếng Anh. - Số liệu tổng hợp đã chốt lấy từ báo cáo: không quét record để tính lại. Frontend gọi
/api/v1/report(x-req-type: 9) service200vớisetting: truebằng phiên người dùng cuối theo Gọi báo cáo bằng phiên người dùng cuối: payload là hằng số dựng từ bước 2B, tham số người dùng chọn chỉ vàofiltersđã xác nhận, kiểm trarở root, gom nhiều chỉ số cùng Report Type vào một lần chạy, để báo cáo group/aggregate và giao diện chỉ biến đổi nhẹ. Backend không nhận số liệu báo cáo do client gửi làm căn cứ cho quyết định cần bảo vệ; khi cần thì theo phương án người dùng đã chọn ở mục Giới hạn của reference. Tách hàm biến đổi/parser thành hàm thuần, unit test bằng mẫu response đã redact. - Ngân sách của một lần thực thi (
limits; SDK từ0.14.0, mục Giới hạn của một lần thực thi): code cho khối lượng dữ liệu lớn nhất dự kiến, không cho fixture. Số tổng dùngrecords.aggregatehoặc saved report; đọc bằnggetManyhoặclistvớifieldstối thiểu, khônggettrong vòng lặp;listgiữ chỗ bằnglimitnên đặtlimittheo số record thực cần. Việc có thể vượt ngân sách route hoặc trigger chạy trong background job chia trang: sau mỗi trang đọclimits.usage()(recordsRead.remaining,capabilityCalls.remaining,elapsedMsso vớitimeLimitMs) vàjobs.enqueuetrang kế với cursor trước khi hết, theo ví dụ ở mục Đọc giới hạn trong code.RateLimitErrorcódetails.budgetnghĩa là lần thực thi đã hết ngân sách đó: không chờ, không bắt rồi gọi tiếp (sau 1.000 lời gọi bị từ chối lần thực thi bị kết thúc, script không bắt được), không chuyển thànhRetryableErrortrong job hay after-change vì lần chạy lại gặp đúng ngân sách đó; sửa thiết kế. Quota riêng của lần thực thi (20fetch, 20 thao tác secret, 50 enqueue) cũng không hết khi chạy lại; chỉ HTTP429RATE_LIMITEDkhông códetails.budgetdo Workspace có quá nhiều lần thực thi đang chờ (bị từ chối trước khi chạy) và hạn mức theo giờ của notification/email mới thử lại sau được. Bắt lỗi SDK để trả response riêng thì giữcodevàdetailsan toàn (budget,limit,used,requested) trong response lỗi và log, không thay bằng thông báo chung làm mất dấu hiệu ngân sách.limits.usage()némCogoverApiErrorCAPABILITY_UNAVAILABLEkhi gọi ngoài handler (lúc nạp module, unit test chạy ngoài Cogover): truyền kết quảusage()vào hàm thuần để unit test quyết định bàn giao sang job. - Ghi nhiều và gọi song song (mẫu đầy đủ trong Ghi record hiệu quả): giảm số call trước, song song sau. Đọc nhiều record bằng một
getMany, tạo/sửa nhiều record bằng mộtbatchInsert/batchUpdate(1–200 dòng, ánh xạresultstheoreferenceId), khôngcreate/updatetrong vòng lặp. Các call không phụ thuộc nhau bắt đầu cùng lúc bằngPromise.allđể chạy song song (tối đa một số call đồng thời do nền tảng đặt, vẫn tính ngân sách như cũ); call cần kết quả của call trước thìawaittheo thứ tự;awaitthao tác ghi xong rồi mớipush.refreshRecords; không dùng cùngidempotencyKeycho hai call trong mộtPromise.all; call khôngawaitvẫn chạy xong trước khi lời gọi kết thúc nhưng lỗi của nó không được xử lý, nên luônawait. Dev CLI chạy call lần lượt: local chậm hơn, kết quả như nhau. - Lời gọi đồng thời: trên Cogover nhiều lời gọi của Workspace chạy cùng lúc (route, webhook, trigger after-change, job), local thì lần lượt. Chuỗi đọc → sửa → ghi cùng record/state key giữ
locks.withLockhoặc dùngstate.setvớiexpectedVersion; module chưa an toàn khi chạy song song thì tạm đặtmaxConcurrentRuns: 1. Frontend hoặc hệ thống gọi module xử lý HTTP429RATE_LIMITEDkhông códetails.budget(Workspace bận, chưa chạy gì) bằng cách chờRetry-Aftergiây rồi gửi lại có giới hạn, kèmIdempotency-Keycho thao tác ghi; việc hàng loạt do người dùng bấm thì chuyển sang background job thay vì gửi nhiều request song song. Chi tiết: Lời gọi đồng thời của cùng module. - Quy tắc theo Super Admin/role (SDK từ
0.10.0): chỉ đọcinvocation.user.membership.isSuperAdminvàrolestừ context, không nhận role hay cờ quản trị do client gửi. So theo roleidđã xác minh trên Workspace đích (không theo tên), đặt ID thành hằng số hoặc cấu hình có tên rõ ràng;invocation.userlànullvới danh tínhsystem/inboundnên xử lý rõ nhánh này (từ chối hoặc bỏ qua theo contract) thay vì truy cập thẳng. Tách hàm kiểm tra thuần để unit test các ca Super Admin, có role, không role vàuser: null; route từ chối trả403kèm message tiếng Anh. Đây là quy tắc nghiệp vụ bổ sung: quyền record/field vẫn do security rules theo danh tính người gọi (Super Admin không tự bỏ qua Object Security Rules); khi cờ/role mở đường chodata.asSystem(), kiểm tra trước lệnh ghi đầu tiên và giữ grant của policy giới hạn đúng thao tác. Chạy local: các giá trị được lấy lúc development session bắt đầu, đổi role của user test thì khởi động lạicogover-dev. - Record trigger: khai báo bằng
defineTriggertrong module riêng và liệt kê đủ trong named exporttriggers; tách quy tắc nghiệp vụ thành hàm thuần để unit test.keyổn định giữa các version;fieldschỉ gồm field handler đọc;changedFields,whenvàrunWhen: "onEnter"thu hẹp lần chạy và ngăn trigger after-change tự kích hoạt lại;writableFieldschỉ với before-change và đúng field handler gán. Handler nhận cả lô tới 200 record: gom ID, đọc một lần bằngrecords.getManyvớifieldstối thiểu, ghi một lần bằng batch; lookup trongrecord.new/record.oldđã có tên, không cần đọc lại chỉ để lấy tên; không đọc/ghi trong vòng lặp. Before-change chỉ đọc (ghi record,fetch, lock, state, push, notification, email,jobs.enqueue,secrets.get,processes,agentsvà mọi thao táccryptobằng key{ secret }bị từ chối với reasonTRIGGER_READ_ONLY; đọcorg, hash, random và thao táccryptovới key truyền trực tiếp, ví dụverifybằng public key, vẫn dùng được), fail-closed và phải xong trongtimeoutMs(mặc định 2 giây, tổng 8 giây cho mọi trigger before-change của một thao tác ghi): đặt quy tắc bắt buộc ở đây, từ chối bằngrecord.addError(field hoặc null, CODE, message)với code viết hoa, message tiếng Anh. After-change best-effort, có thể chạy lặp hoặc không chạy: idempotent theotrigger.changeIdcùngrecord.id, némRetryableErrorchỉ cho lỗi tạm thời, không sửarecord.new, sửa record bằngrecords.update/batch (thao tác ghi mới, chạy lại trigger trong ngân sách 10 bước) rồipush.refreshRecordscho các record đã sửa để người đang mở, kể cả người vừa lưu, tải lại; việc dài hơntimeoutMshoặc cần state/locks thìjobs.enqueuevới idempotency key theochangeId. Trigger hoặc job phản ứng với thay đổi của record con không lấy rollup/formula của record cha làm căn cứ: trong before-change giá trị đó chưa gồm thay đổi đang lưu, còn thời điểm tính lại sau khi lưu tài liệu không nêu (đã quan sát có độ trễ, xemrollup_summarycủa$object-info); tính từ record nguồn (before-change: tổng đã lưu của record cha trừ giá trị cũ cộng giá trị mới của record con; job: list record con theo record cha). Before-change đọc nhiều Object thì đọc theo tầng và thoát sớm: đọc trước dữ liệu quyết định có cần kiểm tra (trạng thái, liên kết), bỏ qua ngay khi không thể vi phạm, chỉ đọc record tổng hoặc cấu hình khi thay đổi làm tăng giá trị được kiểm soát; capability call chạy lần lượt nên chuỗi đọc dài có thể hếttimeoutMsvà fail-closed chặn mọi thao tác ghi liên quan (suy luận từ contract, chưa đo). Unit test khẳng định số lời gọi đọc của ca không cần kiểm tra và của lô lớn nhất dự kiến. - Push message (
push.refreshRecords,push.toast,push.message; SDK từ0.7.0): có trong script, route, trigger after-change và Development Session ghi được; cùng cơ chế với action Push Message của Workflow nên trang record chuẩn tự xử lýrefreshRecordsvàtoast. Người nhận mặc định"viewers"(người đang mở record đích, bắt buộcobjectvàrecordIds), hoặc danh sách personnel ID đích danh;exclude: ["actor"]bỏ người gây ra execution. Gửi là best-effort, không thu hồi được và có thể gửi lặp khi execution chạy lại; mỗi lời gọi tốn một capability call; tối đa 200 record ID và 200 personnel ID; ID/slug không tồn tại némNotFoundError, deployment tắt push némPUSH_DISABLED. Không đưa secret hoặc dữ liệu người dùng không được xem vào toast/message;linkscủa toast phải là URL tuyệt đốihttp(s). Frontend nhận push bằng@cogover/client-sdk(từ0.3.0): custom component và Federation Page dùnguseSocketListener, single page app dùngcreateSocketListenertừ@cogover/client-sdk/socket, theo mục Nhận push message trong Dùng@cogover/client-sdkở frontend; không tự mở WebSocket hay suy đoán API ngoài tài liệu đó và README của gói. - Cơ cấu tổ chức (
org; SDK từ0.11.0): chỉ đọc, có trong script, route, trigger (cả before-change), job và Development Session. Lấy người gọi bằngorg.me()hoặcinvocation.user.membership.personnelId, không nhậnpersonnelIdngười duyệt hay cấp quản lý do client gửi; ID nhận từ request thì kiểm tra định dạng (ví dụ^[A-Za-z0-9_-]{1,64}$) trước khi gọi. DùngisManagerOf(managerId, personnelId, { departmentId })cho kiểm tra quyền duyệt và truyền phòng ban của record khi có (không códepartmentIdthì mọi phòng ban của người tạo đều được xét, nên người quản lý họ ở phòng ban khác cũng nhậntrue);directOnly: truekhi chỉ quản lý trực tiếp được duyệt. Chọn người duyệt bằngpersonnel.managerChain(id, { departmentId, accountOnly: true }):tiers[0]là quản lý gần nhất, nhiều người cùng cấp chung một tier, mỗi người chỉ xuất hiện ở tier gần nhất; xử lý rõ chuỗi rỗng (người đứng đầu, nhân sự không thuộc phòng ban nào hoặc không tồn tại),vacantDepartmentIdsvàtruncatedtheo contract thay vì mặc định cho qua.levelcủa membership là cấp quản lý (0nhân viên,1cao nhất),levelcủa phòng ban là độ sâu cây (1là gốc). Chỉ thêmwithDisplay: truekhi cần tên, email, avatar (thêm tối đa một lần đọc cho mỗi loại dữ liệu và mỗi 200 ID, tính capability call thay vì lời gọi cục bộ; giá trị hiển thị có thể lànull); tên theo ngôn ngữ người gọi trừ khi truyềnlanguage. Đọc nhiều người bằnggetManyhoặc một trangmembers(mặc định 500, tối đa 2.000, tối đa 200 khi cówithDisplay; phân trang bằngnextCursor), không gọigettrong vòng lặp; mọi lời gọi trong một execution đọc cùng một phiên bản cơ cấu. Field nhân sự khác (số điện thoại, custom field) đọc quadata.object("personnel")với quyền record. Lỗi:gettrảnull,personnel.managerChaintrả mảng rỗng,isManagerOf/isInDepartmenttrảfalsevới ID không tồn tại;members,managers,managerChaincủa phòng ban,ancestors,tree({ rootId })némNotFoundError(chuyển thành404);ORGANIZATION_UNAVAILABLElà lỗi tạm thời (HTTP503nếu không bắt, némRetryableErrortrong job/after-change khi cần thử lại). Tách logic chọn người duyệt thành hàm thuần nhận kết quảorgđể unit test các ca nhân viên, phó phòng, trưởng phòng, nhiều phòng ban, chuỗi rỗng và phòng trống. - Notification (
notifications.send; SDK từ0.12.0): có trong script, route, trigger after-change, job, lời gọi inbound webhook và Development Session ghi được.tolà 1–200 personnel ID (chia lô khi nhiều hơn),exclude: ["actor"]khi không báo cho chính người gây ra execution; kiểm tranotifiedvàskippedPersonnelIds(personnel chưa có tài khoản) trong kết quả thay vì coi mọi người nhận đều đã được báo.channellà record IDnotification_channelđã xác minh, đặt thành hằng số hoặc cấu hình có tên rõ ràng; không truyềnchannelthì mọi hình thức gửi đều bật;email: falsekhi không muốn bản sao email.categories(SDK từ0.17.0) chỉ nhóm notification trong danh sách để người nhận lọc, không đổi cách gửi nhưchannel: truyềncodecủanotification_category(ví dụprocess,sales,system; ổn định giữa các Workspace, khác channel là record ID riêng từng Workspace), tối đa 10, mỗi phần tử khớp^[A-Za-z0-9_-]{1,64}$; Cogover không báo lỗi khi danh mục không tồn tại hoặc không hoạt động mà bỏ qua, không còn danh mục hợp lệ thì vàosystem; không truyền thì Workspace tự gán theo loại notification.title≤ 200 ký tự,content≤ 16.384 ký tự;contentType: "html"chỉ giữ định dạng cơ bản, escape dữ liệu người dùng trước khi ghép HTML;subtitlecầndisplay: "FULL"(mặc định khi có subtitle);link.urllà path web app bắt đầu bằng/hoặc URLhttp(s)tuyệt đối;sender: "actor"némValidationErrorkhi execution không có người dùng. Không đưa secret hoặc dữ liệu người nhận không được xem vào nội dung. - Email (
email.send,email.senders; SDK từ0.12.0): có trong script, route, trigger after-change, job, lời gọi inbound webhook và Development Session. Chọn hộp thư theo địa chỉ từemail.senders()hoặc ID cấu hình có tên rõ ràng, không nhận mailbox ID từ client;from: "actor"chỉ khi có người dùng và policy cho phép. Người nhận là địa chỉ,{ email, name }hoặc{ personnelId }(tổng tối đa 50 trongto/cc/bcc,tobắt buộc trừ khi córecordEmailFields); cung cấp đúng một tronghtmlvàtext(tối đa 192 KB),subjectmột dòng ≤ 255 ký tự; escape dữ liệu người dùng trong HTML. Cần email trên timeline record (theo dõi lượt mở/bấm, gom thư trả lời) thì truyềnrecord;recordEmailFields(≤ 20 field email),attachments(file field của record, ≤ 10 file, tổng 20 MB) vàappendSignature(chỉ với"actor") cầnrecord, và record phải đọc được bằng danh tính mặc định. Không córecordthìdelivery: "direct". Lời gọi xong khi Cogover tiếp nhận: lưu trạng thái "đã tiếp nhận để gửi" cùngrequestId, không coi là đã tới hộp thư. Lỗi riêng:EMAIL_SENDER_NOT_GRANTED(grant ở bước 5),NotFoundErrormailbox/personnel/record,ValidationError. - Chung cho notification và email: mỗi lời gọi là một capability call, không thu hồi được; việc gửi lặp khi execution chạy lại chỉ tránh được bằng
idempotencyKey(chữ, số,.,_,:,-, tối đa 128 ký tự, giữ 7 ngày, key của notification và email tách biệt): trigger after-change dùng${trigger.changeId}:${record.id}, job dùngjob.idkèm ID nghiệp vụ, route dùng khoá nghiệp vụ để bấm lặp không gửi hai lần;duplicate: truelà kết quả hợp lệ.DUPLICATE_IN_PROGRESSnghĩa là lời gọi cùng key đang gửi: thử lại sau giây lát hoặc némRetryableErrortrong job. Hạn mức mặc định mỗi project 5.000 người nhận notification và 500 email mỗi giờ (RateLimitError, không gửi gì): chia đợt lớn qua nhiều run job.NOTIFICATIONS_DISABLED/EMAIL_DISABLEDlà dependency của deployment, báo lại thay vì sửa code. Gửi sau khi các thao tác ghi record liên quan đã thành công, không gửi trong vòng lặp đọc/ghi từng record khi gom được một lần gửi cho nhiều người nhận. Tách hàm dựng message (người nhận, tiêu đề, nội dung, key) thành hàm thuần để unit test. - Background job (
defineJob, named exportjobs,jobs.enqueue; SDK từ0.8.0): khai báo trong module riêng, liệt kê đủ trongjobs(tối đa 50),keyổn định giữa các version (key mới là job khác). Tự kiểm trapayloadnhư input request (JSON tối đa 64 KiB; truyền ID record thay vì dữ liệu lớn). Handler idempotent và chạy lại an toàn (ít nhất một lần, có thể chạy đồng thời với run khác): kiểm tra trạng thái/record trước khi ghi, dùngjob.idlàm idempotency key khi gọi dịch vụ ngoài, némRetryableErrorchỉ cho lỗi tạm thời (lỗi khác kết thúcFAILEDkhông retry), giá trị trả về bị bỏ qua nên lưu kết quả vào record hoặc state. Mỗi lần thử trongtimeoutMs(tối đa 60 giây): việc dài xử lý một trang (tối đa 200 record, sắp xếp ổn định, ghi bằng batch) rồijobs.enqueuechính nó với cursor và idempotency key${job.id}:next, có điều kiện dừng. Enqueue từ route/trigger/webhook vớiidempotencyKeytheo khoá nghiệp vụ (tối đa 128 ký tự, key cũ trả run cũ vớiduplicate: truekể cả đã kết thúc),delayMshoặcrunAtkhi cần chạy trễ; enqueue sau các thao tác ghi record mà job phụ thuộc và không cùng transaction; tối đa 50 lần enqueue mỗi invocation; run đã enqueue không hủy riêng được nên handler đọc lại dữ liệu và bỏ qua việc không còn cần. Lịch: cron năm trường (không tênMON, không macro@daily), múi giờ IANA mặc địnhUTC,defineJobvalidate cron khi module được nạp (typecheck không phát hiện), mốc bị lỡ không chạy bù, run lịch có thể chồng nhau nên bọclocks.withLock(waitMs: 0) cho đoạn không được chạy song song. - Custom Module Action (
defineAction,s; SDK từ0.15.0, mục Custom Module Action): khai báo trong module riêng, liệt kê đủ trong named exportactions(tối đa 50);defineActionvalidate ngay khi module được nạp (key lạ, handler không phải function,descriptionthiếu khi mở cho agent némValidationError) nên chạynpm testhoặc máy chủ local trước publish.key(chữ thường, chữ số,_, tối đa 64 ký tự) giữ ổn định giữa các version: node Process và tool agent tham chiếuprojectSlug+keyvà luôn gọi version active, đổi key là action khác.label1–100 ký tự;descriptiontối đa 1.000 ký tự, viết cho AI Agent đọc (khi nào dùng, cần gì, trả gì);.describe()cho từng property (1–500 ký tự) để builder hiển thị và agent điền đúng. Chọneffect: "read"khi action không ghi: host từ chối ghi record,locks,push,notifications.send,email.send,jobs.enqueue,secrets.get(và key{ secret }củacrypto),fetchkhácGET/HEADhoặc có credential,processes.start,agents.startvớiPermissionDeniedError(This action is declared read-only);"write"khi có ghi, và tool AI Agent của action này mặc định cần phê duyệt. Schema: gốcinput/outputlàs.object; builders.string({ minLength, maxLength })(mặc địnhmaxLength10.000, không cópattern: kiểm tra định dạng trong handler rồi trả kết quả hoặc némValidationError),s.enum(1–100 giá trị),s.number/s.integer(minimum/maximum),s.boolean,s.date(YYYY-MM-DD),s.dateTime(Unix ms),s.recordId(objectSlug)(kiểuCogoverRecordId, truyền thẳng vào records API; Cogover không kiểm tra record tồn tại),s.array(item, { maxItems })(mặc định 1.000),s.object(tối đa 50 property, tên[A-Za-z][A-Za-z0-9_]{0,63}, giữ thứ tự khai báo); lồng tối đa 3 cấp, JSON Schema input + output ≤ 16 KiB; schema viết tay không được nhận. Property Process có thể để trống thì khai báo.optional()(node không gửi property rỗng). Cogover kiểm tra input trước handler (INPUT_INVALID, handler không chạy) và output sau handler (OUTPUT_INVALID, thao tác ghi đã làm không hoàn tác): trả object khớp schema, khôngundefined. Bên gọi chỉ nhận mã lỗi và thông báo cố định (SCRIPT_ERRORkèmdetails.scriptErrorCode, không có message của handler): kết quả dự kiến như không tìm thấy, không đủ điều kiện, vượt hạn mức trả trong output (ví dụfound,reasonkiểus.enum) để gateway phía sau rẽ nhánh; chỉ ném lỗi cho trường hợp thật sự thất bại. Mọi lời gọi chạy trong ngân sách route vàtimeoutMs(1.000–8.000 ms, vượt làTIMEOUT, handler không bắt được): việc dài hơnjobs.enqueuerồi trả ngay (ví dụstatus: "queued"). Idempotent: bên gọi gửi lại cùngaction.runIdkhi không nhận được trả lời, Cogover trả kết quả đã lưu trong 7 ngày nhưng có thể chạy lại handler nếu bị gián đoạn giữa chừng, nên dùngaction.runIdlàm idempotency key chojobs.enqueue,processes.start,notifications.send,email.sendvà lời gọi ra ngoài; lần chạy mới của node/tool córunIdmới. Danh tính: đọcinvocation.executionIdentity/invocation.user(nullkhi system) vàinvocation.source(type: "process"vớiprocessId,processInfoId,instanceId,nodeId,runAsPROCESS_STARTER/PERSONNEL/SYSTEM;type: "agent"vớiagentId,sessionId,runId,interactive,initiatorPersonnelId); xử lý rõ nhánhuser: null; action do agent gọi chạy dưới danh tính của agent (nhân sự run-as của agent, agent nội bộ thì người đang chat, lượt chạy nền theorunAscủa lượt chạy) nên quy tắc theo người dùng thật dựa vàoinitiatorPersonnelId, không tinpersonnelIdnằm trong input do LLM hay dữ liệu Process sinh ra để nâng quyền. Input từ agent là dữ liệu không tin cậy như request của client. Ghi record của action tiêu thụ ngân sách tự động hoá của lượt chạy gọi nó (MAX_HOP_EXCEEDEDkhi hết), nên action ghi Object có trigger hoặc Process tự kích hoạt lại không tạo vòng lặp vô hạn nhưng vẫn phải có điều kiện dừng. Tách logic thành hàm thuần nhận input đã định kiểu và dữ liệu đã đọc để unit test; handler chỉ đọc, gọi hàm thuần, ghi và trả output. - Khởi chạy Process và AI Agent (
processes,agents; SDK từ0.15.0, mục Process và AI Agent): có trong script, route, trigger after-change, job và action"write"; không có trong before-change, action"read"(chỉget), preview/session chỉ đọc.processes.start(processInfoId, …)dùng Process ID (PI...) đã được policy cho phép, khởi chạy version đã kích hoạt và xuất bản của Normal Flow:inputlà object theo tên biến Process được đánh dấu input (tối đa 64 KiB),instanceName1–255 ký tự.agents.start(agentId, …):instruction1–20.000 ký tự,variablestối đa 50 giá trị chuỗi (≤ 4.000 ký tự)/số/boolean,recordstối đa 20{ objectSlug, recordId, withActivities },resultSchemabằngs.object(agent trả kết quả không khớp thì lượt chạy thất bạiRESULT_SCHEMA_MISMATCH),runAs: "caller"(mặc định) hoặc"agent"(bắt buộc khi không có người dùng),approvalPolicy: "deny"(mặc định: tool cần duyệt không chạy) hoặc"autoApprove"(cầnallowAutoApprovevà người dùng),timeoutSeconds10–1.800 (mặc định 300).deliverTo: { personnelId, conversation, title }(SDK từ0.16.0, mục Gửi tin nhắn chat cho một nhân sự) cho agent gửi tin nhắn chat tới một nhân sự trong AI chat của agent với người đó:personnelIdbắt buộc;conversation: "new"(mặc định) luôn mở hội thoại mới,"latest"nối hội thoại gần nhất của agent với người nhận và mở mới khi chưa có, hội thoại đang bận hoặc đã bị xoá;titlemột dòng 1–80 ký tự (mặc định lấy phần đầu tin nhắn). Agent tự soạn tin và tự quyết định có gửi hay không nêninstructionnêu người nhận cần biết gì và khi nào đáng gửi;text/resultchỉ trả về code, không gửi cho người nhận; khi người nhận trả lời, agent chạy với danh tính và quyền của người nhận. Người nhận phải là nhân sự của Workspace, được dùng agent và có seat AI Agent: không đạt thìstartnémCogoverApiErrorRECIPIENT_NOT_FOUND,RECIPIENT_ACCESS_DENIEDhoặcRECIPIENT_AI_SEAT_REQUIRED(cần đổi cấu hình Workspace: báo người dùng, không thử lại ngay), hoặcDELIVERY_UNAVAILABLE(tạm thời, thử lại sau); không lượt chạy nào được tạo, mã có ởcodevàdetails.upstreamCode, và nói về người nhận, không về người gọi (lỗi của người gọi làAI_SEAT_REQUIRED,AGENT_RUN_DENIED). Kết quả gửi nằm ởdeliverycủaagents.getvàagentRun.deliverycủa jobonResult(kiểuAgentDelivery;nullkhi không códeliverTo, đọc khi lượt chạy đã ở trạng thái cuối):statusSENT(kèmchatSessionId,conversationId,newConversation),NOT_SENT(agent không gửi),BLOCKED/FAILEDkèmerrorCode(ví dụRECIPIENT_RATE_LIMITEDkhi vượt số tin một lượt chạy hoặc một ngày được gửi cho người đó); lượt chạy có thểCOMPLETEDmà tin không được gửi nên job kiểm tradelivery.statusriêng vớiagentRun.status. Không chờ kết quả trong route/action:onComplete: { job, payload }/onResult: { job, payload }chỉ định job của version active (payload ≤ 8 KiB; job không được khai báo thìstartnémValidationError), job nhậnProcessCompletionJobPayload(payload,process.stateCOMPLETED/CANCELED/DELETED/FAILED,process.outputlà biến output hoặcnullkhi lớn hơn 48 KiB) hoặcAgentResultJobPayload(agentRun.status,text≤ 32 KiB,resulthoặcnullkhi lớn hơn 16 KiB,error.codetrong danh sách cố định,usage,delivery), chạy dưới danh tính của lần thực thi đã gọistart,enqueuedBylàprocess:<instanceId>/agent:<runId>; kiểm trastate/statustrước khi dùng kết quả, job có thể chạy lặp nên idempotent, và kết quả không được giao khi version active không còn khai báo job, hết ngân sách tự động hoá hoặc chuỗi job quá dài. Luôn truyềnidempotencyKeytheo khoá nghiệp vụ (trong job suy ra từpayload/job.id, trong action từaction.runId; nhiều người dùng chung khoá thì đưa người dùng vào key; một việc nhắn nhiều người thì đưa người nhận củadeliverTovào key, vì cùng key vớideliverTokhác trả lượt chạy đầu và không gửi cho người kia): cùng key trả lượt chạy cũ vớiduplicate: true, key thuộc danh tính khác némCogoverApiErrorIDEMPOTENCY_CONFLICT.RetryableErrorvớidetails.writesMayHaveCompleted: truenghĩa là lượt chạy có thể đã được tạo: thử lại sau với cùng key (trong job ném lạiRetryableError), không đổi key. Giới hạn: 20processes.startvà 5agents.startmỗi lần thực thi (RateLimitErrorkhông códetails.budget), 1.000 Process mỗi ngày UTC mỗi project,maxRunsPerDaycủa agent; mỗi lần start tốn một bước ngân sách tự động hoá (MAX_HOP_EXCEEDED) và agent lồng nhau tối đa 3 cấp (AGENT_DEPTH_EXCEEDED): không thiết kế chuỗi Process/agent → action/job → start lại chính nó. Lỗi khác:PermissionDeniedError(PROCESSES_NOT_ALLOWED,AGENTS_NOT_ALLOWED,AGENT_AUTO_APPROVE_NOT_ALLOWED,PROCESS_START_DENIED,AGENT_RUN_DENIED),NotFoundError(Process không tồn tại/không active, agent không có,getlượt chạy không do project khởi tạo),ValidationErrorvớidetails.upstreamCode(ví dụ biến input không tồn tại),CogoverApiErrorvới mã tính phí credit AI củaagents.start(INSUFFICIENT_CREDITS,AI_SUBSCRIPTION_REQUIRED,AI_SEAT_REQUIRED,AGENT_CREDIT_LIMIT_EXCEEDED,MODEL_NOT_PRICED: không lượt chạy nào được tạo, báo người dùng việc cần làm thay vì thử lại ngay),PROCESSES_DISABLED/AGENTS_DISABLEDlà dependency của môi trường.processes.get/agents.getchỉ để hiển thị trạng thái; không poll trong vòng lặp. Tách hàm dựng tham số start và hàm xử lý payload kết quả thành hàm thuần để unit test. - Secret/credential (
fetch({ credential }),secrets.get, key{ secret }củacrypto; SDK từ0.8.0): ưu tiên credential khi token nằm trong HTTP header (không tự đặtauthorizationhoặc header trùng với header đã cấu hình); chỉ dùngsecrets.get/{ secret }với secret OPAQUE khi cần chính giá trị, đọc một lần trước vòng lặp (tối đa 20 thao tác secret mỗi invocation, giá trị cache trong invocation); không ghi giá trị vào log, record, state, response hay thông báo lỗi; chỉ trả field cần thiết từ response của dịch vụ ngoài vì response có thể chứa token.401/500từ dịch vụ ngoài là responsefetchbình thường: kiểm traok/status; timeout không chứng minh thao tác ghi đã thất bại. - Crypto hash/HMAC/random (
sha256,hmacSha256,randomBytes,randomUUID,timingSafeEqual; SDK từ0.8.0): lấy từ context hoặcimport { crypto } from "@cogover/sdk", mọi thao tác trừtimingSafeEqualcầnawaitvà tính một lời gọi cục bộ (localCalls), không tính capability call; so sánh chữ ký/token luôn bằngtimingSafeEqual; tạo body gửi đi một lần rồi ký đúng chuỗi đó, kiểm tra chữ ký inbound trênrequest.rawBody(khôngJSON.stringify(request.body)), chuẩn hoá encoding/prefix của bên gửi trước khi so; tối đa 256 KiB mỗi lần gọi; chỉ SHA-256 hash/HMAC, không băm mật khẩu; token truy cập chỉ lưu hash kèm hạn; sandbox không cóBufferhaynode:crypto. - Mã hoá và chữ ký (
aesEncrypt,aesDecrypt,rsaEncrypt,rsaDecrypt,sign,verify; SDK từ0.9.0): key qua{ secret: name }(AES key thêmencoding: "base64"hoặc"hex"khi secret lưu dạng đó), không đưa private key/AES key thật vào source, fixture hay log (test chỉ dùng key sinh riêng cho test). AES: mặc định GCM và để Cogover sinh IV ngẫu nhiên, chỉ truyềnivkhi bên kia quy định và không bao giờ dùng lại IV với cùng key GCM; gửiivcùng ciphertext; tag mặc định nối cuối ciphertext (Java, .NET, Web Crypto),separateTag: truekhi bên kia cần tag riêng (Node.js, OpenSSL);aadphải khớp khi giải mã; CBC chỉ khi bên kia bắt buộc và xác thực thêm bằng HMAC. RSA: mặc định OAEP-SHA256 với MGF1 SHA-256, xác nhận digest MGF1 của bên kia (code JavaOAEPWithSHA-256AndMGF1Paddingthường để MGF1 SHA-1, không khớp cảOAEP-SHA256lẫnOAEP-SHA1),PKCS1chỉ khi bắt buộc; dữ liệu vượt giới hạn RSA thì mã hoá bằng AES với keyrandomBytes(32)rồi chỉ mã hoá key đó bằng RSA. Chữ ký: chọnalgorithm/encoding/signatureFormattheo bảng JWTalgcủa SDK API reference (ES*cầnbase64urlvàieee-p1363), ký và xác minh đúng chuỗi byte (request.rawBody,header.payloadcủa JWT), không dùngRSA-SHA1cho chữ ký mới;verifytrảfalsevới chữ ký sai hoặc sai định dạng nên kiểm tra giá trị trả về, không dựa vào exception; JWT có chữ ký hợp lệ vẫn phải kiểmexp,iss,audtrước khi tin. Giải mã thất bại vì bất kỳ lý do gì némCogoverApiErrorcode: "DECRYPTION_FAILED": bắt riêng để trả lỗi nghiệp vụ tiếng Anh, không log ciphertext/plaintext nhạy cảm; plaintext không phải UTF-8 thì dùngoutput: "bytes"hoặc encoding nhị phân. Key/thuật toán/tuỳ chọn sai némValidationError;data,aadvà ciphertext tối đa 256 KiB, văn bản key tối đa 16.384 ký tự; mỗi lời gọi tính một lời gọi cục bộ (localCalls), key{ secret }còn tính vào giới hạn 20 thao tác secret mỗi invocation. - Inbound webhook (SDK từ
0.8.0): đăng ký route dưới/hooks/(bên gửi gọi/hooks/{inboundId}/..., handler thấyrequest.pathkhông có inbound ID), kiểm trainvocation.identity === "inbound"vàinvocation.inbound.modekhi bắt buộc HMAC (lời gọi qua URL thường hoặc local có danh tính người dùng thì trả403), validaterequest.body(JSON object; body dạng khác cho{}và tự parse từrawBody/contentType). Handler ngắn: kiểm tra, ghi dấu đã nhận bằng stateexpectedVersion: 0(xử lýStateConflictErrorkhi trùng, thêm định danh bên gửi vào khoá nếu nhiều bên có thể dùng cùng event ID) hoặcjobs.enqueuevới event ID làmidempotencyKey, rồi trả200/202; nhà cung cấp gửi lại khi không nhận 2xx nên phải chịu được sự kiện lặp, và chặn chữ ký đã dùng của Cogover không bảo đảm tuyệt đối.request.headerskhông chứa Authorization, cookie, inbound key hay header chữ ký đã cấu hình. Cần thêm chữ ký riêng của nhà cung cấp thì kiểm tra bằngcryptotrênrawBody; cách ký của họ khớp chế độ HMAC có sẵn thì cấu hình ở inbound access (bước 6A) để Cogover kiểm tra trước handler.
- Dùng public
- Test logic, input không hợp lệ, thiếu record, quyền được phép/bị từ chối và ghi lặp khi liên quan; route, trigger hoặc job xử lý nhiều record thêm ca khối lượng lớn nhất dự kiến và ca
usage()sắp hết để chứng minh bàn giao sang job. Custom Module Action: test hàm thuần với giá trị biên của schema (property optional vắng mặt, chuỗi dài nhất, số ởminimum/maximum, mảng rỗng), mọi nhánh kết quả dự kiến trong output, danh tínhuser/systemvàsourceprocess/agent, gửi lại cùngrunIdkhông tạo hiệu ứng lần hai; kiểm traJSON.stringify(schema)của input/output đúng ý (thứ tự property,required,description). Khởi chạy Process/AI Agent: test hàm dựng tham sốstart(key idempotency, input theo tên biến) và hàm xử lý payload kết quả với từngstate/statusvàresult: null. Decimal, preview/apply hoặc ghi hàng loạt: đọc Batch writes và checkpoint, kiểm tra làm tròn, request đồng thời, preview cũ và lỗi từng phần. Chạy typecheck/build và các test phù hợp dự án. - Backend có dữ liệu thật chạy qua
COGOVER_LOCAL_PORT=<PORT> cogover-dev run --profile <PROFILE> --allow-writes=false -- npm run devcho test đọc; test ghi trong phạm vi yêu cầu dùng--allow-writes=truevới fixture có marker/ID theo dõi. CLI mặc định cho phép writes; chạy local không đồng nghĩa mock hay dry-run. CLI từ0.19.0với SDK từ0.14.0áp cùng ngân sách theo loại lần thực thi của từng handler (route, before-change, after-change, job) và từ chối như trên Cogover, trừ record liên kết củaexpandLookupsvà giới hạn thời gian (chỉ báo trongusage());--no-enforce-limitschỉ đếm để đo mức dùng, chạy lại không có cờ trước khi kết luận PASS. Record trigger chạy trên cùng server local theo Record trigger quick start:GET /__cogover/triggersliệt kê manifest đã chuẩn hoá;POST /__cogover/triggers/<key>vớioperationvàchanges(create) hoặcrecordIdkèmchanges(update/delete), hoặcrecords: [...]cho một lô; đối chiếuinput.records,results(errors,changes) vàwarnings. Before-change chạy với--allow-writes=false; after-change có ghi hoặc push cần server không có cờ này và ghi/gửi thật qua Development Session (Development Session chỉ đọc từ chối push). Push trên local gửi tới người dùng thật của Workspace: khi thử, giới hạnrecipientslà personnel test hoặc record fixture chỉ người test đang mở. Notification và email trên local cũng tới người thật, kể cả địa chỉ ngoài Workspace: chỉ gửi tới personnel test và địa chỉ test đã được người dùng xác nhận, từ hộp thư test hoặc hộp thư được phép thử, gắnidempotencyKeyvà ghi lạirequestId; session--allow-writes=falsetừ chốinotifications.send/email.sendnhưng vẫn gọi đượcemail.senders()để xác minh hộp thư khả dụng.records.aggregatetrên local chạy dưới danh tính người dùng của Project key nên có giới hạn củadata.asUser():groupByhoặc metric ngoàicounttrên"id"/field số bị từ chối (ValidationError) dù route production chạy bằng người gọi nhóm được; kiểm thử phần nhóm của route trên version đã publish ở bước 9, hoặc quadata.asSystem()khi code thực sự dùng danh tính đó. Đọcorgchạy được với session chỉ đọc và dùng cơ cấu thật của Workspace dưới danh tính nhân sự được cấp Project key (khác người dùng của Workspace API key khi gọi production): đối chiếu kết quả/me, thành viên phòng ban và chuỗi quản lý với dữ liệu Department/Personnel/Position đọc qua$user-permission, thay đổi cơ cấu hiện ra sau vài giây. Runner không đánh giáwhen/changedFields/runWhenvà bỏ field ngoàifields; route/__cogover/chỉ có trên local. Cogover chỉ gửi sự kiện thật tới version đã activate, nên kiểm thử bằng thay đổi bản ghi thật ở bước 9. Máy chủ local không chạy job, lịch cron hay xác thực inbound:jobs.enqueuetừ route local tạo run thật trên Cogover chạy code job của version đang active (publish/activate handler job trước khi thử theo cách này; session--allow-writes=falsetừ chối enqueue); route/hooks/...gọi local mang danh tính người dùng nên điều kiện inbound trả403theo thiết kế; secret, credential và{ secret }chỉ dùng được local khi quản trị viên cho phép Development Session, còn hash, random vàcryptovới key truyền trực tiếp chạy được local (chỉ dùng cặp key test sinh riêng, không dùng key thật). Mã hoá/chữ ký: kiểm tra round-trip (aesEncrypt→aesDecrypt,rsaEncrypt→rsaDecrypt,sign→verify), ca ciphertext bị sửa trảDECRYPTION_FAILED, chữ ký bị sửa trảfalse, và giải mã/xác minh mẫu do bên kia cung cấp để chứng minh tương thích. Tách logic job/webhook thành hàm thuần để unit test bằng fixture; kiểm chứng retry, lịch, danh tính và chữ ký trên Cogover ở bước 9. Custom Module Action: Process và AI Agent chỉ gọi version đã activate trên Cogover; starter dùng cho SDK0.15.0trở lên có thể có runner action local, dùng theo README của bản đã clone và không tự gọi__cogoverActionHandler(dành riêng cho Cogover). Không có runner thì kiểm thử handler bằng unit test và, khi cần dữ liệu thật, gọi cùng hàm thuần từ một route chỉ dùng khi phát triển rồi gỡ trước publish;effect, kiểm tra input/output theo schema,runId, danh tính và lỗi bên gọi nhận được kiểm chứng trên Cogover ở bước 9.processes.start/agents.startchạy trên local với Dev CLI từ0.20.0và session ghi được: tạo lượt chạy Process/agent thật dưới danh tính nhân sự của Project key (chỉ dùng Process/agent test,idempotencyKeycó marker),onComplete/onResultbị từ chốiNOT_SUPPORTEDnên thử start không kèm hai tuỳ chọn, đọc kết quả bằngprocesses.get/agents.get, rồi kiểm thử job nhận kết quả sau publish. Lượt chạy códeliverTotrên local gửi tin nhắn chat thật: người nhận là personnel test đã biết trước việc này. - Frontend test trên browser: sub-agent Frontend tự kiểm tra khi phát triển các trạng thái loading, empty, error, thao tác chính, khả năng dùng bàn phím và kích thước màn hình liên quan; sub-agent E2E độc lập viết E2E test tự động và chạy trên trình duyệt theo Kiểm thử E2E frontend, chạy các ca chạy được trên local ngay khi frontend báo sẵn sàng. Full-stack phải test xuyên frontend → backend → dữ liệu. Đăng nhập trình duyệt test bằng tài khoản người dùng cung cấp; không có thì sub-agent E2E dùng
$cogover-api-authđổi Workspace API key thành phiên Web App (POST /bapi/v1/auth-token) rồi nạp cookie phiên vào browser context. Phiên mang danh tính chủ key nên persona khác lấy theo$user-permission; agent không gõ mật khẩu vào form đăng nhập. Custom component: chạynpm run build-watchvànpm run preview, dán URLhttp://localhost:5101/#./Components/<Tên>do preview in ra vào item Federation component của layout test, kiểm tra trên form thật các ca thiếuformBuilder, cập nhật field/ô Related List, lỗi giữa chừng và không tự chạy lặp; slug item lấy từ layout qua$object-layout, không dùng slug Object. Federation Page:npm run devvới.env.localtrỏ Workspace test, đăng nhập theo cách trên hoặc người dùng tự đăng nhập Cogover trên trình duyệt (agent không nhập mật khẩu), kiểm tra từng route,Linknội bộ và API qua HTTP client của template. URL localhost trên layout chỉ dùng khi debug và phải thay bằng đường dẫn đã deploy ở bước 9. - Ghi có thể đã thành công trước khi lỗi: đọc lại state trước retry; nhiều lệnh ghi không mặc nhiên là transaction. Lưu kết quả test và fixture đã chạm tới, không lưu credential.
Điều kiện bắt buộc cho từng project trước khi kết thúc bước 8:
- Toàn bộ unit tests PASS, số thất bại bằng 0, có test thực sự được chạy; không dùng suite rỗng hoặc skip/todo thay các ca cần kiểm chứng. Typecheck/build và các test tích hợp/browser thuộc phạm vi cũng phải thành công.
- Frontend: sub-agent E2E đã viết E2E test cho mọi tiêu chí nghiệm thu và các ca chạy được trên local đã PASS; ca chỉ chạy được trên Workspace (phiên thật, trang record, URL đã deploy) được ghi rõ để chạy ở bước 9. Thiếu sub-agent E2E độc lập hoặc chưa có script E2E là chưa đạt.
- Backend có record trigger, background job, action ghi hoặc route ghi dữ liệu: sub-agent Review backend đã review code tại commit sẽ publish; phát hiện mức Cao đã sửa và review lại, phát hiện còn mở được agent chính ghi là rủi ro chấp nhận kèm lý do hoặc chuyển thành ca kiểm thử ở bước 9, phát hiện phụ thuộc hành vi nền tảng chưa đo được đo ở bước 9 hoặc bằng spike. Thiếu review độc lập là chưa đạt.
- Review bảo mật: sub-agent Review bảo mật đã review commit sẽ publish của từng project theo Review bảo mật. Không còn phát hiện Nghiêm trọng/Cao đã xác định: không giới hạn số vòng sửa, sau 2 vòng chưa đóng thì báo người dùng và không publish. Phát hiện Trung bình/Thấp sửa tối đa 2 vòng, phần còn lại agent chính ghi rủi ro chấp nhận kèm lý do và báo người dùng trước khi publish. Phát hiện Nghiêm trọng/Cao
PLAUSIBLEcần chạy thử thì chỉ publish để preview, chưa activate. Commit publish ở các vòng sửa sau qua review phần chênh lệch trước khi publish. Thiếu review bảo mật độc lập là chưa đạt. - Line coverage >= 70% riêng backend và >= 70% riêng frontend. Không lấy trung bình hoặc gộp coverage hai project; không dùng branch/function/statement coverage thay line coverage; không cộng E2E/integration coverage vào kết quả unit tests.
- Đo coverage từ unit-test suite trên toàn bộ source ứng dụng của project, kể cả file chưa được test import. Chỉ loại trừ dependency, test/fixture, build output, code sinh tự động, declaration và cấu hình/local runner không thuộc code ứng dụng; ghi rõ cấu hình include/exclude. Không loại code nghiệp vụ, entrypoint hay UI cần chạy để đạt ngưỡng. Backend test logic/validation/error/side effect bằng dependency được kiểm soát; frontend test logic và component/hành vi UI phù hợp stack.
- Lưu cho mỗi project: lệnh test, số test pass/fail, số dòng covered/total, phần trăm lines và đường dẫn báo cáo coverage, khớp source cuối sẽ publish. Sau khi sửa code, chạy lại unit tests với coverage của project bị ảnh hưởng.
- Agent chính kiểm tra báo cáo và ngưỡng, yêu cầu sub-agent sửa nếu chưa đạt. Coverage chưa đo hoặc thiếu báo cáo là chưa đạt, không tự coi là 70%; không chuyển project chưa đạt sang publish.
9. Publish, activate và test trên Workspace thật
- Ghi nhận active version hiện tại của từng project và các thay đổi schema liên quan. Kiểm tra archive không có secret,
.env, session file, source map hoặc dependency/local runner ngoài phạm vi. Publish, approve policy và activate thay đổi môi trường thật: trước mỗi thao tác xin người dùng xác nhận cho đúng project và version (version ID hoặc commit, thay đổi so với version đang active, trigger/job/lịch/action bị ảnh hưởng, dữ liệu thật có thể bị ghi); xác nhận cho version trước, cho riêng bước publish hay cho kế hoạch chung không thay xác nhận activate version mới. Chạy publish, approve policy, activate và đọc lại thành các lệnh riêng, không gộp thao tác thay đổi môi trường với nhau hay với thao tác đọc lại. Môi trường của agent từ chối hoặc đòi phê duyệt một thao tác thì dừng, báo người dùng thao tác bị chặn và lý do, chờ quyết định; không đổi sang đường khác cho cùng hiệu ứng (management API thay CLI, gộp hoặc tách lệnh, đổi tên script) để vượt qua. cogover-dev publishtại đúng thư mục project: backend ZIP chứasrc/main.ts; frontend phải build trước, ZIP chứadist/index.html(giữ thư mụcdist/), CLI không build frontend. Publish qua API trực tiếp: upload ZIP private, dùng file metadata thật cùngidempotencyKeytheo reference; không đoán upload endpoint.- Chờ version
READYhoặcFAILEDcó thời hạn; HTTP202chưa phải hoàn tất. Timeout: đọc version đã tạo trước khi publish lại.FAILED: sửa nguyên nhân rồi tạo version mới. Ghi version ID và build error an toàn nếu có. Backend có trigger: build evaluate exporttriggersthành trigger manifest và validate Object/field slug tồn tại,writableFieldskhông có field chỉ đọc, key không trùng;TRIGGER_MANIFEST_INVALIDnêu key/slug sai, sửa code rồi publish lại. Backend có job: build evaluate exportjobsthành job manifest (key,timeoutMs,maxAttempts,scheduleđã chuẩn hoá);defineJobnémValidationErrorvới cron/múi giờ/key sai khi module được nạp nên chạy máy chủ local hoặcnpm testtrước publish, không chỉ typecheck. Backend có action: build đọc exportactionsthành action manifest (key, label, mô tả,exposeTo,effect,timeoutMs, JSON Schema input/output);ACTION_MANIFEST_INVALID(key trùng, schema dùng keyword không hỗ trợ hoặc lớn hơn 16 KiB) sửa code rồi publish lại. - Backend có policy: approve/read snapshot đúng version theo bước 5. Preview service
6phải chỉ rõ version ID vàREAD_ONLY/READ_WRITE; preview mặc địnhREAD_WRITE, ghi thật và không rollback theo nhóm. - Activate đúng version bằng CLI, đọc lại project xác nhận
ACTIVEvàactiveVersionId. Full-stack: backend sẵn sàng trước frontend phụ thuộc nó; hoàn tất Process còn chờ dependency. Version có trigger: activate áp dụng manifest ngay cho mọi thao tác create/update/delete trên Object từ mọi nguồn; activate version khác thay bằng manifest của version đó, deactivate gỡ toàn bộ. Nêu phạm vi ảnh hưởng trong yêu cầu xác nhận ở mục 1, đặc biệt lần activate đầu tiên trên Object đang có dữ liệu thật; gỡ một trigger bằng cách bỏ khỏi exporttriggersrồi publish/activate version mới. Version có trigger after-change hoặc job ghi dữ liệu: trước khi activate, xác nhận không có kiểm thử thủ công, spike hay sub-agent khác đang sửa cùng record hoặc Object mà trigger/job sẽ tính lại (trigger/job sẽ ghi đè số liệu và làm sai kết luận của kiểm thử đó); có thì hoãn activate hoặc tách dữ liệu test. Sau activate, đọc Record trigger đã đăng ký (POST /api/v1/ts-projects/triggers/listvớiprojectId, không nằm dưới/ts-projects/{projectId}/) để xác nhận trigger của version mớiACTIVEkhớp manifest; chưa khớp thì đọc lại sau vài giây trước khi kết luận. Version có job: activate làmjobs.enqueuenhận các key của version đó và bật lịch ngay (xem bằngcogover-dev jobs schedules); activate version khác thay lịch, deactivate gỡ lịch; run chạy trễ/retry dùng code của version active tại lúc chạy nên giữ key và định dạng payload tương thích. Lịch ghi dữ liệu thật nêu trong yêu cầu xác nhận ở mục 1; dừng một lịch bằng cách bỏschedulerồi publish/activate version mới. Version có action: activate đưa action vào danh mục của Process Builder/AI Agent Builder và mọi node/tool đang tham chiếu cùngprojectSlug+keygọi version mới ngay; bỏ action, đổikey, bỏ"process"/"agent"khỏiexposeTohoặc deactivate Project làm node/tool đó lỗi (ACTION_NOT_FOUND,ACTION_NOT_EXPOSED; xuất bản lại Process bị chặn với mã71/72), đổi schema không tương thích (thêm input bắt buộc, đổi kiểu, bỏ field output mà bước sau đọc) làm node cũ lỗiINPUT_INVALIDhoặc tham chiếuRESOURCE_NOT_FOUND. Trước khi activate, liệt kê Process/tool đang dùng action và báo người dùng thay đổi cần làm trên chúng; đổi schema thì nhờ sub-agent Process/AI Agent cập nhật node/tool theo schema mới. Trong lúc đổi version active, lời gọi có thể thất bại chốc lát vớiPROJECT_POLICY_NOT_APPROVEDvà bên gọi tự gửi lại. Có Git: gắn tagcogover-<VERSION_ID>vào commit đã publish và push tag. - Backend: gọi production service
3bằng session do CLI tạo theo mẫu cURL; kiểm tra HTTP/body và đọc lại dữ liệu nghiệp vụ. Preview hoặcREADYkhông phải bằng chứng production PASS. Route xử lý nhiều dữ liệu: gọi preview service6vớishowDebugDatatrên dữ liệu đại diện, sousage,operations(đếm cả lời gọi bị từ chối, chỉ ra lời gọi chạm giới hạn) vàdurationMsvới ngân sách route; sát ngân sách thì đổi thiết kế trước khi bàn giao. Route dùng query string: test filter có kết quả/không có kết quả và cursor trên production; local khác Workspace thì áp dụng mục tương thích trong Full-stack integration. Record trigger: kiểm thử bằng thay đổi bản ghi thật qua$object-recordhoặc giao diện theo mục 8 của Record trigger quick start, đủ các ca của từng trigger: bị từ chối trả HTTP400,r: 70,msg: BEFORE_CHANGE_TRIGGER_REJECTED,data.trigger.key,metatheo field slug ($recordcho lỗi cả bản ghi) vàmessages; điều chỉnh giá trị thì đọc lại record thấy field đã điền; after-change đọc lại sau vài giây và kiểm trarunWhen: "onEnter"không chạy lại khi record đã khớpwhen. HTTP503vớiBEFORE_CHANGE_TRIGGER_FAILEDlà handler ném lỗi, hết giờ hoặc ghi trong before-change: mọi thao tác ghi liên quan trên Object bị chặn cho tới khi activate version đã sửa hoặc version cũ hợp lệ. Dọn fixture theo đúng quy tắc trigger (ví dụ đổi trạng thái trước khi xoá). Push message: xác minh trên trình duyệt đã đăng nhập đang mở record đích (record tải lại saurefreshRecords, toast hiển thị đúng nội dung/vị trí/thời lượng, người trongexcludekhông nhận) và log Project không cóPUSH_DISABLED; push best-effort nên không nhận được một lần chưa chứng minh lỗi code, lặp lại với record khác trước khi kết luận. Backend có trigger, job, action ghi hoặc route ghi dữ liệu: sub-agent Review backend chạy tiêu chí nghiệm thu trên version đã activate theo Kiểm thử end-to-end trên Workspace; lỗi giao lại sub-agent Backend, sau mỗi version sửa chạy lại ca lỗi cùng ca hồi quy liên quan. - Background job, Custom Module Action, khởi chạy Process/AI Agent, secret/credential, crypto, inbound webhook, notification, email và cơ cấu tổ chức trên Workspace thật:
- Background job:
cogover-dev jobs schedulesxác nhận lịch sau activate; enqueue thử bằngcogover-dev jobs enqueue <key> --payload-file payload.json --idempotency-key <key>(có--delay-mskhi cần; run này mang danh tính Super Admin của Workspace API key, nên nhánh danh tính system kiểm thử bằng run theo lịch hoặc từ webhook) hoặc qua route/trigger, rồi theo dõicogover-dev jobs runs --job <key>vàcogover-dev jobs run {RUN_ID}tớiSUCCEEDEDvới số lần thử mong đợi, đọcdurationMs/usagecủa lần thử gần nhất (CLI từ0.19.0) để biết mức dùng ngân sách job và chỉnh kích thước trang; đọc lại record/state để chứng minh hiệu ứng nghiệp vụ. Kiểm thử ca lỗi tạm thời (PENDINGvới lỗiRETRYABLErồiSUCCEEDEDở lần thử sau,FAILEDkhi hếtmaxAttempts), enqueue lặp cùng idempotency key trảduplicate: true, chạy trễ, job tự enqueue trang kế dừng đúng lúc. Run theo lịch chờ mốc kế tiếp; cần thử nhanh thì dùng cron ngắn trên Workspace test rồi publish lại lịch thật. CLI/management API không trả payload, chỉpayloadBytes; lịch sử run giữ bảy ngày.JOBS_DISABLED,JOB_NOT_DEFINED,IDENTITY_NOT_GRANTED,JOB_ACTOR_UNAVAILABLE,JOB_TIMEOUT,RateLimitErrorxử lý theo bảng sự cố của Background job quick start. - Custom Module Action: đọc danh mục
GET /api/v1/ts-projects/actions?consumer=process(vàconsumer=agent) vớix-req-type: 9,x-req-service: 4bằng phiên Workspace, xác nhận action của version active có đúngexposeTo,effect,timeoutMs, schema và thứ tự property. Process: sub-agent Process tạo hoặc cập nhật node Custom Module Action theo Custom Module Action Task (schema snapshot lấy từ route chi tiết của danh mục), kích hoạt, chạy lượt chạy và đọc$action.<slug>.output(status,result.*,errorCode,errorMessage,runId,durationMs) cùng hiệu ứng nghiệp vụ bằng$object-record; đủ các ca của contract: thành công và bước sau dùngresult.*, kết quả dự kiến trong output,INPUT_INVALID(thiếu hoặc sai kiểu), lỗi handler (SCRIPT_ERROR) vớicontinueOnFailurebật và tắt, từng danh tính dùng thật (PROCESS_STARTER,PERSONNEL,SYSTEMcó và không cóallowInternalSystem),effect: "read"từ chối ghi. AI Agent: sub-agent AI Agent tạo toolCUSTOM_MODULEtheo mục CUSTOM_MODULE, gắn vào skill/agent, chat bằng câu hỏi cần action và đối chiếu agent gọi đúng tool với input hợp lệ, câu trả lời dựa trên output; tool"write"sinh yêu cầu phê duyệt, từ chối không tạo hiệu ứng; agent CSKH chạy bằng nhân sự cố định thì kiểm thử quy tắc theoinitiatorPersonnelIdbằng hai người chat khác quyền. Log Project không được chứa input/output nhạy cảm. - Khởi chạy Process/AI Agent: approve policy có mục
processes/agentscho đúng version; gọi route/job/action khởi chạy với Process hoặc agent test, ghiinstanceId/runId, theo dõi lượt chạy Process theo$process-creator(chi tiết lượt chạy, giá trị resource) hoặcagents.gettới trạng thái cuối, rồi xác nhận job nhận kết quả chạy (cogover-dev jobs runs --job <key>; nguồnenqueuedBylàprocess:<instanceId>hoặcagent:<runId>đọc qua mục Liệt kê hoặc đọc lần chạy job của Backend API Reference) và hiệu ứng đã đọc lại; gọi lặp cùngidempotencyKeytrảduplicate: true; ca bị từ chối theo contract: mục policy thiếu (PROCESSES_NOT_ALLOWED/AGENTS_NOT_ALLOWED), người dùng không có quyền khởi chạy (PROCESS_START_DENIED/AGENT_RUN_DENIED, cần persona theo$user-permission), execution không có người dùng thiếuallowSystem,autoApprovekhi chưa bậtallowAutoApprove. CódeliverTo: người nhận là personnel test đã biết trước (tin là thật); đọcdeliveryquaagents.gethoặc jobonResultkhi lượt chạy ở trạng thái cuối,SENTthì đối chiếuconversationIdvà người nhận xác nhận thấy tin trong AI chat của agent (conversation: "latest"nối đúng hội thoại gần nhất); thử ca người nhận không tồn tại, không được dùng agent hoặc chưa có seat AI Agent (RECIPIENT_NOT_FOUND/RECIPIENT_ACCESS_DENIED/RECIPIENT_AI_SEAT_REQUIRED, không tạo lượt chạy) và cainstructioncho phép agent không gửi (NOT_SENT). Huỷ lượt chạy test còn dang dở theo quy tắc an toàn của$process-creator. - Secret/credential:
cogover-dev secrets listcó mụcACTIVEđúng tên, loại, host trước khi gọi route; gọi route production và xác minh response không chứa, log không ghi giá trị; trong phạm vi được phép, thử host ngoài danh sách nhậnFETCH_BLOCKEDrồi khôi phục; xoay giá trị bằngsecrets setcùng tên và gọi lại không cần publish. Xoá mục thử nghiệm bằngsecrets delete(có xác nhận), giữ mục thật. - Crypto với key
{ secret }: gọi route production với dữ liệu thử, kiểm tra round-trip và ca lỗi như bước 8 trên version đã activate; trao đổi mẫu với bên kia (họ giải mã/xác minh được dữ liệu Cogover gửi, Cogover giải mã/xác minh được mẫu của họ) trước khi dùng dữ liệu thật; response và log không chứa key hay plaintext nhạy cảm.SECRETS_NOT_ALLOWED,TRIGGER_READ_ONLY,ValidationErrorvề key xử lý theo Crypto quick start và mục Key của SDK API reference. - Inbound webhook: tạo inbound access theo bước 6A sau activate; gửi request thử tới
{URL gốc}/{route}với key (Authorization: BearerhoặcX-Cogover-Inbound-Key) hoặc chữ ký HMAC đúng cách ký đã cấu hình (có timestamp header thì ký<timestamp>.<raw body>, gửi--data-binaryđúng chuỗi đã ký) mong đợi200/202và hiệu ứng (state/job/record); ca sai key, sai chữ ký, body đổi sau khi ký, gửi lại cùng chữ ký/timestamp, route ngoàiroutePrefix, access hết hạn mong đợi401trước handler; gửi lại cùng event ID để chứng minh chống trùng;429khi vượt 600 request/phút. Bên gửi không cần Workspace session hay headerx-req-*; response không có envelope. Thu hồi access thử vàunsetbiến shell chứa key/secret sau khi xong; access thật giữ lại và bàn giao metadata. - Notification: kích hoạt qua route, trigger hoặc job trên version active với người nhận là personnel test đã xác nhận (notification và email không thu hồi được); kiểm tra kết quả
notified/skippedPersonnelIds, rồi trên trình duyệt đăng nhập đúng người nhận xác minh notification trong danh sách thông báo (tiêu đề, subtitle, nội dung, người gửi, link mở đúng trang), hình thức gửi theo kênh và cài đặt riêng của người nhận, notification nằm đúng chip danh mục khi truyềncategories, người trongexcludekhông nhận; gọi lại cùngidempotencyKeytrảduplicate: truevà không tạo notification mới.NotFoundErrorvớiresourcenotificationChannellà sai channel ID, vớiresourceIdnotification_channellà Workspace chưa thiết lập notification. - Email: approve policy có mục
emailcho đúng version trước khi thử; xác nhậnemail.senders()trả đúng hộp thư mong đợi; gửi tới địa chỉ test, người dùng xác nhận thư tới nơi (gửi bất đồng bộ nên chờ và kiểm tra lại trước khi kết luận lỗi) và, khi córecord, hoạt động email xuất hiện trên timeline record (delivery: "activity") với file đính kèm đúng; ca hộp thư chưa grant nhậnEMAIL_SENDER_NOT_GRANTED, execution không có người dùng thiếuallowInternalSystembị từ chối; gọi lặp cùng key trảduplicate: true. - Cơ cấu tổ chức: gọi route production bằng session người dùng thật;
org.me()là nhân sự của người dùng sở hữu session, có thể khác nhân sự của Project key dùng ở local. Kiểm tra đủ các ca quyết định của contract: quản lý và người không phải quản lý (isManagerOftrue/false), nhân viên/phó phòng/trưởng phòng, nhân sự nhiều phòng ban có và không códepartmentId, nhân sự chưa có tài khoản vớiaccountOnly, chuỗi rỗng hoặc có phòng trống; job, webhook và trigger do hệ thống ghi đọc đượcorgkhi policy cóallowInternalSystem: true. Cần đổi phòng ban hoặc cấp quản lý của nhân sự test để có fixture thì làm qua$user-permissionvới xác nhận, snapshot và phục hồi sau test; chờ vài giây để thay đổi có hiệu lực.
- Background job:
- Frontend theo dạng đã xác nhận, trong browser đã đăng nhập Workspace (CLI publish không đăng nhập browser; link frontend yêu cầu session Workspace, không phải trang công khai vô danh). Sub-agent E2E chạy lại toàn bộ E2E trên URL đã deploy bằng phiên test theo Kiểm thử E2E frontend cho từng persona; E2E PASS trên Workspace là điều kiện nghiệm thu frontend, lỗi giao lại sub-agent phụ trách rồi chạy lại sau mỗi lần publish. Số liệu từ báo cáo: bằng phiên từng persona, giá trị hiển thị khớp service
200chạy trực tiếp cùng tham số, người ngoài ACL thấy trạng thái không có quyền, người có phạm vi record hẹp chỉ thấy số liệu của phạm vi đó, ghi thời gian tải với dữ liệu đại diện:- Single page app: mở
https://{WORKSPACE_DOMAIN}/{slugSlot}/index.html, xác minh asset/load/refresh và luồng nghiệp vụ với API thật. - Custom component: thêm hoặc cập nhật item Federation component trên layout đích với đường dẫn
{slugSlot}/Components/<Tên>khớp chữ hoa/thường với khóa expose; đặt qua$object-layout(mục "Đưa Federation component vào layout": componentfieldType: "federation_component", trườngfederationUrl) hoặc trình sửa layout, rồi view lại layout để xác minh. Mở form thật, chạy lại các ca ở bước 8, lưu và đọc lại record bằng$object-record; thay mọi URL localhost còn sót. - Federation Page: mở
https://{WORKSPACE_DOMAIN}/{APP_SLUG}/c{N}/{PATH}vớiNtừslugSlotvà App do người dùng chỉ định, đã xác minh qua$app-menu-manager; kiểm tra từng route, refresh, deep-link và quyền. Gắn vào menu ứng dụng bằng mục menu điều hướng tới trang trong Workspace với đường dẫn/{APP_SLUG}/c{N}/{PATH}; dùng API$app-menu-managerchỉ khi contract của skill đó mô tả loại hành động này, nếu không thì cấu hình trên giao diện rồi đọc lại menu. - Custom component và Federation Page: sau activate version mới, tải lại cứng, xác nhận không còn lỗi không tìm thấy remote module và asset tải từ
/{slugSlot}/assets/.
- Single page app: mở
- Xác minh quyền bằng caller phù hợp: session từ Workspace API key không bảo đảm cùng user với Project key và không giả lập được user tùy ý; chưa test user thường thì báo đúng giới hạn. Quy tắc theo Super Admin/role: kiểm thử người gọi là Super Admin, người có role được phép và người không có role (persona test của
$user-permissionhoặc session của đúng user đó; session tạo từ Workspace API key mang role của user sở hữu key, user đó là Super Admin thì không chứng minh được ca bị từ chối); sau khi gán/gỡ role, HTTP invocation phản ánh ngay còn record trigger và background job có thể chậm khoảng một phút; phục hồi role test tạm. Object do backend quản lý: phối hợp sub-agent Security kiểm chứng action trực tiếp bị từ chối, action được phép (ví dụ View) vẫn đúng phạm vi, đường backend hợp lệ thực hiện được thao tác hệ thống; kiểm thử cả caller không được phép gọi nghiệp vụ. Thành công dướidata.asSystem()không chứng minh caller có quyền CRUD trực tiếp. Quy tắc theo cơ cấu tổ chức (chỉ quản lý của người tạo được duyệt, chỉ thành viên phòng ban được thao tác): kiểm thử bằng session của người thoả điều kiện và của người không thoả, không chỉ bằng user sở hữu Workspace API key. Hoàn tất các ca runtime còn thiếu từ bước 2A trước khi tuyên bố quyền đã được kiểm thử end-to-end. - Hiệu năng (khi thuộc điều kiện của sub-agent Hiệu năng): sau khi kiểm thử chức năng ở mục 6–8 PASS, sub-agent Hiệu năng đo trên version đang active với dữ liệu đại diện và so với ngưỡng trong contract theo Kiểm thử hiệu năng; không chạy load test. Không đạt: sub-agent phụ trách sửa, publish/activate version mới với xác nhận theo mục 1 (commit mới qua review bảo mật phần chênh lệch), chạy lại ca chức năng bị ảnh hưởng rồi đo lại; tối đa 2 vòng sửa, hết 2 vòng vẫn không đạt thì dừng, báo người dùng số đo trước/sau và phương án, không tự nới ngưỡng. Code đổi sau khi đo thì đo lại ca liên quan.
- Process: kiểm chứng instance và hiệu ứng nghiệp vụ theo
$process-creator, phục hồi lịch và quyền test tạm. Module lỗi sau activate: ưu tiên activate lại version cũ còn hợp lệ trong phạm vi phục hồi và đọc lại xác nhận; rollback version không hoàn tác schema/record writes hay side effect. - Phục hồi cấu hình test tạm, xóa file session export do tác vụ tạo khi dùng xong; fixture Workspace xử lý theo skill dữ liệu và phạm vi đã được phép; báo rõ phần còn lại. Không tự xóa Project, schema hoặc Process để “cleanup”. Kiểm thử, spike hoặc backfill chạm số liệu do module hoặc backend khác duy trì: snapshot record tổng hợp bị ảnh hưởng trước và sau; không sửa tay số liệu đó để đưa về snapshot (record tổng sẽ lệch với tổng record chi tiết còn lại); chứng từ đang giữ số liệu, marker và phần dư không hoàn tác theo Dữ liệu kiểm thử của
$object-record.
10. Bàn giao
- Frontend: project/version đang active,
slugSlot, dạng đã xác nhận ở 1A, thư mục source và kết quả browser test. Single page app kèm link/{slugSlot}/index.html. Custom component kèm danh sách expose key, Object/layout đã gắn item Federation component và đường dẫn{slugSlot}/Components/<Tên>. Federation Page kèm URL từng route theo App, mục menu đã gắn và quyền hiển thị. Kèm kết quả E2E của sub-agent E2E: thư mục và lệnh chạy, môi trường/version, persona và nguồn phiên (không kèm credential), số test pass/fail/skip, bảng tiêu chí nghiệm thu → test → kết quả, ca chưa chạy được và lý do. - Backend: lệnh
cogover-dev auth session --format curl --output ...và cURL hoàn chỉnh tới production theo reference bàn giao, điền domain, slug, method, route, payload đã kiểm thử; không đưa cookie/token thật vào câu trả lời. Kèm response mong đợi và cách tạo lại session khi hết hạn. Route, trigger hoặc job xử lý nhiều dữ liệu: mức dùng ngân sách đo với dữ liệu đại diện (usagecủa preview hoặc job run) và điểm bàn giao sang job. Có record trigger: bảng trigger (key, Object, timing, operations,fields/changedFields/when/runWhen/writableFields, mã lỗi và message), version đang áp dụng, bằng chứng kiểm thử bằng thay đổi bản ghi thật cho từng ca và cách gỡ/rollback (activate version khác hoặc deactivate). Có push message: liệt kê nơi gọi, loại message, người nhận/excludevà bằng chứng quan sát trên trình duyệt. Có background job: bảng job (key, tên,timeoutMs,maxAttempts, lịch cron/múi giờ hoặc nơi enqueue, quy tắc idempotency key, định dạng payload), version áp dụng, bằng chứng run (run ID, trạng thái, số lần thử, hiệu ứng đã đọc lại) và cách dừng/rollback (bỏschedulehoặc activate version khác, deactivate). Có Custom Module Action: bảng action (key, label,exposeTo,effect,timeoutMs, input/output chính, danh tính chạy dự kiến, kết quả dự kiến trong output và mã lỗi), version đang áp dụng, Process/node và agent/tool đang dùng (ID, link), bằng chứng lượt chạy hoặc hội thoại cho từng ca, và cách gỡ an toàn (cập nhật node/tool trước, rồi bỏ khỏiactionshoặc deactivate). Có khởi chạy Process/AI Agent: nơi gọi, Process ID/agent ID, mụcprocesses/agentscủa policy đã duyệt cho version,runAs/approvalPolicy, job nhận kết quả, quy tắcidempotencyKey, bằng chứng lượt chạy và job; códeliverTothì thêm cách xác định người nhận,conversation, điều kiện gửi nêu tronginstructionvà bằng chứngdelivery. Có secret/credential: tên, loại, header/host được phép,valueVersion, ai giữ giá trị và cách xoay; không kèm giá trị. Có inbound access: inbound ID, name, mode, route prefix, URL gốc, hạn dùng, cách ký của bên gửi và cách rotate/revoke; không kèm inbound key hay HMAC secret. Có crypto: nơi gọi và thao tác (hash/HMAC, AES, RSA,sign/verify), thuật toán/mode/padding/định dạng chữ ký và encoding đã thống nhất với bên kia, tên secret chứa từng key, bên giữ private key/AES key và cách xoay (secrets setcùng tên, phối hợp thời điểm với bên kia); không kèm giá trị key. Có notification: nơi gọi, cách xác định người nhận (org, field record, danh sách cố định) vàexclude, channel (record ID, tên kênh), link, người gửi, quy tắcidempotencyKeyvà bằng chứng quan sát trên danh sách thông báo. Có email: nơi gọi, hộp thư gửi (ID, địa chỉ, dùng chung hay"actor") cùng mụcemailcủa policy đã duyệt cho version, người nhận hoặc field email, record ghi nhận timeline, file field đính kèm, quy tắcidempotencyKey, bằng chứng gửi (requestId,delivery, thư nhận hoặc hoạt động trên timeline). Có cơ cấu tổ chức: các hàmorgdùng và quyết định nghiệp vụ dựa trên chúng (ai được duyệt, phòng ban nào được xét,accountOnly,directOnly), cách xử lý chuỗi rỗng/phòng trống/truncated, policyallowInternalSystemcho execution không có người dùng và các ca đã kiểm thử. - Cả hai: hai mục trên cùng mapping frontend → backend, thư mục source và lệnh build/test/publish để bảo trì.
- Git: link repository (
webUrltrong fieldgit) và commit/tag cuối đã push của từng Project, hoặc lý do chưa lưu được; repository chứa tài liệu chung, nội dungdocs/vàe2e/của từng repository cùng lệnh chạy E2E; xác nhận không còn file dự án nằm ngoài thư mục Project. - Báo cáo: khi module dùng số liệu báo cáo, bàn giao theo mục Kiểm thử của Tận dụng báo cáo: saved report (ID, slug, link), Report Type, ACL, payload, nơi gọi và lý do, kết quả theo persona, thời gian tải, chỉ số vẫn quét record kèm lý do.
- Kiểm thử: báo cáo unit tests và line coverage của từng project theo bước 8, không gộp hai project thành một chỉ số; E2E báo riêng, không cộng vào coverage; nêu rõ sub-agent phụ trách backend/frontend/E2E/Review backend/Review bảo mật/Hiệu năng/Báo cáo/Process/AI Agent đã thực hiện phần nào. Kèm báo cáo review bảo mật theo mục Báo cáo của reference (phát hiện đã sửa, rủi ro được chấp nhận và người quyết định) và, khi thuộc điều kiện, báo cáo hiệu năng theo mục Báo cáo (ngưỡng, số đo, các vòng sửa). Backend ghi dữ liệu kèm báo cáo review và kết quả kiểm thử end-to-end theo Báo cáo của reference.
- Security: ma trận quyền, mapping bốn slot rule của từng Object mới và rules bổ sung nếu có, action chỉ backend được thực hiện, quy tắc nghiệp vụ theo Super Admin/role trong code (role ID, tên và thao tác áp dụng) và theo cơ cấu tổ chức (quản lý, phòng ban được xét), bằng chứng đọc lại/nghiệm thu của sub-agent Security, kết quả runtime cùng giới hạn còn lại. Phân biệt cấu hình đã xác minh với quyền đã kiểm thử end-to-end.
- Kèm phần schema đã tạo/tái sử dụng, workbook đã duyệt nếu có, Process/link/instance nếu có, policy/key ở mức metadata và bằng chứng kiểm thử. Báo
PASS,PARTIAL,FAILhoặcBLOCKEDtheo kết quả quan sát; liệt kê thiếu quyền/dependency/fixture còn lại. Chỉ gọi hoàn tất khi local và Workspace test đáp ứng tiêu chí của bài toán.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

