hango: agent harness multi-tenant viết bằng Go
Tổng quan
hango là một server agent harness multi-tenant viết từ đầu bằng Go: xác thực bằng API key, run bền vững (durable), streaming qua SSE, và các tính năng harness ghép từ middleware (lập kế hoạch, filesystem ảo, subagent, tóm tắt, memory, skill, human-in-the-loop). Dự án được xây từng tính năng một như một lộ trình học, và trạng thái cuối là sẵn sàng cho production. Cuối mỗi giai đoạn luôn có một binary build, vet và test đều xanh, có ít nhất một integration test chạy trên Postgres thật nếu giai đoạn đó đụng đến store, cùng một tài liệu học tại docs/learning/NN-<slug>.md so sánh lựa chọn của hango với goclaw và langchain deepagents, kèm bản tiếng Việt tại docs/learning/NN-<slug>.vi.md.
Các quyết định đã chốt
- Go 1.27,
net/httpcủa thư viện chuẩn (http.ServeMuxcộng một router nhỏ tronginternal/server, không dùng web framework), chỉ dùng SSE (không WebSocket). Giới hạn package là 20 cộnginternal/tool/builtin, liệt kê chính xác:cmd/hango,migrations, vàinternal/{app,config,server,auth,store,store/pg,provider,provider/anthropic,provider/openai,agent,tool,tool/builtin,run,fs,usage,telemetry,safego,telegram,claudecli}.scripts/không tính vì không chứa code import được. Các package helper chỉ dùng cho test, nhưinternal/store/pg/pgtest, không tính vào giới hạn này. Muốn thêm package thứ hai mươi mốt thì phải sửa dòng này trước khi viết code. - Id là UUID v7 từ package
uuidcủa thư viện chuẩn Go 1.27 (uuid.NewV7);uuid.Nillà một hàm và luôn được gọi, không bao giờ so sánh như một giá trị. - Hướng phụ thuộc là một chiều:
internal/agentkhông import gì từinternal/run.agentđịnh nghĩaEventSinkvàGate;runcài đặt chúng và composition root nối lại với nhau. Thiết kế nào cần chiều ngược lại là thiết kế sai. - Việc thực thi run có chủ sở hữu, có lease và có thể tiếp tục. Worker chỉ bắt đầu một run qua một conditional claim (
UPDATE ... WHERE state='queued' RETURNING) đặtowner_idvàlease_expires_at; mọi chuyển trạng thái đều có điều kiện theo state và owner; reconciliation chỉ thu hồi các lease đã hết hạn và đưa run về hàng đợi tại ranh giới lượt (turn). Khi restart và drain thì requeue, không đánh dấu thất bại. pause_turnkhông thể xảy ra, nên nó là một lỗi. hango không đăng ký server tool nào của Anthropic, vì vậypause_turntrả vềErrUnsupportedStop. Không có bộ đếm resume ở bất kỳ đâu.anthropic-sdk-gov1.74 nằm sauprovider.Providerdo hango tự định nghĩa; mô hìnhMessage/Block/Eventđã chuẩn hóa được thiết kế để adapter OpenAI có thể tự tổng hợp block start/stop.- Chỉ dùng PostgreSQL qua pgx v5, migration nhúng bằng goose. Từ phase 2.1, query được sqlc sinh ra từ các file SQL; pgx viết tay chỉ được phép cho một query sqlc không biểu đạt được, kèm một comment nói rõ vì sao. Không SQLite, không ORM. Message là các dòng append-only
(session_id, seq, role, jsonb blocks). - Tenancy logic:
tenant_id uuid NOT NULLtrên mọi bảng thuộc về tenant, index bắt đầu bằng tenant, scoped store dựng từ context và fail closed (lỗi thì từ chối), có test đọc chéo tenant cho từng bảng ngay từ bảng đầu tiên. RLS là lớp bảo vệ đến muộn, không bao giờ thay thế được các lớp kia, và việc bật nó ở phase 16 là một đợt refactor tường minh sangWithTenantTxcùng một system pool nhỏ cóBYPASSRLScho năm đường đi xảy ra trước khi có tenant scope. - Khả năng nhìn thấy dữ liệu trong cùng tenant: một
memberchỉ thấy và chỉ sửa được những gì mình tạo;ownervàadminthấy và sửa được mọi thứ trong tenant của họ. Được thực thi bằng ma trận phân quyền theo từng route cùng một predicatevisibility(scope)dùng chung ở store, và được test từng route, từng bảng. - hango tự sở hữu danh tính: tenant, user thuộc tenant (owner/admin/member), api_keys thuộc về một user (hash SHA-256 và prefix để tra cứu). Không có mật khẩu, không có giao diện đăng nhập, bootstrap admin key lấy từ config.
- Run là các dòng bền vững, được thực thi bởi một scheduler lane worker chạy trong tiến trình; SSE subscribe vào event log đã lưu và hỗ trợ tiếp tục bằng
Last-Event-ID; có endpoint cancel; có graceful drain. Phiên bản v1 chạy một replica (rate limiter và run registry nằm trong tiến trình, có ghi lại trong tài liệu). - Kiến trúc middleware: một slice có thứ tự, điều khiển bằng dữ liệu, với
BeforeRun,WrapModelCall,WrapToolCall,ToolProvider,PromptContributor. Mọi tính năng harness sau phase 7 đều là một middleware. System prompt được ghép bên trong chain tại thời điểm gọi model thành[]provider.Blocktừ các contributor theo thứ tự trong stack, nhờ vậy cache breakpoint và thứ tự memory có thể diễn đạt bằng chỉ số block. State của middleware làmap[string]json.RawMessagedo chính middleware đó marshal, nên nó sống sót qua persistence và resume.Tooltrả vềResult{ForLLM, ForUser, IsError}; lỗi của tool trở thành tool result, không bao giờ panic vào loop; có phát hiện gọi lặp. Không có system prompt gốc do hango tự viết: chính sách nằm trong mô tả tool, system prompt giữ ngắn và động. - Audit mọi thứ: mọi API request có thay đổi dữ liệu và mọi quyết định approval đều ghi một dòng `audit_events` append-only mang theo request id, trace id và span id; các dòng audit, run event, span và log nối với nhau qua `trace_id`.
- Observability stack: OTel trong tiến trình (một TracerProvider/MeterProvider/LoggerProvider,
otelsloglàm handlerslogmặc định) → một đường xuất OTLP duy nhất tới OpenTelemetry Collector → fan-out sang Grafana LGTM (Tempo/Loki/Prometheus) và Langfuse Cloud; tên attribute GenAI vàlangfuse.*là các hằng số cục bộ của hango, nội dung prompt mặc định tắt và bị collector loại bỏ. Observability là yêu cầu nền tảng: nó ra mắt ở phase 1 và mọi phase đều tự gắn instrumentation, để bất kỳ lỗi nào cũng định vị được theo chuỗi tenant → agent → session → run → turn → model call → tool call.
Mục tiêu
| # | Mục tiêu | Ưu tiên |
|---|---|---|
| 1 | Server harness multi-tenant sẵn sàng cho production, một binary duy nhất, chạy bằng docker compose up; truy cập chéo tenant là bất khả thi do cách thiết kế, được chứng minh theo từng bảng | P1 |
| 2 | Run bền vững, hủy được, tiếp tục được, có streaming SSE; tầng provider abstraction được chứng minh bằng adapter thứ hai (tương thích OpenAI) | P1 |
| 3 | Các tính năng harness ghép từ middleware: lập kế hoạch, filesystem, subagent, quản lý context, memory, skill, HITL | P1 |
| 4 | Mọi tầng đều quan sát được và mọi lỗi đều định vị được trong Grafana và Langfuse; có thống kê usage và quota | P1 |
| 5 | Một tài liệu học cho mỗi giai đoạn: khái niệm, hango so với goclaw và deepagents, cách debug tầng đó | P1 |
Các giai đoạn
Effort được ghi dưới dạng phần cài đặt cộng tài liệu học, vì tài liệu học là một sản phẩm bàn giao của mọi giai đoạn, không phải việc làm thêm cho có.
| # | Giai đoạn | Impl | Doc | Effort | Phụ thuộc |
|---|---|---|---|---|---|
| 1 | Nền tảng, khung server, observability spine | 15h | 2h | 17h | — |
| 2 | Persistence và lõi tenancy | 16h | 2h | 18h | 1 |
| 2.1 | Chuyển store sang sqlc | 6h | 1h | 7h | 2 |
| 3 | Provider abstraction và adapter Anthropic | 12h | 3h | 15h | 1 |
| 4 | Agent, session, message | 10h | 2h | 12h | 2, 2.1 |
| 5 | Tool, agent loop, run bền vững | 17h | 3h | 20h | 3, 4 |
| 6 | Streaming, hủy run, drain | 14h | 2h | 16h | 5 |
| 7 | Middleware chain và tool lập kế hoạch | 11h | 2h | 13h | 6 |
| 8 | Filesystem backend và offload kết quả lớn | 13h | 2h | 15h | 7 |
| 9 | Subagent | 10h | 2h | 12h | 8 |
| 10 | Quản lý context và tóm tắt | 11h | 2h | 13h | 8 |
| 11 | Memory dài hạn và skill | 7h | 2h | 9h | 9, 10 |
| 12 | Provider tương thích OpenAI và provider registry | 8h | 2h | 10h | 5, 8, 10 |
| 13 | Usage, quota, rate limit | 9h | 2h | 11h | 9, 10, 12 |
| 14 | Dashboard, alert, runbook debug | 10h | 2h | 12h | 13, 18, 19 |
| 15 | Human-in-the-loop | 12h | 2h | 14h | 9, 14 |
| 16 | Làm cứng cho production và lớp bảo vệ RLS | 13h | 3h | 16h | 15 |
| 17 | Vòng đánh giá (evaluation loop) | 8h | 2h | 10h | 16 |
| 18 | Kênh Telegram | 15h | 2h | 17h | 6, 13 |
| 19 | Runtime Claude Code CLI cho owner | 12h | 2h | 14h | 6, 8, 13 |
| 20 | Telegram: media, pairing và approval | 23h | 3h | 26h | 15, 16, 18 |
| Tổng | 252h | 45h | 297h |
Thứ tự xây dựng
Build order: 1 → 3 → 2 → 2.1 → 4 → 5 → 6 → 7 → 8 → 9 → 10 → 11 → 12 → 13 → 18 → 19 → 14 → 15 → 16 → 20 → 17.Phase 3 chỉ phụ thuộc vào phase 1, nên nó chạy trước phase 2. Nhờ vậy một model thật stream vào Tempo và Langfuse ở mốc khoảng 31h thay vì 49h, và observability spine có thứ đáng xem từ sớm. Số phase, phạm vi và phụ thuộc không đổi. Phase 18 và 19 chạy ngay sau phase 13, để gia đình có thể dùng hango qua Telegram trước khi làm dashboard, approval và hardening; cả hai đều không thêm migration, nên các migration 00001–00011 giữ nguyên số. Phase 20 chạy sau phase 16, nên migration 00012_telegram.sql của nó đứng sau migration RLS và tạo các bảng với row-level security ngay từ đầu, và các eval của phase 17 có thể có một task về hình ảnh. build_plan_html.go đọc dòng này, và mục “Next up” của trang đi theo nó.
Phase 2.1 được thêm sau khi phase 2 đã merge (issue #26). Nó chuyển store của phase 2 sang sqlc trước khi phase 4 sao chép pattern của nó, và không thêm migration nào, nên không số migration nào đổi.
Phase 5 dừng lại hai lần để review: 5a (lõi harness: tool và loop chạy với provider.Fake) và 5b (độ bền: run, claim, lease, reclaim, worker, endpoint). File của phase ghi rõ bước và tiêu chí nào thuộc về checkpoint nào.
Tiêu chí nghiệm thu
- Server khởi động từ config, tắt êm (graceful shutdown), có health endpoint.
- Tenant được xác định từ API key; mọi lời gọi store và tool đều bị giới hạn trong tenant. Truy cập chéo tenant là bất khả thi do cách thiết kế, được test bao phủ.
- Người dùng trong một tenant có thể tạo và chạy agent.
- Agent loop có gọi tool và streaming qua HTTP.
- Session và message được lưu bền vững.
- Subagent, tool todo/lập kế hoạch, filesystem ảo theo từng tenant, memory dài hạn.
- Trace và log có cấu trúc của OpenTelemetry, đối chiếu được với nhau trong Grafana và Langfuse.
-
go test ./...xanh,go vetsạch. -
docker compose updựng lên server và database. -
hango eval runchấm điểm một bộ task cố định qua các run thật, đẩy điểm lên Langfuse, vàhango eval comparecho thấy một thay đổi có cải thiện được kết quả hay không.
Tài liệu học
Mỗi giai đoạn viết docs/learning/NN-<slug>.md (khái niệm, lựa chọn thiết kế, đánh đổi, cách goclaw và deepagents làm cùng việc đó, vì sao hango đi khác, cách debug tầng đó) và cập nhật docs/learning/README.md cùng docs/system-architecture.md. Mọi tài liệu dưới docs/ đều bắt đầu bằng front matter có title và weight (weight của một tài liệu học là số phase của nó nhân 10) để site Hugo trong site/ sắp xếp được; xem docs/docs-site.md.
Site có hai ngôn ngữ. Mỗi tài liệu học đi kèm một bản tiếng Việt docs/learning/NN-<slug>.vi.md trong cùng pull request, và mỗi bài nhật ký trong plans/journals/ cũng vậy. ak journal create chỉ viết tiếng Anh, nên file .vi.md được viết tay sau đó.
Các việc tiếp theo ngoài phạm vi
- Tự mang provider credential theo từng tenant (BYO) (hoãn từ phase 12). Các yêu cầu mà công việc này phải đáp ứng: một bảng
provider_credentialsvới ciphertext AES-GCM mà AAD gắn với(tenant_id, provider)để một dòng không thể bị phát lại sang tenant khác; một key id cho mỗi dòng với khóa primary/secondary để rotate khóa; resolve qua seamCredentialLookuphiện có và không cache trong bộ nhớ các secret đã giải mã; allowlist chống SSRF cộng việc từ chối IP riêng sau khi resolve đối vớibase_urldo tenant cung cấp; response chỉ chứa metadata; chỉowner/adminđược dùng. Credential của server do operator đặt vẫn là đường mặc định. - Server tool của Anthropic và việc truyền nguyên block thô (opaque raw-block passthrough), hai thứ này cùng nhau mới làm cho
pause_turncó thể xảy ra và tiếp tục được. Cho đến lúc đópause_turnlàErrUnsupportedStop. - LLM gateway với quota theo từng user. Một nơi duy nhất nắm provider credential, định tuyến mỗi lời gọi tới một provider, và áp quota theo từng user chứ không chỉ theo tenant, để các thành viên trong gia đình không dùng hết ngân sách của nhau và có thể thêm provider mà không đụng vào agent loop. Nó xây trên registry của phase 12 và các bản ghi usage của phase 13. Cho đến lúc đó cả gia đình dùng chung một quota của tenant.
- MCP bridge (MCP server được phơi ra thành các cài đặt của
tool.Tool); deploy nhiều replica (rate limiter dùng chung, fan-out event; lưu ý lease sở hữu run đã an toàn cho nhiều replica); tool thực thi trong sandbox (backendSandboxcóExecute); thực thi tool song song trong một batch; các kênh chat khác ngoài Telegram, chat nhóm Telegram (thiết kế goclaw được giữ trong phase 20, với giới hạn lịch sử cấu hình được), Telegram webhook mode và multi-bot, giao diện web chat (dự kiến làm sau Telegram) và một trang web admin, cả hai viết bằng Svelte 5 và nói chuyện với cùng HTTP API, ứng dụng desktop, i18n, hơn 20 provider. - Context inspector (spec), làm sau trang web admin. Hiển thị request model kế tiếp của một session thành các phần có gắn nhãn kèm số token, lưu các override theo agent và theo session để loại bỏ hoặc thay thế từng phần, và chạy các lệnh gọi thử một lần. Giao diện chính là một bảng checkbox trên trang web admin, kèm CLI
hango contextdùng cùng API.
Red Team Review
Bốn đợt review đối kháng (security adversary, failure-mode analyst, assumption destroyer, scope and complexity critic) đưa ra 41 phát hiện đối với 16 file phase, được gom lại bên dưới thành 15 nhóm chủ đề cùng bốn quyết định do người dùng trực tiếp đưa ra. Mọi nhóm đều được chấp nhận. Báo cáo: plans/reports/redteam-260922-0126-{security-adversary,failure-mode-analyst,assumption-destroyer,scope-complexity-critic}.md.
| # | Phát hiện | Mức độ | Xử lý | Áp dụng cho |
|---|---|---|---|---|
| D1 | BYO provider credential theo từng tenant (bảng, AES-GCM, rotate khóa, 3 endpoint) là phạm vi vượt ngoài hợp đồng và là bề mặt bảo mật không giới hạn | Cao | Chấp nhận (quyết định của người dùng) | phase 12 (cắt phạm vi còn seam CredentialLookup trên config của operator, base URL được kiểm tra là https hoặc loopback), phase 16 (danh sách bảng RLS, hướng dẫn deploy), mục follow-up của plan.md |
| D2 | Không có mô hình phân quyền trong cùng tenant: một member có thể đọc và phá hủy dữ liệu của mọi user khác | Nghiêm trọng | Chấp nhận (quyết định của người dùng) | phase 2 (ma trận route, các case test trong cùng tenant), phase 4 (sessions.created_by, ma trận agent/session/message, trả 404 chứ không phải 403), phase 5 và 6 (run và event), phase 8 (files API), phase 15 (approval) |
| D3 | Restart hoặc drain khi đang running làm mất việc hoặc thực thi run hai lần; không có lease sở hữu | Nghiêm trọng | Chấp nhận (quyết định của người dùng) | phase 5 (owner_id, lease_expires_at, conditional claim, heartbeat, resume_turn, reclaim sweep, tool idempotency), phase 6 (drain requeue, HANGO_DRAIN_TIMEOUT ≥ một turn), phase 16 (hướng dẫn deploy) |
| D4 | pause_turn không thể round-trip qua mô hình block của hango, và hango không đăng ký server tool nào, nên đường resume là code không thể chạm tới | Nghiêm trọng | Chấp nhận (quyết định của người dùng) | phase 3 (ErrUnsupportedStop), phase 5 (xóa MaxResumes, cập nhật fixture và tiêu chí), các quyết định đã chốt và mục follow-up của plan.md |
| F1 | RLS như đã thiết kế từ chối mọi query trên pool thường, bắt đầu từ việc xác thực API key | Nghiêm trọng | Chấp nhận | phase 16 (system role có BYPASSRLS trên pool thứ hai nhỏ với năm nơi gọi được liệt kê, refactor WithTenantTx cho mọi call site thuộc tenant, không bao giờ băng qua một lời gọi LLM, toàn bộ test suite xanh khi ép RLS, đường auth được chạy thử sau khi bật RLS) |
| F2 | Không có atomic run claim và các chuyển trạng thái approval/expiry không nằm trong transaction: run có thể chạy hai lần | Nghiêm trọng | Chấp nhận | phase 5 (conditional claim, chuyển trạng thái có điều kiện, enqueue dedup, test race hai worker), phase 15 (quyết định và expiry mỗi bên chuyển trạng thái trong transaction riêng; hai approval cuối đồng thời ⇒ chỉ một lần thực thi) |
| F3 | Khi resume sau approval, RepairToolCalls phá hủy chính batch mà nó định resume; creator tự approve; tham số edit không được kiểm tra | Nghiêm trọng | Chấp nhận | phase 15 (nạp các quyết định trước BeforeRun, repair bỏ qua các id đã quyết định, resume chạy lại batch đang chờ qua tool chain, tiêu chí exactly-once, chỉ owner/admin và không bao giờ là creator, edit được kiểm tra theo Parameters() và bộ khớp when, approval.decided do handler publish) |
| F4 | Semaphore lane của subagent được acquire kiểu re-entrant: tự deadlock ở depth ≥ 2 | Nghiêm trọng | Chấp nhận | phase 9 (TryAcquire không chặn khi depth > 0 với tool result báo hết capacity, các batch tool chạy tuần tự trong v1, xóa tiêu chí và test parallel-task, giữ giới hạn depth) |
| F5 | internal/agent và internal/run tạo thành vòng import; chữ ký Agent.Run đổi ở phase 9 mà không liệt kê các nơi gọi | Nghiêm trọng | Chấp nhận | các quyết định đã chốt của plan.md (quy tắc một chiều), phase 5 (RunOptions, chữ ký cuối cùng, liệt kê các call site, EventSink thuộc về agent), phase 9 (agent.Gate được inject, không đổi chữ ký) |
| F6 | Thứ tự “Memory đứng sau PromptCache” mang tính then chốt lại không thể tồn tại: các prompt fragment là string được ghép bên ngoài chain | Nghiêm trọng | Chấp nhận | phase 7 (PromptContributor trả về []provider.Block, ghép bên trong ModelCall, thứ tự được assert theo chỉ số), phase 10 (breakpoint theo chỉ số block, breakpoint cho summary message), phase 11 (memory là RunHook + PromptContributor, bỏ tuyên bố WrapModelCall) |
| F7 | Accumulate(seq) không phục vụ được streaming và vứt mất message dở dang mà đường cancel cần | Cao | Chấp nhận | phase 3 (Accumulator có trạng thái với Push/Message/Partial/Usage/Stop), phase 5 và 6 (emit theo từng event trong khi tích lũy; lưu phần dở dang khi lỗi), phase 12 (parity test dùng cùng accumulator) |
| F8 | Lỗ hổng về độ bền của run: SSE replay không phân trang, rớt event trong giai đoạn replay, seq được cấp phát ngoài lệnh insert, lỗi Emit không được xử lý, run queued không có đường kiểm tra sống | Cao | Chấp nhận | phase 6 (vòng lặp catch-up cho replay, replay stage giới hạn theo byte, seq nằm trong tx insert, chính sách lỗi Emit có tài liệu với stream_degraded, DB poller, hango.run.oldest_queued_age, runs.last_event_seq trong DDL, turn.aborted trước khi retry, HANGO_SSE_WRITE_TIMEOUT và HANGO_MAX_QUEUED_RUNS_PER_SESSION trong config), phase 14 (alert) |
| F9 | Compaction có thể cắt đôi một cặp tool_use/tool_result; ngân sách không tính system prompt và định nghĩa tool | Cao | Chấp nhận | phase 10 (snap-back về ranh giới, validator sau compaction, fixture đối kháng, EstimateRequest, TokenCounter tùy chọn trên Messages.CountTokens, lần tràn thứ hai thì hạ cấp xuống truncation, cache breakpoint cho summary message, các counter ghi tại ranh giới turn, bỏ sessions.summary), phase 8 (EstimateRequest được giới thiệu cùng estimator) |
| F10 | State.Private map[string]any không có codec persistence: state middleware có kiểu bị reset im lặng khi resume | Cao | Chấp nhận | phase 7 (map[string]json.RawMessage, accessor theo từng middleware, đường missing-key tường minh với span event, test round-trip, scratch theo batch được xóa khi batch kết thúc) |
| F11 | Vòng đời topic của broadcaster mâu thuẫn với việc HITL tạm dừng; profile goroutineleak không thấy được các rò rỉ này | Cao | Chấp nhận | phase 6 (topic đếm tham chiếu, giới hạn subscriber có tài liệu, hango.broadcaster.topics, assert số goroutine và số topic, các điểm gỡ khỏi cancel registry), phase 16 (goroutineleak chỉ trong soak test) |
| F12 | Filesystem theo phạm vi agent phơi bày transcript của mọi session trên toàn tenant và biến /memories/ thành kho prompt-injection bền vững | Cao | Chấp nhận | phase 8 (prefix theo từng session bị loại khỏi glob/grep/API, context key agent-id do internal/fs sở hữu, kiểm tra quyền sở hữu agent trong files API, cột version với kết quả conflict, ghi vào prefix dành riêng cần đặc quyền, xóa biện pháp giảm thiểu sai về parallel-mutation), phase 9 (sửa dòng rủi ro), phase 11 (allowed_tools chỉ được thu hẹp), phase 15 (quy tắc interrupt mặc định) |
| F13 | Vệ sinh observability: tắt trace theo tenant chưa được cài đặt, lọc nội dung dựa trên pattern, metric HTTP không có route, không giới hạn tốc độ auth, bootstrap key không revoke được, thiếu hằng số GenAI, giá cache-write bị gộp | Cao | Chấp nhận | phase 1 (5 hằng số GenAI còn thiếu, metrics_names.go, content_attrs.go, bộ gắn nhãn http.route, danh sách strip của collector ở cả hai nhánh), phase 2 (tenants.trace_llm_content, limiter trước auth, last_used_at được gộp, bootstrap key dùng một lần), phase 3 (test cả hai cổng nội dung), phase 13 (giá cache-write 5m/1h), phase 14 (bỏ retrofit và test regex, thêm alert), phase 15 và 16 (cập nhật dashboard/alert) |
| F14 | Phạm vi không có lý do: CompositeBackend, hai chương trình driver load/soak riêng, cách nối test database và integration test chưa quyết định | Cao | Chấp nhận | phase 11 (bỏ CompositeBackend, prefix dành riêng nằm trong path.go, giữ ghi chú học), phase 16 (TestConcurrentRuns/TestSoak thay cho scripts/, viết ngân sách p95 trước), phase 2 (test database theo từng package qua HANGO_TEST_DB_DSN, go test ./... chạy integration, -short thì bỏ qua) |
| F15 | Mâu thuẫn nội bộ: phụ thuộc sai, các bảng effort không khớp nhau, số bước bị trùng, hợp đồng không ai đọc, giới hạn package sai, cách dùng uuid của thư viện chuẩn | Trung bình | Chấp nhận | plan.md (bảng effort được đối soát với các dòng tài liệu học, giới hạn package ghi là 18 + tool/builtin, chốt UUID v7), phase 12 ([5, 8, 10]), phase 13 ([9, 10, 12]), phase 3 và 5 (đánh số bước), phase 5 (xóa Result.Meta), phase 8 (đổi tên FileStore), phase 9 (định nghĩa InterruptConfig), phase 10/12 (dùng PromptCaching, xóa ParallelTools), đánh số lại migration 00009-00011 |
Phân bố mức độ trên 41 phát hiện gốc: 11 Nghiêm trọng, 22 Cao, 8 Trung bình. Được chấp nhận: 41 (100%). Bị từ chối: 0. Bốn mục được giải quyết bằng quyết định tường minh của người dùng (D1-D4) thay vì cách khắc phục mà các reviewer đề xuất.
File đã sửa: plan.md và cả mười sáu file phase. Effort ròng thay đổi: 180h → 216h (+36h), trong đó +35h là mục tài liệu học trước đó chưa được dự toán; effort cài đặt chuyển từ 180h → 181h vì các lần cắt phạm vi (BYO credential, CompositeBackend, driver load/soak, retrofit metric của phase 14: −11h) gần như bù cho công việc mới (sở hữu run và lease, refactor RLS, độ bền SSE, ma trận phân quyền, làm cứng filesystem: +12h).
Rà soát tính nhất quán toàn kế hoạch
Đọc lại plan.md và cả mười sáu file phase sau các lần sửa, rồi grep mọi file của kế hoạch để tìm các thuật ngữ mà những thay đổi có thể đã để lại mồ côi.
| Kiểm tra | Kết quả |
|---|---|
provider_credentials, HANGO_ENCRYPTION_KEY*, internal/auth/crypt.go | Chỉ nằm trong các ghi chú hoãn D1 có chủ đích (phase 12), chú thích danh sách bảng RLS (phase 16) và mục follow-up của plan.md. Không còn yêu cầu, file, bước hay tiêu chí nào đang hiệu lực. |
MaxResumes, pause_turn resume | Chỉ nằm trong chú thích struct của phase 5 ghi lại việc đã xóa. Mọi chỗ nhắc pause_turn giờ đều trỏ về ErrUnsupportedStop. |
CompositeBackend | Chỉ nằm trong văn xuôi so sánh với deepagents và ghi chú học “phương án bị loại” của phase 11. Không có file, bước hay tiêu chí nào của hango. |
scripts/loadtest, scripts/soaktest | Chỉ nằm trong câu của phase 16 nói rằng chúng không được xây. Các target Makefile giờ bọc go test. |
Các cổng goroutineleak | Chỉ nằm trong soak test của phase 16 và hai ghi chú giải thích vì sao không dùng ở nơi khác. Phase 6 dùng assert dựa trên số đếm. |
Accumulate(, Accumulate như một hàm | Đã biến mất. Phase 3 định nghĩa Accumulator; phase 5, 6 và 12 tham chiếu tới nó. Từ “Accumulate” còn lại xuất hiện hai lần như văn xuôi bình thường (“accumulate usage”, “accumulate strictly by Index”). |
Result.Meta | Đã xóa khỏi kiểu; chỉ còn chú thích ghi lại việc xóa. Phase 8 ghi đường offload lên span thay thế. |
sessions.summary | Cột đã bị bỏ khỏi migration phase 10 kèm chú thích nêu tên thứ thay thế. Không còn nơi nào đọc. |
FileStoreFactory | Đổi tên thành store.FileStore ở phase 8 (hai chỗ), có ghi chú việc đổi tên. |
hango.tenant.trace_llm (attribute cũ) | Được thay ở mọi nơi bằng hango.tenant.trace_llm_content, khai báo ở phase 1, điền ở phase 2, thực thi và test ở phase 3, được cấu hình collector của phase 1 dùng. |
State.Private map[string]any | Giờ là map[string]json.RawMessage ở phase 7; các map[string]any còn lại chỉ là tool.Parameters() (một đối tượng JSON Schema, đúng) và văn xuôi so sánh với goclaw. |
SystemPromptFragment / Chain.PromptFragments | Được thay bằng SystemBlocks ở phase 7, 10 và 11, kể cả danh sách “Modify” của phase 7. |
EstimateMessages | Được thay bằng EstimateRequest ở phase 8 và 10, kể cả danh sách “Modify” của phase 10. |
LaneGate | Được thay bằng agent.Gate cộng semaphore của internal/run ở phase 9. |
MarkRunning | Đã bỏ: conditional claim chính là bước chuyển sang running (phase 5). |
| Phụ thuộc giữa các phase | Frontmatter và bảng của plan.md khớp nhau cho cả mười sáu phase, kể cả phase 12 → [5, 8, 10] và phase 13 → [9, 10, 12] đã sửa. DAG không có chu trình và phụ thuộc của mỗi phase đều đứng trước nó về số thứ tự. |
| Effort | Tổng trong frontmatter khớp với bảng plan.md từng dòng (16, 16, 15, 12, 19, 16, 13, 15, 12, 13, 9, 10, 11, 10, 13, 16) và cộng lại 216h, khớp với frontmatter của plan.md. Dàn ý tài liệu học của mỗi phase kết thúc bằng ngân sách thời gian, và impl + doc bằng tổng của phase. |
| Đánh số migration | Được đánh số lại sau khi migration phase 12 bị bỏ: 00001-00008 không đổi, 00009 usage và quota (13), 00010 pending approvals (15), 00011 RLS (16). Không có khoảng trống và không đảo thứ tự so với thứ tự phase. |
| Số bước bị trùng | Đã sửa ở phase 3 (hai bước “12”) và phase 5 (hai bước “13”); các bước cài đặt của mọi phase giờ tăng nghiêm ngặt. |
| Giới hạn package | plan.md giờ ghi 18 package cộng internal/tool/builtin, có liệt kê; danh sách khớp với mọi package được nêu trong các file phase, và scripts/ không còn xuất hiện như nơi chứa code. |
| Các bảng test xuyên suốt | Bảng isolation (phase 2) được các phase 4, 5, 6, 8, 9, 13, 15 mở rộng và giờ có cả case chéo tenant lẫn case trong cùng tenant cho mỗi bảng; ma trận phân quyền (phase 2) được các phase 4, 5, 8 và 15 mở rộng. |
| Các quyết định đã chốt so với các phase | Mỗi quyết định đã chốt trong plan.md đều được hiện thực ở ít nhất một phase, và không phase nào mâu thuẫn với chúng: phụ thuộc package một chiều (5, 9), lease sở hữu (5, 6, 16), pause_turn là lỗi (3, 5), khả năng nhìn thấy trong cùng tenant (2, 4, 5, 8, 15), ghép prompt tại thời điểm chain (7, 10, 11), RLS như lớp bảo vệ được refactor (16), UUID v7 (2). |
Mâu thuẫn chưa giải quyết: 0.
Nhật ký xác thực
Phiên 1 — 2026-09-22
Nguyên nhân: cuộc phỏng vấn xác thực sau red-team trong /ak:plan --hard. Hỏi bốn câu, được trả lời bốn câu. Ba câu xác nhận kế hoạch như đã viết; một câu (Q3) thay phạm vi tối thiểu được đề xuất bằng một yêu cầu rộng hơn và được lan truyền qua các file phase bên dưới.
Q1 — Kiến trúc: đường dẫn Go module và nhà cung cấp CI
Câu hỏi: “Go module path? Phase 1 giả định github.com/phihdn/hango và phase 16 giả định GitHub Actions cho CI.”
Lựa chọn: github.com/phihdn/hango (Khuyến nghị) | Đường dẫn khác.
Trả lời: github.com/phihdn/hango.
Lý do: cố định mọi import path trong kế hoạch và nhà cung cấp CI ở phase 16. Kế hoạch không đổi.
Q2 — Đánh đổi: trace mức LLM đi đâu trong lúc phát triển
Câu hỏi: “Trace mức LLM đi đâu trong lúc phát triển?”
Lựa chọn: Langfuse Cloud Hobby (Khuyến nghị) | Tự host Langfuse từ phase 1 | Tạm thời chỉ dùng Grafana.
Trả lời: Langfuse Cloud Hobby.
Lý do: xác nhận mặc định của phase 1. Việc tự host vẫn nằm sau compose profile, nên quyết định này đảo ngược được mà không đụng vào code ứng dụng.
Q3 — Phạm vi: audit trail cho các thay đổi của admin
Câu hỏi: “Bản review bảo mật yêu cầu một audit trail cho các thay đổi của admin (tenant, user, key, quota, approval). Nó không nằm trong hợp đồng.”
Lựa chọn: Thêm một bảng audit_events tối thiểu ở phase 2 (Khuyến nghị) | Hoãn sang follow-up.
Trả lời (nhập tùy ý, nguyên văn): “I would like to audit/trace everything. As I said, I want to know what happened when something went wrong”.
Lý do: rộng hơn phương án được khuyến nghị. Mọi thay đổi đi qua API và mọi quyết định approval đều được audit, và mỗi dòng được gắn với trace id để audit trail, trace, run event log và log nối với nhau qua một khóa duy nhất.
Q4 — Giả định: phần tăng trưởng chạy được của phase 3
Câu hỏi: “Phase 3 (provider adapter) không có bề mặt HTTP. Bằng chứng binary của nó là lệnh hango probe cần một Anthropic key thật, cộng các test fake-provider trong go test. Chấp nhận được làm phần tăng trưởng chạy được của phase không?”
Lựa chọn: Có, lệnh probe cộng test (Khuyến nghị) | Không, thêm một chat endpoint tối thiểu.
Trả lời: Có.
Lý do: một chat endpoint tối thiểu thêm ở phase 3 sẽ bị vứt đi ở phase 5. Kế hoạch không đổi.
Các quyết định đã xác nhận
| # | Quyết định | Ảnh hưởng đến kế hoạch |
|---|---|---|
| Q1 | Module path github.com/phihdn/hango, CI trên GitHub Actions | Không có — xác nhận phase 1 và 16 như đã viết |
| Q2 | Langfuse Cloud Hobby cho trace LLM trong lúc phát triển | Không có — xác nhận mặc định của phase 1; tự host vẫn là một compose profile |
| Q3 | Audit mọi thứ: mọi request có thay đổi dữ liệu và mọi quyết định approval | Quyết định đã chốt mới; audit_events và audit middleware vào phase 2; các phase 4, 5, 6, 8, 13, 15 khai báo action của mình; phase 14 thêm panel, một bước runbook và alert; phase 16 thêm policy RLS và follow-up về retention |
| Q4 | Phase 3 ra mắt hango probe cộng test fake-provider, không có bề mặt HTTP | Không có — xác nhận phase 3 như đã viết |
Hạng mục hành động
| # | Hành động | Ở đâu | Trạng thái |
|---|---|---|---|
| A1 | Thêm DDL audit_events vào migration 00001_identity hiện có, giữ nguyên số của mọi migration khác | phase 2 | Đã áp dụng |
| A2 | Thêm audit middleware, SetAuditAction, AuditStore và route chỉ đọc GET /v1/audit-events cùng dòng phân quyền của nó | phase 2 | Đã áp dụng |
| A3 | Đăng ký hango.audit.events và hango.audit.write_failures qua cơ chế metrics_names.go của phase 1, và thêm bullet học về audit so với trace so với log | phase 2 | Đã áp dụng |
| A4 | Thêm audit_events vào bảng isolation chéo tenant và endpoint đọc vào ma trận phân quyền | phase 2 | Đã áp dụng |
| A5 | Khai báo các audit action của mỗi phase trong các bước cài đặt và assert chúng trong tiêu chí thành công | phase 4, 5, 6, 8, 13, 15 | Đã áp dụng |
| A6 | Audit các lần approval hết hạn do sweeper thực hiện với actor_kind=system, action approval.expire | phase 15 | Đã áp dụng |
| A7 | Thêm nhóm panel Audit vào hango-api, bước audit vào runbook, và các alert rule cho tỷ lệ bị từ chối và lỗi ghi | phase 14 | Đã áp dụng |
| A8 | Đưa audit_events vào danh sách RLS với quyền chỉ insert và select, và ghi lại follow-up về retention theo kiểu partition hoặc archive | phase 16 | Đã áp dụng |
| A9 | Đối soát effort: phase 2 +2h, phase 5, 14 và 15 mỗi phase +1h; cập nhật bảng các phase và cả hai tổng trong frontmatter | plan.md, phase 2, 5, 14, 15 | Đã áp dụng |
Tác động lên các phase
| Phase | Thay đổi | Effort |
|---|---|---|
| 2 | DDL audit_events trong 00001_identity, AuditStore, audit middleware và SetAuditAction, GET /v1/audit-events, hai metric, dòng phân quyền, case isolation, ba dòng rủi ro, bốn tiêu chí thành công | 16h → 18h |
| 4 | Các handler khai báo agent.create, agent.update, agent.delete, session.create, session.delete; việc append message cố ý không khai báo gì | 12h (không đổi) |
| 5 | Tạo run khai báo run.create và ghi agent, session và lane vào detail | 19h → 20h |
| 6 | Cancel khai báo run.cancel; tiêu chí cancel assert dòng audit | 16h (không đổi) |
| 8 | Files API khai báo file.write và file.delete, đường dẫn trong detail, không bao giờ ghi nội dung | 15h (không đổi) |
| 13 | Handler quota khai báo quota.update với giới hạn cũ và mới trong detail | 11h (không đổi) |
| 14 | Nhóm panel Audit trên hango-api, bước 8 của runbook, hai alert rule mới (mười → mười hai) | 10h → 11h |
| 15 | approval.decide từ handler kể cả các lần từ chối, approval.expire từ sweeper, bullet học, tiêu chí thành công | 13h → 14h |
| 16 | audit_events trong danh sách RLS với quyền chỉ insert và select, follow-up retention trong hướng dẫn deploy, bằng chứng OWASP trích dẫn các audit action | 16h (không đổi) |
Effort ròng: 216h → 221h (+5h). Cài đặt 181h → 186h; tài liệu học không đổi ở 35h.
Rà soát tính nhất quán toàn kế hoạch (Phiên 1)
Grep lại mọi file của kế hoạch để tìm audit, tên các action, endpoint, tên metric và số migration sau các lần sửa.
| Kiểm tra | Kết quả |
|---|---|
| Tên bảng | audit_events là cách viết duy nhất ở mọi nơi; nó được tạo một lần, trong migrations/00001_identity.sql (phase 2), và được các phase 4, 5, 6, 8, 13, 14, 15 và 16 cùng quyết định đã chốt của plan.md tham chiếu. |
| Tên action | Tập đóng được khai báo ở phase 2 là tenant.create, user.create, api_key.create, api_key.revoke, agent.create, agent.update, agent.delete, session.create, session.delete, run.create, run.cancel, file.write, file.delete, quota.update, approval.decide, approval.expire. Mọi action được nêu ở phase 4, 5, 6, 8, 13 và 15 đều nằm trong tập đó, và không phase nào đưa thêm action ngoài tập. approval.requested và approval.decided trông giống nhưng là loại SSE event của phase 15, không phải audit action, và vẫn tách biệt. |
| Endpoint | GET /v1/audit-events chỉ xuất hiện ở phase 2 — yêu cầu, ma trận phân quyền, file handler và tiêu chí thành công. Không phase nào thêm route ghi, sửa hay xóa dòng audit. |
| Tên metric | hango.audit.events và hango.audit.write_failures được khai báo trong dòng Instrumentation của phase 2, đăng ký qua cơ chế internal/telemetry/metrics_names.go mà phase 1 giới thiệu. Phase 1 không liệt kê tên metric nào một cách tường minh, nên không cần đổi gì. Các bổ sung dashboard và alert của phase 14 dùng đúng hai tên này. |
| Seam middleware | SetAuditAction(c, action, targetKind, targetID) được định nghĩa một lần (kiến trúc phase 2) và được gọi theo tên ở phase 4, 5, 6, 8, 13 và 15. Không phase nào tự nghĩ ra cách thứ hai để ghi một action. |
| Tính append-only | Được nêu ở phase 2 (store chỉ lộ Append và List) và được thực thi ở mức database ở phase 16 (quyền chỉ insert và select cho hango_app, cộng test). Không phase nào xóa hay sửa một dòng audit, và retention là một non-goal tường minh của v1 với follow-up có tên. |
| Đánh số migration | Không đổi: 00001-00011 y như trước, với audit_events được thêm vào thân của 00001 thay vì lấy một số mới. Migration RLS của phase 16 vẫn là 00011. |
| Effort | Tổng trong frontmatter là 16, 18, 15, 12, 20, 16, 13, 15, 12, 13, 9, 10, 11, 11, 14, 16 và cộng lại 221h, khớp với Tổng của bảng plan.md và frontmatter của plan.md. Cột Impl cộng lại 186h và cột Doc 35h. Các dòng “Time budget” của từng phase 2, 5, 14 và 15 đã được cập nhật cho khớp frontmatter mới. |
| Phụ thuộc giữa các phase | Phiên này không đụng tới; DAG không đổi và vẫn không có chu trình. |
| Các quyết định đã chốt so với các phase | Quyết định mới “audit mọi thứ” được hiện thực ở các phase 2, 4, 5, 6, 8, 13, 14, 15 và 16, và không phase nào mâu thuẫn với nó. |
Mâu thuẫn chưa giải quyết: 0.
Phiên review — 2026-10-13
Nguyên nhân: bản review learning-flow trong plans/reports/review-261013-1855-roadmap-learning-flow-and-gaps.md. Người dùng chọn giữ testcontainers (RV1 phương án O2), dùng thứ tự xây dựng 1 → 3 → 2 → 4 → 5 (RV7) và thêm một phase đánh giá (RV9 phương án O2). Tất cả RV1–RV9 đã được áp dụng.
| # | Thay đổi | Ở đâu |
|---|---|---|
| RV1 | main_test.go của từng package khởi động hoặc tái sử dụng một container testcontainers có tên và tạo database riêng; HANGO_TEST_DB_DSN là một override tùy chọn cho CI; không có testing.go ngoài test, nên binary production không link testcontainers | phase 2 |
| RV2 | Worker dựng scope từ role và trạng thái hiện tại của creator và làm run thất bại với creator_inactive khi creator bị vô hiệu hóa hoặc không còn; role rỗng là không hợp lệ và visibility(scope) fail closed với nó | phase 2, 4, 5 |
| RV3 | Việc tạo tenant, bootstrap và các route nền tảng tác động lên một tenant đều ghi bên trong scope của tenant đó, nên system pool giữ đúng năm nơi gọi | phase 2, 16 |
| RV4 | Phase 11 phụ thuộc [9, 10] | phase 11, plan.md |
| RV5 | internal/run/reconcile.go và test của nó chuyển từ Modify sang Create | phase 5 |
| RV6 | Mười chỗ văn bản lỗi thời được sửa | docs/roadmap.md, phase 6, 11, 12, 13, 14 |
| RV7 | Thêm mục Build order; “Next up” của trang đi theo nó | plan.md, docs/roadmap.md, build_plan_html.go, plan-template.html |
| RV8 | Các checkpoint review 5a và 5b bên trong phase 5 | phase 5 |
| RV9 | Phase 17, vòng đánh giá, 8h + 2h, không thêm package | phase 17, plan.md, docs/roadmap.md |
Effort ròng: 221h → 231h (+10h, toàn bộ là phase 17). Cài đặt 186h → 194h, tài liệu học 35h → 37h.
Phiên quyết định — 2026-10-14
Nguyên nhân: lần deploy đầu tiên dành cho gia đình của owner, và bản so sánh trong plans/reports/research-261014-1634-aicoworker-goclaw-hango-comparison.md. Người dùng chọn một kênh Telegram cho gia đình và một runtime Claude Code CLI giới hạn cho owner. Web chat để sau. Các request giả dạng Claude Code bằng subscription OAuth token vẫn nằm ngoài phạm vi.
| # | Thay đổi | Ở đâu |
|---|---|---|
| DS1 | Phase 18, kênh Telegram: long polling, chat riêng tư, bản đồ Telegram-sang-user hango tĩnh, trả lời dạng stream bằng cách sửa message, tạo run dùng chung với handler HTTP | phase 18, plan.md, docs/roadmap.md |
| DS2 | Phase 19, runtime Claude Code CLI: claude -p làm tiến trình con cho các agent có provider = "claude_cli", kiểm tra chỉ-owner khi lưu agent, tạo run và claim, tool qua một MCP bridge loopback, usage bị loại khỏi quota tenant | phase 19, plan.md, docs/roadmap.md |
| DS3 | Giới hạn package 18 → 20 cộng internal/tool/builtin (internal/telegram, internal/claudecli) | plan.md |
| DS4 | Build order đặt 18 và 19 sau 13 | plan.md, docs/roadmap.md |
| DS5 | MCP bridge của phase 19 được viết tay bằng thư viện chuẩn; không thêm SDK Go chính thức | phase 19 |
| DS6 | Phase 14 phụ thuộc 18 và 19 và thêm một dashboard hango-channels cùng ba alert cho Telegram và Claude CLI | phase 14, plan.md |
| DS7 | Phase 18 thêm trả lời Markdown-sang-HTML với phương án dự phòng plain text, dự phòng khi sửa message lỗi, retry_after trên mọi lời gọi, back-off khi 409 với poll watchdog, đăng ký lệnh, typing lặp lại và ngữ cảnh reply-to (+5h) | phase 18 |
| DS8 | Phase 20 đưa kênh Telegram tiến gần goclaw và OpenClaw: ảnh, album, tài liệu, voice note có phiên âm, gửi file ra, debounce, pairing code, chat nhóm có mention gating, nút approval và reaction trạng thái; nó thêm provider image block và migration 00012, và chạy sau phase 16 | phase 20, plan.md, docs/roadmap.md |
| DS9 | LLM gateway với quota theo từng user là một follow-up; cả gia đình dùng chung một quota tenant cho đến lúc đó | plan.md |
| DS10 | Nhóm của phase 20 theo goclaw: pairing nhóm, một group principal và một session dùng chung cho mỗi nhóm hoặc forum topic, policy open/allowlist/disabled, require_mention với chế độ strict/yield, wake word, lịch sử nhóm đang chờ, writer, và gộp config môi trường → nhóm → topic; forum topic chuyển từ follow-up vào phase 20 (+5h) | phase 20, plan.md, docs/roadmap.md |
| DS11 | Phiên âm giọng nói dùng bất kỳ endpoint tương thích OpenAI nào (OpenAI hoặc Groq Whisper) với gợi ý HANGO_STT_LANGUAGE, vi cho gia đình, transcript được chuẩn hóa NFC và một test fixture tiếng Việt có cổng điều kiện (+1h) | phase 20 |
| DS12 | Chat nhóm bị hoãn: mỗi người dùng chat riêng tư trong lúc này, và thiết kế nhóm của goclaw chuyển sang một mục hoãn trong phase 20 với HANGO_TELEGRAM_GROUP_HISTORY_LIMIT (mặc định 50, 0 là tắt) ghi đè được theo từng nhóm và topic; câu hỏi về group principal vẫn mở cho đến khi nhóm quay lại (−9h) | phase 20, phase 18, plan.md, docs/roadmap.md |
| DS13 | Tầng HTTP dùng http.ServeMux của thư viện chuẩn với một router nhỏ trong internal/server (Handle, Group, một bộ chuyển đổi lỗi, DecodeJSON, một writer ghi lại status) thay cho Echo v5; SetAuditAction nhận *http.Request, và tên span cùng http.route lấy từ r.Pattern (+1h) | phase 1, phase 2, plan.md, docs/tech-stack.md, docs/roadmap.md |
| DS14 | Web chat và một trang web admin là các follow-up xây bằng Svelte 5 trên HTTP API hiện có; không cái nào là phase của v1, nên effort không đổi | plan.md, docs/roadmap.md, docs/tech-stack.md |
Effort ròng: 231h → 290h (+59h). Cài đặt 194h → 246h, tài liệu học 37h → 44h.
Phiên quyết định — 2026-10-15
Nguyên nhân: bảng “Optimize context” của AICoworker, được ghi là A4 trong plans/reports/research-261014-1634-aicoworker-goclaw-hango-comparison.md.
| # | Thay đổi | Ở đâu |
|---|---|---|
| DS15 | Context inspector là một follow-up làm sau trang web admin, không phải phase của v1. Spec của nó, gồm cả thay đổi usage_records cho các lệnh gọi thử, nằm trong followup-context-inspector.md; effort v1, thứ tự build và số migration không đổi | plan.md, followup-context-inspector.md, docs/roadmap.md |