Giao diện
Endpoints
Tất cả đường dẫn /v1/* dưới đây yêu cầu danh tính Cloudflare Access hợp lệ. Một Worker phục vụ cả ba mặt phẳng của Conan1. Các đường công khai (/public/b21/*, /mm/*, /public/mm/*) nằm ở cuối trang.
Danh tính
GET /v1/me
json
{ "email": "dac2205@gmail.com", "name": "Đắc", "role": "architect", "brand_id": null }Năng lực chuẩn
Chỉ vai trò architect được ghi.
| Method | Path | Mô tả |
|---|---|---|
GET | /v1/capabilities | Toàn bộ mô hình chuẩn, sắp xếp theo level và sort |
GET | /v1/capabilities/:idOrCode | Một năng lực, kèm con và đánh giá của các Brand |
POST | /v1/capabilities | Tạo mới; ID sinh tự động từ ID cha |
PATCH | /v1/capabilities/:id | Sửa; chỉ những cột được gửi mới bị đổi |
DELETE | /v1/capabilities/:id | Xoá, hoặc chuyển sang deprecated nếu đã có Brand đánh giá |
POST body:
json
{
"name_vi": "Thu hút tự nhiên",
"name_en": "Organic Acquisition",
"definition_vi": "Khả năng ...",
"definition_en": "The ability to ...",
"parent_id": "CAP-02",
"level": 2,
"sort": 1
}ID sinh theo cây: con thứ nhất của CAP-02 là CAP-02-01. Nhìn ID là biết vị trí trong mô hình.
Rule 8
DELETE không bao giờ xoá một năng lực đã được Brand đánh giá. Nó chuyển status thành deprecated và trả về { "deprecated": true, "reason": "in_use_by_brands" }. Lịch sử không bị mất.
Brand
| Method | Path | Mô tả |
|---|---|---|
GET | /v1/brands | Danh sách Brand |
POST | /v1/brands | Tạo Brand (chỉ architect) |
PATCH | /v1/brands/:code | Sửa Brand |
Bản đồ năng lực của Brand
| Method | Path | Mô tả |
|---|---|---|
GET | /v1/brands/:code/capabilities | Mọi năng lực chuẩn, kèm đánh giá của Brand này (assessment_id là null nếu chưa kích hoạt) |
PUT | /v1/brands/:code/capabilities/:capabilityId | Kích hoạt hoặc cập nhật đánh giá |
DELETE | /v1/brands/:code/capabilities/:capabilityId | Bỏ kích hoạt |
PUT với body rỗng {} nghĩa là "kích hoạt, chưa chấm điểm". Body có thể chứa bất kỳ tập con nào của:
json
{
"importance": 5, "priority": 5, "health": 3,
"maturity": 2, "target_maturity": 4,
"likelihood": 4, "impact": 5, "detectability": 2,
"owner": "Đắc", "rationale": "...", "notes": "..."
}Quyền ghi: architect và executive ghi được mọi Brand; brand_owner chỉ ghi được Brand của mình.
So sánh
GET /v1/compare?capability=learner-acquisition
Trả về cùng một năng lực dưới góc nhìn của mọi Brand đang hoạt động, kể cả Brand chưa kích hoạt (các trường điểm là null).
Gói kiến trúc
GET /v1/architecture
Một request trả về toàn bộ tầng thực thi, chuyển đổi và hai mặt phẳng Products, Requirements. Frontend gọi đúng một lần khi khởi động rồi tính mọi màn hình tại client.
| Khoá | Bảng |
|---|---|
valueStreams, stages, stageCapabilities | Value stream và chặng |
processes, activities | Quy trình, hoạt động |
applications, dataEntities, technologies | Ứng dụng (kèm product_id, repository), dữ liệu, công nghệ |
capabilityApplications, capabilityData, applicationTechnologies, applicationData | Bảng nối của Compass |
initiatives, initiativeCapabilities, kpis | Chuyển đổi |
objectives, keyResults | OKR — Objective và Key Result (xem OKR) |
products, productCapabilities, applicationUrls, features | Products |
requirements, questions, decisions, decisionLinks, evidence | Requirements |
acceptanceCriteria, requirementDependencies | Tiêu chí chấp nhận và phụ thuộc giữa các yêu cầu |
releases, releaseRequirements | Release |
ai_suggestions và ai_runs không nằm trong gói — xem GET /v1/requirements/:id/suggestions.
CRUD chung
Các tài nguyên sau dùng chung một bộ endpoint, chỉ khác nhau ở cột (năm tài nguyên của Chiến lược — strategies, strategic-themes, strategic-objectives, annual-priorities, quarterly-focus — cũng dùng đúng bộ này):
GET /v1/:resource
POST /v1/:resource
PATCH /v1/:resource/:id
DELETE /v1/:resource/:id:resource | Bảng | Prefix id | Phạm vi | Ghi chú |
|---|---|---|---|---|
value-streams | value_streams | VS | brand | |
value-stream-stages | value_stream_stages | VSS | model | |
processes | processes | PRC | brand | |
activities | activities | ACT | brand | |
applications | applications | APP | model | cột thêm: product_id, repository |
data-entities | data_entities | DAT | model | |
technologies | technologies | TEC | model | |
initiatives | initiatives | INI | brand | |
objectives | objectives | OKR | brand | code, capability_id, stage_id, strategic_objective_id, owner, period, status. Đặt strategic_objective_id cần architect hoặc executive — nó quyết định màn hình Chiến lược hiện gì |
key-results | key_results | KR | brand | code, objective_id, kpi_id, unit, direction, baseline, current, target, status; không có description_*. Ghi hoặc xoá còn cần quyền ghi trên Objective cha |
products | products | PRO | brand | code, brand_id, proposition_*, domain_*, owner, kind, status |
application-urls | application_urls | URL | model | code, application_id, url, kind |
features | features | FEA | brand | brand_id tự điền từ product_id |
requirements | requirements | REQ-NNNN | model | id từ sequences; brand_id tự điền từ product_id |
questions | questions | Q-NNNN | model | chỉ name_*, description_*, draft_answer_*, requirement_id, sort |
decisions | decisions | D-NNNN | model | chỉ name_*, description_*, rationale_*, product_id, capability_id, application_id, feature_id. status và supersedes_id không ghi được ở đây: trạng thái của bộ nhớ quyết định chỉ đổi qua POST /v1/decisions (thay thế ngay lúc tạo) và DELETE /v1/decisions/:id (thu hồi) |
evidence | evidence | EVI-NNNN | model | requirement_id, kind, url, status; brand_id tự điền từ yêu cầu |
acceptance-criteria | acceptance_criteria | AC-NNNN | model | requirement_id, evidence_id, status (pending · met · failed); brand_id tự điền từ yêu cầu |
releases | releases | REL-NNNN | model | application_id, version, released_at, status; brand_id tự điền từ ứng dụng |
name_envàname_vibắt buộc khi tạo.- ID sinh từ prefix cộng slug (
PRC-develop-authentic-content), trừ các tài nguyên cósequence:REQ-,Q-,D-,EVI-,AC-,REL-nhận số tuần tự bốn chữ số từ bảngsequences. PATCHchỉ ghi các cột được gửi lên. Cột không có trong danh sách bị bỏ qua lặng lẽ.- Với tài nguyên có
brandFrom, Worker nạp bản ghi cha (400nếu thiếu cột cha,404nếu không tồn tại) và điềnbrand_idtrước khi kiểm tra quyền; gửibrand_idkhác cha →400. key-resultscố tình không dùngbrandFrom: một Key Result là con số của một Brand nằm dưới một Objective cấp doanh nghiệp, nên brand của nó được phép khác brand của cha. Đổi lại, Worker kiểm tra quyền hai lần — trên Brand của chính bản ghi và trên Brand của Objective cha (parentGate), ở cảPOST,PATCH(cả hai đầu khi đổi cha) vàDELETE. Nhờ vậybrand_ownerkhông gắn được Key Result vào Objective không thuộc về mình.kpiskhông còn là tài nguyên CRUD. Toàn bộ KPI đã thành Key Result (0015/0016); để hai bảng cùng ghi được thì con số sẽ lệch nhau ngay lần đầu có người nhập vào bảng cũ. KPI vẫn được trả về trong gói/v1/architecturelàm hồ sơ nguồn gốc (key_results.kpi_id).
Cột chỉ route riêng mới ghi được
final_answer_*, selected_suggestion_id, answered_by/at, status, decision_id của questions và status, supersedes_id, decided_by, decided_by_kind, decided_at, ai_analysis_*, ai_run_id, requirement_id, question_id của decisions không có trong danh sách cột chung. Một PATCH chung mà ghi được status sẽ hồi sinh được một quyết định đã thu hồi; ghi được supersedes_id sẽ thay thế một quyết định mà không có quyết định kế nhiệm. Chúng chỉ được ghi bởi các route ở mục Requirements bên dưới, luôn đóng dấu email từ Access.
Quyền ghi:
| Tài nguyên | Ai được ghi |
|---|---|
applications, data-entities, technologies, value-stream-stages, application-urls | architect |
processes, activities, value-streams, initiatives, objectives, key-results, products, features | architect, executive, hoặc brand_owner với bản ghi thuộc Brand của mình |
requirements, questions, decisions, evidence, acceptance-criteria, releases | chỉ architect |
WARNING
Bản ghi có brand_id = NULL là bản ghi dùng chung (với sản phẩm: nền tảng Conan1). brand_owner không sửa được chúng — chỉ architect hoặc executive. Đổi brand_id sang Brand khác cũng cần quyền ghi ở Brand đích.
Xoá có lịch sử
DELETE | Kết quả khi bản ghi đã có lịch sử |
|---|---|
/v1/decisions/:id | Luôn status = reverted, trả { "deprecated": true, "reason": "decision_memory" } |
/v1/requirements/:id | Xoá thật chỉ khi draft và không có câu hỏi / quyết định / bằng chứng; ngược lại dropped, { "deprecated": true, "reason": "has_history" } |
/v1/questions/:id | Xoá thật chỉ khi open / suggested và chưa có decision_id; ngược lại dropped |
/v1/products/:id | Có yêu cầu → retired, { "deprecated": true, "reason": "has_requirements" } |
Quan hệ
POST /v1/links/:link body: { "a": "...", "b": "..." }
DELETE /v1/links/:link?a=...&b=...:link | a | b |
|---|---|---|
stage-capabilities | stage_id | capability_id |
capability-applications | capability_id | application_id |
capability-data | capability_id | data_entity_id |
application-technologies | application_id | technology_id |
application-data | application_id | data_entity_id |
initiative-capabilities | initiative_id | capability_id |
product-capabilities | product_id | capability_id |
release-requirements | release_id | requirement_id |
requirement-dependencies | requirement_id | depends_on_id — a chờ b |
POST dùng INSERT OR IGNORE, nên gọi lại một quan hệ đã có không gây lỗi. Chỉ architect và executive được đổi quan hệ; riêng release-requirements và requirement-dependencies thuộc mặt phẳng Requirements nên chỉ architect (403 với executive). Quan hệ không có tên trong bảng trên trả 404.
201 phải nghĩa là đã ghi thật
INSERT OR IGNORE nuốt luôn cả vi phạm CHECK và khoá ngoại, chứ không chỉ dòng trùng: một liên kết tới id không tồn tại, hay một yêu cầu phụ thuộc chính nó, từng trả 201 mà không ghi gì. Nay Worker đọc lại đúng cặp đó sau khi chèn; không thấy thì trả 400:
json
{ "error": "Link refused: check that both records exist and that they are not the same record" }Áp dụng cho mọi quan hệ trong bảng trên, không riêng quan hệ mới. Gọi lại một cặp đã có vẫn là 201 — nó đọc lại được.
Requirements
Mọi route dưới đây yêu cầu architect (trừ hai GET, mở cho mọi người đã đăng nhập). Mỗi thao tác ghi tạo dòng audit_log và trả về bản ghi mới.
| Method | Path | Mô tả |
|---|---|---|
POST | /v1/questions | Tạo câu hỏi (route riêng, cùng URL với CRUD chung): cấp Q-NNNN, điền brand_id, chuyển yêu cầu draft → clarifying |
POST | /v1/questions/:id/answer | { selected_suggestion_id?, final_answer_en, final_answer_vi } — ghi câu trả lời cuối, đóng dấu answered_by/at, đánh dấu gợi ý selected / rejected, status = answered |
POST | /v1/questions/:id/decide | { name_*, rationale_*, description_*? } — chỉ khi answered; sinh D-NNNN với decided_by, decided_by_kind, ai_analysis_*, neo từ chuỗi truy vết, dải decision_links; câu hỏi decided. Trả { ...decision, links } |
POST | /v1/decisions | Quyết định độc lập: name_*, rationale_*, description_*?, product_id?, capability_id?, application_id?, feature_id?, links?: [{ entity, entity_id }] — mỗi mục tiêu được kiểm tra tồn tại (400 Unknown link target) |
POST | /v1/decisions/:id/links | { entity, entity_id } — thêm vào dải Liên quan |
DELETE | /v1/decisions/:id/links?entity=&entity_id= | Bỏ khỏi dải Liên quan |
GET | /v1/requirements/:id/suggestions | { items: ai_suggestions[], runs: ai_runs[] } của một yêu cầu |
GET | /v1/requirements/:id/trace | Chuỗi truy vết đã giải: { requirement, feature, product, brand, application, capability, initiative } |
entity của dải Liên quan là một trong requirement · question · feature · product · application · capability · initiative.
AI
Gợi ý sinh bởi Workers AI (DeepSeek) qua AI Gateway conan1 — không cần khoá API. Chi tiết ở Làm rõ có AI hỗ trợ.
| Method | Path | Mô tả |
|---|---|---|
GET | /v1/ai/status | { "enabled": true, "model": "@cf/deepseek-ai/deepseek-v4-flash-0731", "gateway": "conan1", "limit": 50, "used": 7 } — enabled là có binding AI hay không; limit là AI_DAILY_LIMIT (0 = không giới hạn), used là số dòng ai_runs của ngày UTC hôm nay (luôn 0 khi enabled = false) |
POST | /v1/requirements/:id/suggest-questions | Tối đa 5 gợi ý câu hỏi (kind = question). Trả 201 { items, run } |
POST | /v1/questions/:id/suggest-answers | Đúng 3 phương án A / B / C, một recommended; câu hỏi open → suggested. Trả 201 { items, run } |
POST | /v1/suggestions/:id/accept | Biến một gợi ý câu hỏi proposed thành câu hỏi thật (source_suggestion_id), gợi ý → accepted. Trả 201 câu hỏi |
Lỗi:
| Mã | error | Khi nào | Ghi ai_runs |
|---|---|---|---|
503 | ai_not_configured | Không có binding AI | không |
429 | ai_daily_limit | Đã dùng hết AI_DAILY_LIMIT lượt của ngày UTC hôm nay; body kèm used, limit, message_en, message_vi | không |
429 | ai_rate_limited | Workers AI báo giới hạn / hạn mức | failed |
502 | ai_failed | Gọi thất bại vì lý do khác | failed |
502 | ai_no_answer | JSON không đọc được, sai số phương án, sai nhãn | failed |
Quản trị
GET /v1/governance
Kiểm tra toàn bộ dữ liệu theo các quy tắc quản trị và trả về danh sách vi phạm. brand_code rỗng nghĩa là bản ghi dùng chung, không thuộc Brand nào:
json
{
"checked": 51,
"issues": [
{
"rule": "rule-5-owner-required",
"brand_code": "nemo12",
"capability_id": "CAP-06",
"capability_code": "assessment",
"detail": "Importance >= 4 but no owner assigned"
},
{
"rule": "rule-9-requirement-capability",
"brand_code": "nemo12",
"capability_id": "",
"capability_code": "",
"detail": "REQ-1835 · Parents can export progress"
}
]
}Rule id: rule-2-definition-required, rule-5-owner-required, rule-6-p5-rationale, rule-7-process-owner, rule-9-requirement-capability, rule-10-decision-rationale, rule-11-product-capability; cảnh báo: target-maturity-missing, capability-without-process, capability-without-application, các cảnh báo về chặng, feature-brand-mismatch, application-without-product. Xem Quy tắc quản trị.
Nhật ký
GET /v1/audit?entity=capability&id=CAP-02&limit=50
Mọi thay đổi đều lưu cả trạng thái trước và sau dưới dạng JSON. Trang chi tiết quyết định dùng entity=decisions&id=D-0182 để hiện lịch sử của chính nó; quan hệ dùng entity=link:<tên> với id a|b.
Ngoài Access: mm.conan1.com
Markmap dùng đăng nhập Google riêng (bảng mm_users, mm_sessions), không dùng Access. Trình duyệt gọi qua https://mm.conan1.com/api/... (Worker của mm chuyển tiếp qua service binding), nên cookie mm_session là first-party. Khuôn trả về là { "data": ... } / { "error": { "code", "message" } }. Nguồn: src/routes/mm.ts.
| Đường | Phiên | Ý nghĩa |
|---|---|---|
GET /mm/config | không | { google_client_id } cho nút Google |
POST /mm/auth/google | không | { credential } (ID token của Google) → đặt cookie, trả { user_id, email, display_name, avatar_url } |
POST /mm/auth/logout | tuỳ | Thu hồi phiên, xoá cookie |
GET /mm/me | có | Người đang đăng nhập; 401 nếu chưa |
GET /mm/markmaps | có | { items, limit: 10 }, mới sửa trước |
GET /mm/markmaps/:id | có | Một sơ đồ của mình; của người khác trả 404 như không tồn tại |
POST /mm/markmaps | có | { title, content } → tạo; đủ 10 bản thì 409 |
PUT /mm/markmaps/:id | có | Sửa tiêu đề và nội dung |
PUT /mm/markmaps/:id/visibility | có | { public: boolean } bật/tắt share |
GET /public/mm/markmaps?q= | không | Tối đa 500 sơ đồ đang share; không có danh tính người viết |
GET /public/mm/markmaps/:id | không | Một sơ đồ đang share; chưa/không còn share thì 404 |
Không có đường xoá sơ đồ (giữ nguyên hành vi của Nemo12).