feat: add command center chat loop widget

This commit is contained in:
thanhnv
2026-07-08 22:58:02 +09:00
parent 36af576f15
commit a029a51e72
14 changed files with 225 additions and 38 deletions
+2 -2
View File
@@ -55,13 +55,13 @@
| **08 Context compression** | � MVP done+test | **CASAN-native token-killer** (Track 3) đã có: `context-compress.py` (dedup/extractive/structural, must-keep, tee, gate fail-able), `phase08-compression-tests.sh` 7/0 (WSL), nối CI. Còn: Track 1 nén INPUT + Track 2 nén VIEW liên-bước + Track 4 abstractive (gated) + nối H4/H5 trong pipeline thật. |
| **12 Domain Pack SDK** | 📋 chưa bắt đầu (🔓 01 done, còn chờ 06) | Onboard bằng khai báo (golden/corpus/policy theo domain). Phụ thuộc 01 ✅, 06. |
| **01 Restructure** | ✅ **done+test (2026-07-08, merged main)** | Đã tách: harness code → `packages/casan-harness/`, domain OKR → `apps/okr/domain/`, runtime state ở `.specify/`; **app promote lên git root** (hết wrapper `AINative_OKR_CASAN5/`); facade `.specify` symlink **gỡ sạch** (hard cutoff); path resolve qua `casan-paths.sh` (marker walk-up). Full gate **PASS=64 FAIL=0 SKIP=3** từ cấu trúc mới. CI (`.gitea/workflows/ci.yml`) + docs đã đồng bộ. Mở khoá 06/12. Chi tiết: `CASAN_PLAN_01_RESTRUCTURE.md`. |
| **13 Control Plane** | 🟡 core + **Track 1/2/3 + FinOps/SLO + Command Center + local-prod TLS/OIDC smoke** done+test | **Sửa kiến trúc: là tài sản harness, KHÔNG nằm trong OKR.** Governance core đã dời vào harness: `packages/casan-harness/scripts/bash/control-plane-settings.py` (settings versioned + audit hash-chain + deny-by-default + approval + rollback), `phase-control-plane-tests.sh` 9/0 + `phase-control-plane-hitl-tests.sh` 9/0 (nối CI). Đã gỡ khỏi `apps/okr` (OKR sạch: 46/0/3skip + 16/16). **Track 1 DONE**: Ops Console read-only telemetry. **Track 2 DONE**: settings API/UI tại `packages/casan-control-panel/` bọc `control-plane-settings.py` + `rbac-check.py`. **Track 3 DONE**: kill-switch API/UI; approval inbox/delegation/oversight API/UI bọc harness `approval-inbox.py` + `delegation-policy.yaml`, SoD, apply setting proposal qua governed store; IdP group claim→RBAC role mapping tested. **Track 4 local-prod DONE**: FinOps/SLO page + Docker/nginx/oauth2-proxy deploy scaffold; `local-prod-smoke.sh` passes `CP_LOCAL_SMOKE_PASS https_oidc=true actor=oidc-ops role=org-admin` and invokes authenticated `managed-prod-smoke.sh` on the local-prod endpoint (`CP_MANAGED_SMOKE_PASS actor=oidc-ops role=org-admin widgets=8`); `prod-readiness-check.sh` validates managed-prod TLS/OIDC/nginx prerequisites. **Command Center §8.6 DONE**: `GET /api/v1/command` + `/command` UI, 8 evidence-backed widgets, provenance envelope, executive briefing VI/EN, live ticker, evidence drawer. `npm run console:test` **20/0**, `npm run console:build` xanh. Targeted related suites: RBAC 12/0, C7 15/0. Còn: live managed prod deploy với cert/enterprise IdP/host thật (07 T2); RAI view và Ask CASAN thuộc Plan-15/18 follow-up. |
| **13 Control Plane** | 🟡 core + **Track 1/2/3 + FinOps/SLO + Command Center + local-prod TLS/OIDC smoke** done+test | **Sửa kiến trúc: là tài sản harness, KHÔNG nằm trong OKR.** Governance core đã dời vào harness: `packages/casan-harness/scripts/bash/control-plane-settings.py` (settings versioned + audit hash-chain + deny-by-default + approval + rollback), `phase-control-plane-tests.sh` 9/0 + `phase-control-plane-hitl-tests.sh` 9/0 (nối CI). Đã gỡ khỏi `apps/okr` (OKR sạch: 46/0/3skip + 16/16). **Track 1 DONE**: Ops Console read-only telemetry. **Track 2 DONE**: settings API/UI tại `packages/casan-control-panel/` bọc `control-plane-settings.py` + `rbac-check.py`. **Track 3 DONE**: kill-switch API/UI; approval inbox/delegation/oversight API/UI bọc harness `approval-inbox.py` + `delegation-policy.yaml`, SoD, apply setting proposal qua governed store; IdP group claim→RBAC role mapping tested. **Track 4 local-prod DONE**: FinOps/SLO page + Docker/nginx/oauth2-proxy deploy scaffold; `local-prod-smoke.sh` passes `CP_LOCAL_SMOKE_PASS https_oidc=true actor=oidc-ops role=org-admin` and invokes authenticated `managed-prod-smoke.sh` on the local-prod endpoint (`CP_MANAGED_SMOKE_PASS actor=oidc-ops role=org-admin widgets=9`); `prod-readiness-check.sh` validates managed-prod TLS/OIDC/nginx prerequisites. **Command Center §8.6 DONE**: `GET /api/v1/command` + `/command` UI, 9 evidence-backed widgets (gồm `chat_loop`), provenance envelope, executive briefing VI/EN, live ticker, evidence drawer. `npm run console:test` **26/0**, `npm run console:build` xanh. Targeted related suites: RBAC 12/0, C7 15/0. Còn: live managed prod deploy với cert/enterprise IdP/host thật (07 T2); RAI view thuộc Plan-15 follow-up. |
| **14 RBAC** | � core done+test | RBAC decision engine trong harness: `packages/casan-harness/scripts/bash/rbac-check.py` (role×resource:action, scope org/project, deny-by-default, tenant isolation, sensitive→org-admin, SoD), `phase-rbac-tests.sh` 10/0 (WSL), nối CI. **Đã thêm:** tenant data-boundary (SEC-23 23.13, org-admin A không đụng B) + **audit quyết định vào H5** (`CASAN_RBAC_AUDIT_LOG`, `phase-rbac-audit` 5/0). Còn: enforcement trong web app (13), ánh xạ IdP claim→role thật (07-C4). |
| **15 Responsible AI & Data Gov** | 🟡 core done+test+enforced | `rai-guard.py` (classify; PII→cloud deny; model-card; **retention** gate/purge-audit; **report** aggregate) + **enforcement** `harness-preflight.sh` chặn PII→cloud trước model-call, `model-router` opt-in `CASAN_PREFLIGHT`. `phase-rai` 12/0 + `phase-preflight` 5/0 (WSL), nối CI. Còn: view trên Control Plane. |
| Future B1–B6 | 💤 vision | `CASAN_PLAN_FUTURE_PHASES.md` — approval workflow nâng cao · state machine · model benchmark · governed memory · auto-remediation · platform KPI. |
| **17 Loop Engineering** | � T1–T6 done+test (offline) | **Agentic Loop Governance** — đủ 5 primitive + orchestrator (97/0 WSL, nối CI). T1 **Governor** (`loop-governor.py`; deny-by-default, no/corrupt policy→strict/HALT, on_exceed halt/escalate) 15/0; T2 **Convergence** (`loop-convergence.py`; repeat/thrash→OSCILLATING, flat→STALLED, fail-closed) 15/0; T3 **Verify Contract** (`loop-gate.py`; H4→DENY, unmet→FAIL, correction bounded→ESCALATE, no self-declared DONE) 20/0; T4 **Trace/Replay** (`loop-trace.py`; append-only hash-linked, edited→BREAK, tampered artifact→replay DRIFT) 16/0; T5 **Meta-loop** (`loop-metaloop.py`; propose≠apply, SoD, loosen>org_ceiling refused, apply qua governed CP store→đổi thật ceiling + rollback) 15/0; T6 **Orchestrator** (`loop-run.sh`; gate→governor→convergence→trace/turn, secure-by-default opt-out, nén giữa vòng) 16/0. State qua `CASAN_LOOP_STATE_ROOT` (repo `.specify/state` sạch). **Còn (infra):** T4 KMS-anchor head (A7 Vault), T6 widget Command Center (17.22, C5), live H3-judge. Chi tiết: `CASAN_PLAN_17_LOOP_ENGINEERING.md`. |
| **16 Security audit remediation** | � P0/P1/P2 phần lớn done+test | **Remediation đã thực thi:** 28 SEC suite (151/0 WSL, nối `ci-harness-gate.sh`). Done: SEC-01..10, 12, 13, **14** (model-digest bỏ env-override ở prod/strict), 15, 16..21, **22** (trusted-time JWT `exp` ARCH-06 + tag proposal nguồn-không-tin ARCH-08), **26** (stored/second-order injection scan), 27..30, **23 Phase 1–5 offline** (multi-tenant: tenant-store+guard · per-tenant CP/audit/telemetry · RBAC data-boundary · tenant kill-switch/quota · ký registry · crypt at-rest per-tenant), **24 offline** (image digest-pin + ký workflow), **25 offline** (artifact attestation tested==deployed); **SEC-11 gộp vào SEC-17** (`CASAN_PROFILE=prod` enforce-by-default). **Còn 📋 planned (hạ tầng/process):** SEC-22 ARCH-10 (attestation ngoài) · SEC-23 23.11 (crypt qua Vault Transit) · **SEC-24 còn** (live CVE/OSV + scan image thật — offline image-pin/ký-workflow đã done) · **SEC-25 còn** (signed-commit enrollment + SLSA chain — offline artifact-attestation đã done). Chi tiết: `CASAN_PLAN_16` §0a/§2d. |
| **18 Chat Console** | 🟡 **MVP-0 + MVP-1 done+test; MVP-2 Track 4 + OPERATOR Track 5/6 + Track 8.1/8.2 done** · target arch còn planned | **Governed Chat Console** (cắt lát MVP chống lan man). **MVP-0 Ask CASAN read-only DONE**: `prompt-mode-router.py`, `chat-readonly.py`, `chat-session.schema.json`, H4 input/output scan, H5 chat audit hash-chain, H6 token telemetry, answer kèm evidence sources. **MVP-1 Operator DONE**: registered actions through `action-gate`, no free-command, action artifacts with provenance. **MVP-2 Track 4 DONE**: `agent-registry.yaml`, `chat-agent-resolver.py`, `chat:select_agent` RBAC, delegation hold, tool allowlist BLOCK, Control Panel agent picker. **MVP-2 OPERATOR Track 5/6 DONE**: `chat-turn.py` certifies UNCERTIFIED draft through Plan-17 `loop-run.sh` + trace verify/replay before side-effect release. **Track 8.1/8.2 DONE**: `chat-replay.py` verifies chat chain, evidence artifact hash, and OPERATOR loop replay; Control Panel `GET /api/v1/chat/replay`. Test: chat suites **42/0** (`phase-chat-prompt-router` 8/0, `phase-chat-readonly` 5/0, `phase-chat-session-audit` 3/0, `phase-chat-operator` 8/0, `phase-chat-agent-select` 8/0, `phase-chat-pipeline` 4/0, `phase-chat-stream-hold` 2/0, `phase-chat-replay` 4/0), Control Panel **25/0** + build xanh. Next: CODEGEN-as-loop + Track 8.3/8.4 replay widget/approvals escalation → MVP-3 multi-tenant. |
| **18 Chat Console** | 🟡 **MVP-0 + MVP-1 done+test; MVP-2 Track 4 + OPERATOR Track 5/6 + Track 8.1/8.2/8.3 done** · target arch còn planned | **Governed Chat Console** (cắt lát MVP chống lan man). **MVP-0 Ask CASAN read-only DONE**: `prompt-mode-router.py`, `chat-readonly.py`, `chat-session.schema.json`, H4 input/output scan, H5 chat audit hash-chain, H6 token telemetry, answer kèm evidence sources. **MVP-1 Operator DONE**: registered actions through `action-gate`, no free-command, action artifacts with provenance. **MVP-2 Track 4 DONE**: `agent-registry.yaml`, `chat-agent-resolver.py`, `chat:select_agent` RBAC, delegation hold, tool allowlist BLOCK, Control Panel agent picker. **MVP-2 OPERATOR Track 5/6 DONE**: `chat-turn.py` certifies UNCERTIFIED draft through Plan-17 `loop-run.sh` + trace verify/replay before side-effect release. **Track 8.1/8.2 DONE**: `chat-replay.py` verifies chat chain, evidence artifact hash, and OPERATOR loop replay; Control Panel `GET /api/v1/chat/replay`. **Track 8.3 DONE**: Command Center `chat_loop` widget reads chat audit/replay evidence, loop ticker, budget gauge, and click-through evidence drawer. Test: chat suites **42/0** (`phase-chat-prompt-router` 8/0, `phase-chat-readonly` 5/0, `phase-chat-session-audit` 3/0, `phase-chat-operator` 8/0, `phase-chat-agent-select` 8/0, `phase-chat-pipeline` 4/0, `phase-chat-stream-hold` 2/0, `phase-chat-replay` 4/0), Control Panel **26/0** + build xanh. Next: CODEGEN-as-loop + Track 8.4 approvals escalation → MVP-3 multi-tenant. |
---
## Trần điểm & điều kiện lên "Strong (81+)"
+1 -1
View File
@@ -35,7 +35,7 @@
| 15 | `CASAN_PLAN_15_RESPONSIBLE_AI_DATA_GOV.md` | Responsible AI & Data Governance (FPT §14.3–14.4) | 📋 |
| 16 | `CASAN_PLAN_16_SECURITY_AUDIT_REMEDIATION.md` | Security audit core harness + kế hoạch vá (tamper-evidence/injection/fail-open) | � P0/P1/P2 done |
| 17 | `CASAN_PLAN_17_LOOP_ENGINEERING.md` | Loop Engineering / Agentic Loop Governance (budget governor, convergence, verify-contract, loop trace/replay, meta-loop) | 📋 |
| 18 | `CASAN_PLAN_18_CHAT_CONSOLE.md` | Governed Chat Console (target arch; **MVP-0 Ask CASAN + MVP-1 Operator done; MVP-2 Track 4/5/6 + Track 8.1/8.2 done** → widget/approvals → multi-tenant) | 🟡 MVP-0/1 + MVP-2 partial done |
| 18 | `CASAN_PLAN_18_CHAT_CONSOLE.md` | Governed Chat Console (target arch; **MVP-0 Ask CASAN + MVP-1 Operator done; MVP-2 Track 4/5/6 + Track 8.1/8.2/8.3 done** → approvals/CODEGEN → multi-tenant) | 🟡 MVP-0/1 + MVP-2 partial done |
| Future | `CASAN_PLAN_FUTURE_PHASES.md` | Approval workflow, state machine, benchmark, memory, remediation, KPI | 💤 vision |
> **Không có plan số 11:** số 11 được bỏ trống có chủ ý — nhánh eval/traceability đã
+10 -9
View File
@@ -21,8 +21,8 @@
> **Track 4 offline partial**:
> FinOps/SLO page reads provider usage + business KPI artifacts and budget cap setting
> without fabricating missing values.
> **Command Center §8.6 built:** `GET /api/v1/command` returns eight evidence-backed
> widgets with `{source, artifact_path, commit, run_at, verified}` envelopes; React
> **Command Center §8.6 built:** `GET /api/v1/command` returns nine evidence-backed
> widgets, including Plan-18 `chat_loop`, with `{source, artifact_path, commit, run_at, verified}` envelopes; React
> `/command` page has executive briefing VI/EN, live governance ticker, and evidence drawer.
> **Deploy scaffold + local-prod smoke:** `Dockerfile.control-panel-api`,
> `Dockerfile.control-panel-ui`, `docker-compose.control-panel.yml`,
@@ -30,14 +30,14 @@
> `docker compose config`; nginx config passes `nginx -t` with a temp cert/upstream aliases;
> `packages/casan-control-panel/scripts/local-prod-smoke.sh` passes
> `CP_LOCAL_SMOKE_PASS https_oidc=true actor=oidc-ops role=org-admin` and asserts
> `/api/v1/command` returns all 8 widgets with provenance envelopes.
> `/api/v1/command` returns all 9 widgets with provenance envelopes.
> `prod-readiness-check.sh` validates managed-prod host prerequisites (compose, TLS,
> oauth2-proxy OIDC env, nginx) without printing secrets.
> `managed-prod-smoke.sh` validates deployed endpoint auth enforcement (unauth blocked,
> spoofed identity headers blocked) and, with a real IdP cookie jar, authenticated
> `/settings` + `/command` behavior.
> Local-prod now runs that managed smoke with the mock-IdP authenticated cookie jar and
> emits `CP_MANAGED_SMOKE_PASS actor=oidc-ops role=org-admin widgets=8`.
> emits `CP_MANAGED_SMOKE_PASS actor=oidc-ops role=org-admin widgets=9`.
> Nginx `auth_request` overwrites identity headers and API maps IdP groups → RBAC roles.
> **Còn:** managed host/cert/enterprise IdP env thật (07 T2).
>
@@ -190,11 +190,11 @@ monitor + manage + settings"] --> API["Control-Plane API (NestJS)"]
- [x] Các key settings đang whitelist trong `control-plane-settings.py` quản lý được qua console (compression/cost/model/security/kill-switch/loop); kill-switch UI có.
- [x] FinOps/SLO board đọc provider usage + business KPI artifact thật; thiếu budget cap thì báo chưa cấu hình.
- [x] Approval inbox + delegation L0-L5 + oversight log có API/UI và test SoD/apply.
- [x] Command Center §8.6 có API/UI: 8 widget evidence-backed, provenance envelope, executive briefing VI/EN, live ticker, evidence drawer.
- [x] Command Center §8.6 có API/UI: 9 widget evidence-backed (gồm `chat_loop`), provenance envelope, executive briefing VI/EN, live ticker, evidence drawer.
- [x] Local production-like deploy có TLS + OIDC + oauth2-proxy + nginx `auth_request`; smoke pass qua HTTPS.
- [x] Managed production readiness checker cho cert/host/enterprise IdP env thật.
- [x] Managed production endpoint smoke script cho unauth/spoof/authenticated-cookie checks.
- [x] Authenticated managed endpoint smoke pass trên local-prod HTTPS/OIDC endpoint (`CP_MANAGED_SMOKE_PASS actor=oidc-ops role=org-admin widgets=8`).
- [x] Authenticated managed endpoint smoke pass trên local-prod HTTPS/OIDC endpoint (`CP_MANAGED_SMOKE_PASS actor=oidc-ops role=org-admin widgets=9`).
- [ ] Managed production deploy với cert/host/enterprise IdP thật (nối Plan-07 TIER 2).
- [x] Red-team mục 5 targeted xanh: control-plane 9/0 + HITL 9/0 + RBAC 12/0 + C7 15/0; full gate dài đã pass qua adversarial 44/0 nhưng A6 mất thời gian nên không claim full-gate xanh trong lượt này.
@@ -270,7 +270,7 @@ monitor + manage + settings"] --> API["Control-Plane API (NestJS)"]
| Task | Việc | Verify |
|---|---|---|
| 13V.0 | Chuẩn hóa envelope provenance trong Control API; core `--json` bổ sung sau khi có widget cần gọi CLI live | ✅ `commandCenter()` envelope có `source/artifact_path/commit/run_at/verified/status` |
| 13V.1 | API `GET /api/v1/command` gom 8 widget đọc-only, không ghi state | ✅ `console:test` contract 20/0 |
| 13V.1 | API `GET /api/v1/command` gom 9 widget đọc-only, không ghi state | ✅ `console:test` contract 26/0 |
| 13V.2 | V1 Maturity gauge + radar H1–H7 (click widget → evidence drawer) | ✅ `/command` widget + drawer |
| 13V.3 | V2 HITL panel + approvals inbox/oversight | ✅ đọc approval store; thiếu artifact → `no_data` |
| 13V.4 | V3 kill-switch + guardrail badges; V7 certified seal | ✅ đọc incidents + audit-head/audit tail |
@@ -278,9 +278,10 @@ monitor + manage + settings"] --> API["Control-Plane API (NestJS)"]
| 13V.6 | V8 self-improve pipeline | ✅ hiển thị primitive present + proposal nếu có trong inbox; không fabricate queue |
| 13V.7 | Executive Briefing mode (song ngữ VI/EN) + Live ticker + Evidence drawer dùng chung | ✅ `/command` page |
| 13V.8 | Provenance guard: thiếu source→`no_data`/`missing`, stale vẫn qua freshness | ✅ contract test + UI status badge |
| 13V.9 | Widget Loop/Chat chuyên sâu | ✅ `chat_loop` widget + ticker/budget/replay evidence drawer (Plan-18 Track 8.3) |
> **Hiện trạng:** Command Center baseline đã có. Widget Loop/Chat chuyên sâu và RAI view
> nằm ở Plan-17/18/15 follow-up, không còn chặn Plan-13 baseline.
> **Hiện trạng:** Command Center baseline và Widget Loop/Chat chuyên sâu đã có. RAI view
> nằm ở Plan-15 follow-up, không còn chặn Plan-13 baseline.
---
_Liên quan: `CASAN_PLAN_07_PRODUCTION_HARDENING.md` (D3 dashboard, C4 IdP, C7 kill-switch, TIER 2 deploy) · `CASAN_PLAN_14_RBAC.md` (phân quyền) · `CASAN_PLAN_04_SELFIMPROVE.md` (proposal→approval) · `CASAN_PLAN_08_CONTEXT_COMPRESSION.md` (compression-policy là settings) · `CASAN_PLAN_15_RESPONSIBLE_AI_DATA_GOV.md` (RAI/data view)._
+10 -8
View File
@@ -2,7 +2,7 @@
!theme plain
top to bottom direction
title CASAN Plan 13 - Baseline Complete and Remaining Follow-ups
title CASAN Plan 13 - Baseline + Chat/Loop Widget Complete and Remaining Follow-ups
skinparam backgroundColor #FAFAFA
skinparam componentStyle rectangle
@@ -20,13 +20,14 @@ package "DONE - Plan 13 Control Panel" #E8F5E9 {
component "NestJS Control API\nread telemetry + governed writes" as api_done #A9DFBF
component "Harness governance core\nsettings + RBAC + approvals\nkill-switch + H5 audit" as gov_core #A9DFBF
component "Local-prod deploy smoke\nTLS + oauth2-proxy + mock OIDC\nnginx auth_request" as local_prod #D5F5E3
component "Command Center baseline\n8 evidence-backed widgets\nbriefing + ticker + evidence drawer" as command_done #D5F5E3
component "Command Center baseline\n9 evidence-backed widgets\nbriefing + ticker + evidence drawer" as command_done #D5F5E3
component "Loop/Chat widget\nbudget + replay drawer + chat ticker\nextends Command Center contract" as loop_widgets_done #D5F5E3
}
package "NEXT - offline platform follow-ups" #FFF8E1 {
component "RAI / Data Gov view\nretention + model-card + PII report" as rai #F9E79F
component "Ask CASAN MVP-0\nread-only prompt router\nsource-cited answers + H4/H5/H6" as chat_ro #F9E79F
component "Loop/Chat widgets\nbudget + replay drawer + chat ticker\nextends Command Center contract" as loop_widgets #FAD7A0
component "Approvals escalation\nturn ESCALATED -> inbox\nJWT + SoD + reason" as approvals #F9E79F
component "CODEGEN-as-loop\nH4 guarded codegen\nloop-certified artifacts" as codegen_loop #F9E79F
}
package "EXTERNAL / MANAGED PROD" #FDEDEC {
@@ -41,11 +42,12 @@ api_done --> gov_core : wraps harness CLIs
api_done --> local_prod : verified behind auth proxy
api_done --> command_done : GET /api/v1/command
command_done --> gov_core : provenance + audit state
loop_widgets_done --> command_done : specialized widget
lead --> rai : next if staying offline
rai --> gov_core : read RAI + policy artifacts
chat_ro --> gov_core : read-only, audited, guarded
loop_widgets --> command_done : add specialized widgets
approvals --> gov_core : proposals + SoD
codegen_loop --> gov_core : guarded loop-run
managed_oidc ..> local_prod : replace mock IdP/cert
alerts ..> gov_core : route real incidents
@@ -53,9 +55,9 @@ billing ..> command_done : ground-truth FinOps
note right of rai
Recommended next:
1. If no external infra: build RAI view or Ask CASAN MVP-0.
1. If no external infra: build RAI view or Plan-18 approvals/CODEGEN slice.
2. If infra is available: promote local-prod OIDC to managed prod.
3. Loop/Chat widgets should extend the existing Command envelope.
3. Keep chat widgets on the existing Command envelope.
4. Keep every metric evidence-backed with provenance.
end note
+14 -7
View File
@@ -1,6 +1,6 @@
# KẾ HOẠCH 18 — Governed Chat Console (Chat-as-Loop qua Control Plane)
> Status 2026-07-08: **🟡 MVP-0 + MVP-1 done+test; MVP-2 Track 4 + Operator Track 5/6 done — target architecture còn planned.**
> Status 2026-07-08: **🟡 MVP-0 + MVP-1 done+test; MVP-2 Track 4 + Operator Track 5/6 + Track 8.1/8.2/8.3 done — target architecture còn planned.**
> Đã implement **Ask CASAN — Read-only Evidence Assistant** qua harness + Control
> Panel (`/api/v1/chat/ask`, `/chat` UI): Prompt Router deterministic
> `READ_ONLY/OPERATOR/BLOCK/NOT_SUPPORTED`, context whitelist, H4 scan in/out, H5
@@ -11,7 +11,8 @@
> gate chọn agent, delegation hold, tool allowlist block, Control Panel agent picker.
> **Operator Track 5/6** đã bind mỗi side-effect turn vào Plan-17 `loop-run.sh`,
> verify trace/replay, H4 preflight/context/tool-output scan, và chỉ release action
> sau khi draft được certify.
> sau khi draft được certify. **Track 8.1/8.2/8.3** đã có replay/verify-chain
> foundation và Command Center Chat/Loop widget đọc chat audit/replay evidence thật.
> Mục tiêu
> tổng thể vẫn là thêm **cửa sổ chat** vào Control Plane (Plan-13) như một
> **bề mặt tương tác của core harness** — mỗi lượt chat là **một loop-run được
@@ -288,7 +289,7 @@ flowchart TD
|---|---|---|---|
| 18.8.1 | ✅ **Replay** một turn (verify lại artifact đã ghi) phát hiện tamper/non-determinism | `chat-replay.py` + `loop-trace.py replay` | sửa artifact turn → replay lệch |
| 18.8.2 | ✅ `verify-chain` lịch sử chat (tamper-evidence, SEC-01/02; KMS khi TIER-2 là follow-up) | `chat-replay.py verify-chain` | sửa 1 turn → chain BREAK |
| 18.8.3 | Widget **Chat/Loop** trên Command Center (13 §8.6): ticker per-iteration, budget gauge, click → Evidence drawer | nối Plan-13 §8.6 | số khớp fixture; click → evidence |
| 18.8.3 | ✅ Widget **Chat/Loop** trên Command Center (13 §8.6): ticker per-iteration, budget gauge, click → Evidence drawer | `GET /api/v1/command` + `/command` | số khớp fixture; click → evidence |
| 18.8.4 | **Approvals/Escalation panel**: turn `ESCALATED` vào approvals inbox (13 §3.4), duyệt/từ chối (JWT + SoD + lý do) | nối Plan-13 §3.4 | escalate → pending; JWT giả → DENY |
### Track 9 — Multi-tenant hardening `[MVP-3]`
@@ -361,6 +362,12 @@ cập nhật tổng test ở `CASAN_HARDENING_STATUS.md`.
- [x] Control Plane `GET /api/v1/chat/replay` wraps harness replay; no verdict reimplementation in NestJS.
- [x] Tampered operator evidence artifact → `DRIFT`; tampered chat audit → `BREAK`. Verify: `phase-chat-replay` 4/0, `console:test` 25/0, `console:build` xanh.
**MVP-2 Track 8.3 (Command Center Chat/Loop widget):**
- [x] `GET /api/v1/command` exposes `chat_loop` widget backed by `.specify/logs/chat/chat-turns.jsonl` and harness `chat-replay.py`.
- [x] Widget metrics include turns, answered/action-completed counts, loop-run count, replay records, replayed loops, and token budget gauge from real chat metrics.
- [x] `/command` renders the Chat/Loop card in the existing evidence drawer flow; ticker includes recent chat/loop turns.
- [x] Fixture test proves counts and budget gauge match evidence. Verify: `console:test` 26/0, `console:build` xanh, `phase-chat-replay` 4/0.
**Full (target architecture) — DoD tổng:**
- [ ] Turn chạy **đúng như một loop-run Plan-17** (không định nghĩa vòng lặp riêng).
- [ ] **Không đường vòng:** test chứng minh bỏ bất kỳ gate nào (preflight/verify) → FAIL.
@@ -374,11 +381,11 @@ cập nhật tổng test ở `CASAN_HARDENING_STATUS.md`.
---
## 7. Ghi chú trung thực & Non-goals (không lan man)
- **[MVP-0 + MVP-1 + MVP-2 Track 4 + Operator Track 5/6 + Track 8.1/8.2] — 🟡 done+test.**
- **[MVP-0 + MVP-1 + MVP-2 Track 4 + Operator Track 5/6 + Track 8.1/8.2/8.3] — 🟡 done+test.**
Ask CASAN read-only, Operator registered actions, agent/skill governance foundation,
OPERATOR chat-as-loop/draft-hold, và replay/verify-chain foundation đã có harness
CLI + Control Panel API. Các phase CODEGEN-as-loop / replay widget / approvals
escalation / multi-tenant production vẫn chưa mở.
OPERATOR chat-as-loop/draft-hold, replay/verify-chain foundation, và Command Center
Chat/Loop widget đã có harness CLI + Control Panel API/UI. Các phase CODEGEN-as-loop /
approvals escalation / multi-tenant production vẫn chưa mở.
- **MVP-first (chống lan man):** **MVP-0 (Ask CASAN read-only) KHÔNG phụ thuộc Plan-17/
14/SEC-23** — làm được ngay trên nền H4/H5/H6 hiện có. Chỉ **MVP-2 trở đi** (chat-as-
loop + agent) mới cần Plan-17 (T1–T3) + Plan-14 RBAC; **MVP-3** mới cần SEC-23 tenant.
+2 -2
View File
@@ -77,8 +77,8 @@ approval JWT thật ([16 SEC-07](CASAN_PLAN_16_SECURITY_AUDIT_REMEDIATION.md)),
|---|---|---|:--:|:--:|---|
| B9 | ✅ **done** — **MVP-0 Ask CASAN read-only**: Prompt Router (Track 0) + Read-only Ask CASAN (Track 1) + session/audit (Track 2) + Control Panel API/UI (Track 7). H4 in/out, H5 audit hash-chain, H6 token, evidence sources; chat suites 14/0, Control Panel 23/0 + build xanh. Real model provider binding remains Track M follow-up when a provider is enabled. | [18 §1b, Track 0/1/2/7/M](CASAN_PLAN_18_CHAT_CONSOLE.md) | L | ✅ | done |
| B10 | ✅ **done** — **MVP-1 Operator mode**: registered actions `run tests` / `build pack` / `verify pack`, no free-command, all through `action-gate`, action artifacts with provenance, Control Panel quick actions. `phase-chat-operator` 8/0; total chat suites 24/0; Control Panel 24/0 + build xanh. | [18 Track 3](CASAN_PLAN_18_CHAT_CONSOLE.md) | M | ✅ | done |
| B11 | 🟡 **partial done** — Track 4 Agent/Skill governance + OPERATOR Track 5/6 chat-as-loop/draft-hold + Track 8.1/8.2 replay foundation. `chat-turn.py` certifies OPERATOR draft through Plan-17 `loop-run.sh`; `chat-replay.py` detects evidence artifact drift and chat-chain tamper. Chat suites 42/0; Control Panel 25/0 + build xanh. **Remaining MVP-2:** CODEGEN-as-loop and Track 8.3/8.4 replay widget/approvals escalation. | [18 Track 4/5/6/8](CASAN_PLAN_18_CHAT_CONSOLE.md) | L | 🔗 | B6, C7(RBAC), B10 |
| B12 | **Widget Loop/Chat** trên Command Center (loop ticker/budget/replay drawer) | [17 (17.22)](CASAN_PLAN_17_LOOP_ENGINEERING.md) · [18 Track 8](CASAN_PLAN_18_CHAT_CONSOLE.md) | M | 🔗 | C5 ✅ baseline |
| B11 | 🟡 **partial done** — Track 4 Agent/Skill governance + OPERATOR Track 5/6 chat-as-loop/draft-hold + Track 8.1/8.2 replay foundation + Track 8.3 Command Center Chat/Loop widget. `chat-turn.py` certifies OPERATOR draft through Plan-17 `loop-run.sh`; `chat-replay.py` detects evidence artifact drift and chat-chain tamper; `/command` shows chat loop ticker/budget/evidence. Chat suites 42/0; Control Panel 26/0 + build xanh. **Remaining MVP-2:** CODEGEN-as-loop and Track 8.4 approvals escalation. | [18 Track 4/5/6/8](CASAN_PLAN_18_CHAT_CONSOLE.md) | L | 🔗 | B6, C7(RBAC), B10 |
| B12 | ✅ **done** — Widget Loop/Chat trên Command Center (`chat_loop` widget, loop ticker, budget gauge, replay evidence drawer) | [17 (17.22)](CASAN_PLAN_17_LOOP_ENGINEERING.md) · [18 Track 8](CASAN_PLAN_18_CHAT_CONSOLE.md) | M | ✅ | C5 ✅ baseline |
| B13 | **MVP-3** multi-tenant chat hardening (Track 9) | [18 Track 9](CASAN_PLAN_18_CHAT_CONSOLE.md) | M | 🔗 | A3(SEC-23) |
---
+6 -3
View File
@@ -56,7 +56,7 @@ Approval inbox / HITL:
- `POST /api/v1/approvals/decide` — approve/reject with SoD and reason; approved
settings proposals apply through `control-plane-settings.py`.
Governed Chat (Plan-18 MVP-0/1 + MVP-2 Track 4 and Operator Track 5/6):
Governed Chat (Plan-18 MVP-0/1 + MVP-2 Track 4, Operator Track 5/6, and Track 8.1-8.3):
- `POST /api/v1/chat/ask` — Ask CASAN endpoint. The API only wraps harness
`chat-turn.py`; router verdicts, H4 input/output scan, action-gate decisions,
@@ -71,11 +71,14 @@ Governed Chat (Plan-18 MVP-0/1 + MVP-2 Track 4 and Operator Track 5/6):
- `GET /api/v1/chat/replay?chatId=<id>` — verifies chat-chain integrity,
evidence artifact hashes, and OPERATOR loop trace replay through harness
`chat-replay.py`.
- `GET /api/v1/command` — includes the `chat_loop` Command Center widget backed
by chat audit/replay evidence, loop ticker rows, and token budget gauge.
- `/chat` UI shows actor/role scope, `mode/risk/decision` badges, certified answer,
evidence sources, registered operator actions, agent binding, loop certification,
action-gate status, router details, and audit hash. Side-effect requests outside
registered actions return governed `BLOCK` or `NOT_SUPPORTED` responses; operator
side effects are held until the loop draft is certified.
- `/command` UI renders the Chat/Loop widget in the existing evidence drawer flow.
FinOps/SLO:
@@ -113,10 +116,10 @@ The scaffold includes `Dockerfile.control-panel-api`, `Dockerfile.control-panel-
overwrites browser-supplied `X-CASAN-*` headers, and passes IdP group claims to the API for
RBAC mapping. The local smoke starts a self-signed HTTPS stack with a mock OIDC IdP and
expects `CP_LOCAL_SMOKE_PASS https_oidc=true actor=oidc-ops role=org-admin`; it also
asserts the Command Center returns all eight widgets with provenance envelopes and invokes
asserts the Command Center returns all nine widgets with provenance envelopes and invokes
`managed-prod-smoke.sh` with the authenticated mock-IdP cookie jar. A passing local run
therefore emits both `CP_LOCAL_SMOKE_PASS ...` and
`CP_MANAGED_SMOKE_PASS actor=oidc-ops role=org-admin widgets=8`.
`CP_MANAGED_SMOKE_PASS actor=oidc-ops role=org-admin widgets=9`.
Managed production readiness, once the host has real TLS files and an enterprise OIDC
env file:
@@ -48,6 +48,7 @@ export const PATHS = {
benignFp: env('CASAN_CP_BENIGN_FP', 'docs/output/casan/benign-fp-report.json'),
scoringReport: env('CASAN_CP_SCORING_REPORT', 'docs/output/casan/phase3-real-run-scoring.md'),
approvalInbox: env('CASAN_CP_APPROVAL_INBOX', '.specify/level5/approval-inbox.json'),
chatAudit: env('CASAN_CP_CHAT_AUDIT', '.specify/logs/chat/chat-turns.jsonl'),
delegationPolicy: env('CASAN_CP_DELEGATION_POLICY', 'packages/casan-harness/config/delegation-policy.yaml'),
selfImprove: env('CASAN_CP_SELF_IMPROVE', 'packages/casan-harness/scripts/bash/self-improve.py'),
};
@@ -2,12 +2,24 @@
// packages/casan-harness/tests/generate-agentops-dashboard.py; all numbers come from real
// on-disk feeds. Missing feeds degrade to zero/empty + a `stale` flag — never fabricated.
import { existsSync, readFileSync, statSync } from 'node:fs';
import { execFileSync } from 'node:child_process';
import { join, relative } from 'node:path';
import { Injectable } from '@nestjs/common';
import { APP_ROOT, PATHS, isStale, metricsAgeSeconds, STALE_AFTER_S } from '../common/app-root.js';
import { readJsonl, readJson, readHead, readTrace } from './telemetry.reader.js';
type Row = Record<string, any>;
type ChatReplayResult = {
ok: boolean;
decision: string;
records: number;
loop_replayed?: number;
diffs?: any[];
audit_path?: string;
error?: string;
};
const CHAT_REPLAY_CLI = join(APP_ROOT, 'packages', 'casan-harness', 'scripts', 'bash', 'chat-replay.py');
const num = (v: any) => (typeof v === 'number' && isFinite(v) ? v : 0);
const sum = (rows: Row[], k: string) => rows.reduce((a, r) => a + num(r[k]), 0);
const count = (rows: Row[], pred: (r: Row) => boolean) => rows.reduce((a, r) => a + (pred(r) ? 1 : 0), 0);
@@ -53,6 +65,108 @@ function widget(
return { id, title, status, summary, metrics, envelope, evidence };
}
function parseJson<T>(raw: string): T | null {
if (!raw) return null;
try {
return JSON.parse(raw) as T;
} catch {
return null;
}
}
function runChatReplay(chatAuditPath: string): ChatReplayResult {
if (!existsSync(chatAuditPath)) {
return { ok: true, decision: 'NO_DATA', records: 0, loop_replayed: 0, diffs: [], audit_path: chatAuditPath };
}
try {
const stdout = execFileSync('python3', [CHAT_REPLAY_CLI, 'replay'], {
cwd: APP_ROOT,
encoding: 'utf8',
stdio: ['ignore', 'pipe', 'pipe'],
env: { ...process.env, CASAN_CHAT_AUDIT_LOG: chatAuditPath },
timeout: 15000,
});
const parsed = parseJson<Record<string, any>>(stdout.trim());
return { ok: true, decision: String(parsed?.decision ?? 'UNKNOWN'), records: num(parsed?.records), ...parsed };
} catch (err: any) {
const stdout = String(err?.stdout ?? '').trim();
const stderr = String(err?.stderr ?? '').trim();
const parsed = parseJson<Record<string, any>>(stdout);
if (parsed) {
return { ok: false, decision: String(parsed.decision ?? 'ERROR'), records: num(parsed.records), ...parsed };
}
return { ok: false, decision: 'ERROR', records: 0, loop_replayed: 0, diffs: [], audit_path: chatAuditPath, error: stderr || stdout || 'CHAT_REPLAY_FAILED' };
}
}
function isChatMetric(row: Row) {
const agent = String(row.agent ?? '');
const step = String(row.step ?? '');
return agent.startsWith('chat.') || step === 'ask-casan-readonly' || step.startsWith('operator:');
}
export function buildChatLoopWidget(
chatAudit: Row[],
metrics: Row[],
chatAuditPath = PATHS.chatAudit,
replay: ChatReplayResult = runChatReplay(chatAuditPath),
) {
const latest = chatAudit[chatAudit.length - 1] ?? null;
const chatMetrics = metrics.filter(isChatMetric);
const tokenBudget = Math.max(Number(process.env.CASAN_CHAT_TOKEN_BUDGET ?? 100000) || 100000, 1);
const totalTokens = sum(chatMetrics, 'total_tokens');
const status = replay.decision === 'BREAK'
? 'fail'
: replay.decision === 'DRIFT' || replay.decision === 'ERROR'
? 'warn'
: chatAudit.length > 0
? 'ok'
: 'no_data';
const loopRuns = count(chatAudit, (r) => Boolean(r.loop_run?.run_id));
const verifiedReplay = replay.decision === 'MATCH' || replay.decision === 'NO_DATA';
return widget(
'chat_loop',
'Chat / Loop console',
status,
`${chatAudit.length} turn(s), ${loopRuns} loop-run(s), replay ${replay.decision}`,
{
turns: chatAudit.length,
answered: count(chatAudit, (r) => r.decision === 'ANSWERED'),
action_completed: count(chatAudit, (r) => r.decision === 'ACTION_COMPLETED'),
loop_runs: loopRuns,
replay_records: replay.records,
replay_loop_replayed: num(replay.loop_replayed),
budget_used_pct: pct(totalTokens, tokenBudget),
},
artifact(chatAuditPath, 'chat-turns.jsonl + chat-replay.py', verifiedReplay),
{
replay,
budget: { total_tokens: totalTokens, token_budget: tokenBudget, used_pct: pct(totalTokens, tokenBudget) },
latest_turn: latest ? {
seq: latest.seq ?? null,
timestamp: latest.timestamp ?? null,
chat_id: latest.chat_id ?? null,
turn_id: latest.turn_id ?? null,
tenant_id: latest.tenant_id ?? null,
mode: latest.mode ?? null,
decision: latest.decision ?? null,
loop_run: latest.loop_run ?? null,
record_hash: latest.record_hash ?? null,
} : null,
recent_turns: recent(chatAudit, 5).map((r) => ({
seq: r.seq ?? null,
timestamp: r.timestamp ?? null,
chat_id: r.chat_id ?? null,
turn_id: r.turn_id ?? null,
decision: r.decision ?? null,
mode: r.mode ?? null,
loop_run_id: r.loop_run?.run_id ?? null,
})),
},
);
}
@Injectable()
export class TelemetryService {
private freshness() {
@@ -184,6 +298,7 @@ export class TelemetryService {
const security = readJsonl(PATHS.security);
const incidents = readJsonl(PATHS.incidents);
const actionGate = readJsonl(PATHS.actionGate);
const chatAudit = readJsonl(PATHS.chatAudit);
const traceability = readJson(PATHS.traceability) as any;
const kpi = readJson(PATHS.businessKpi) as any;
const drift = readJson(PATHS.drift) as any;
@@ -313,12 +428,19 @@ export class TelemetryService {
artifact(PATHS.selfImprove, 'self-improve.py'),
{ proposals: recent(selfImproveRelated, 5) },
),
chat_loop: buildChatLoopWidget(chatAudit, metrics),
};
const ticker = [
...recent(oversight, 10).map((e) => ({ at: e.at ?? e.created_at ?? null, kind: 'oversight', text: `${e.event ?? 'oversight'} ${e.proposal_id ?? ''}`.trim(), status: e.status ?? 'event' })),
...recent(incidents, 10).map((e) => ({ at: e.timestamp ?? null, kind: 'incident', text: `${e.event ?? 'incident'} ${e.scope ?? ''}`.trim(), status: e.severity ?? 'event' })),
...recent(audit, 10).map((e) => ({ at: e.timestamp ?? null, kind: 'audit', text: `${e.action ?? 'action'} ${e.decision ?? ''}`.trim(), status: e.decision ?? 'event' })),
...recent(chatAudit, 10).map((e) => ({
at: e.timestamp ?? null,
kind: 'chat_loop',
text: `${e.decision ?? 'turn'} ${e.chat_id ?? ''} ${e.loop_run?.run_id ? `loop:${e.loop_run.run_id}` : ''}`.trim(),
status: e.decision ?? 'event',
})),
].sort((a, b) => String(b.at ?? '').localeCompare(String(a.at ?? ''))).slice(0, 20);
return {
@@ -331,6 +453,7 @@ export class TelemetryService {
{ label: 'Safety', value: widgets.security.summary, status: widgets.security.status, widget: 'security' },
{ label: 'Economy', value: widgets.finops.summary, status: widgets.finops.status, widget: 'finops' },
{ label: 'Certification', value: widgets.certification.summary, status: widgets.certification.status, widget: 'certification' },
{ label: 'Chat/Loop', value: widgets.chat_loop.summary, status: widgets.chat_loop.status, widget: 'chat_loop' },
],
ticker,
};
@@ -5,7 +5,7 @@ import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { readJsonl, readJson, readHead } from '../src/telemetry/telemetry.reader.js';
import { APP_ROOT, PATHS, isStale, metricsAgeSeconds } from '../src/common/app-root.js';
import { TelemetryService } from '../src/telemetry/telemetry.service.js';
import { TelemetryService, buildChatLoopWidget } from '../src/telemetry/telemetry.service.js';
test('readJsonl parses valid lines and skips malformed', () => {
const d = mkdtempSync(join(tmpdir(), 'cp-'));
@@ -53,6 +53,7 @@ test('commandCenter() returns evidence-backed widget contract', () => {
const ids = Object.keys(command.widgets);
assert.deepEqual(ids.sort(), [
'certification',
'chat_loop',
'finops',
'hitl',
'kill_switch',
@@ -71,6 +72,53 @@ test('commandCenter() returns evidence-backed widget contract', () => {
assert.ok(Array.isArray(command.ticker));
});
test('buildChatLoopWidget summarizes chat replay fixture and evidence', () => {
const d = mkdtempSync(join(tmpdir(), 'cp-chat-'));
const auditPath = join(d, 'chat-turns.jsonl');
writeFileSync(auditPath, '{"seq":1}\n{"seq":2}\n');
const prevBudget = process.env.CASAN_CHAT_TOKEN_BUDGET;
process.env.CASAN_CHAT_TOKEN_BUDGET = '200';
try {
const widget = buildChatLoopWidget(
[
{ seq: 1, timestamp: '2026-07-08T00:00:00Z', chat_id: 'chat1', turn_id: 't1', mode: 'READ_ONLY', decision: 'ANSWERED' },
{
seq: 2,
timestamp: '2026-07-08T00:01:00Z',
chat_id: 'chat1',
turn_id: 't2',
mode: 'OPERATOR',
decision: 'ACTION_COMPLETED',
loop_run: { run_id: 'loop1', decision: 'CERTIFIED' },
record_hash: 'abc123',
},
],
[
{ agent: 'chat.ask-casan', step: 'ask-casan-readonly', total_tokens: 40 },
{ agent: 'chat.operator', step: 'operator:generate_report', total_tokens: 60 },
{ agent: 'planner', step: 'unrelated', total_tokens: 900 },
],
auditPath,
{ ok: true, decision: 'MATCH', records: 2, loop_replayed: 1, diffs: [], audit_path: auditPath },
) as any;
assert.equal(widget.id, 'chat_loop');
assert.equal(widget.status, 'ok');
assert.equal(widget.metrics.turns, 2);
assert.equal(widget.metrics.answered, 1);
assert.equal(widget.metrics.action_completed, 1);
assert.equal(widget.metrics.loop_runs, 1);
assert.equal(widget.metrics.replay_loop_replayed, 1);
assert.equal(widget.metrics.budget_used_pct, 50);
assert.equal(widget.envelope.verified, true);
assert.equal(widget.evidence.latest_turn.loop_run.run_id, 'loop1');
assert.equal(widget.evidence.replay.decision, 'MATCH');
} finally {
if (prevBudget === undefined) delete process.env.CASAN_CHAT_TOKEN_BUDGET;
else process.env.CASAN_CHAT_TOKEN_BUDGET = prevBudget;
}
});
test('freshness helpers behave (age is number|null, isStale is boolean)', () => {
const age = metricsAgeSeconds();
assert.ok(age === null || typeof age === 'number');
@@ -3,7 +3,7 @@ import { useQuery } from '@tanstack/react-query';
import { api, CommandWidget } from '../lib/api';
import { Card, StatTile, StatusBadge } from '../components/ui/Card';
const ORDER = ['maturity', 'hitl', 'kill_switch', 'traceability', 'security', 'finops', 'certification', 'self_improve'];
const ORDER = ['maturity', 'hitl', 'kill_switch', 'traceability', 'security', 'finops', 'certification', 'chat_loop', 'self_improve'];
const COPY = {
en: {
@@ -88,7 +88,8 @@ assert data.get('success') is True
command = json.load(open('/tmp/casan-cp-local-command.json'))
widgets = command.get('data', {}).get('widgets', {})
assert command.get('success') is True
assert len(widgets) == 8, widgets.keys()
assert len(widgets) == 9, widgets.keys()
assert 'chat_loop' in widgets, widgets.keys()
for widget in widgets.values():
envelope = widget.get('envelope', {})
assert {'source', 'artifact_path', 'commit', 'run_at', 'verified', 'status'} <= set(envelope), envelope
@@ -85,7 +85,8 @@ assert actor.get("role") and actor.get("role") != "unknown", actor
command = json.load(open(sys.argv[2]))
assert command.get("success") is True, command
widgets = command.get("data", {}).get("widgets", {})
assert len(widgets) == 8, widgets.keys()
assert len(widgets) == 9, widgets.keys()
assert "chat_loop" in widgets, widgets.keys()
for widget in widgets.values():
envelope = widget.get("envelope", {})
required = {"source", "artifact_path", "commit", "run_at", "verified", "status"}
+2 -2
View File
@@ -19,7 +19,7 @@ Optional layer for teams that want UI / dashboard / visibility. Packages: `casan
| RBAC + approval inbox | ✅ local-prod done | Settings + kill-switch API RBAC enforcement, approval inbox/delegation/oversight, SoD, governed setting proposal apply, and local OIDC claim→role mapping smoke are done. Enterprise IdP rollout remains production follow-up. |
| Evidence Pack Viewer | 📋 planned | reads `docs/output/casan/evidence-packs/` |
| Attack Battery Viewer | 📋 planned | reads red-team corpus + H4 recall results |
| Read-only Ask CASAN + Operator + agent selection + operator loop-hold + replay | 🟡 MVP-0/1 done+test; MVP-2 Track 4 + OPERATOR Track 5/6 + Track 8.1/8.2 done | Plan-18: `POST /api/v1/chat/ask`, `GET /api/v1/chat/actions`, `GET /api/v1/chat/agents`, `GET /api/v1/chat/replay` + `/chat`, backed by harness `chat-turn.py`, `chat-agent-resolver.py`, `chat-replay.py`, and Plan-17 `loop-run.sh` |
| Read-only Ask CASAN + Operator + agent selection + operator loop-hold + replay/widget | 🟡 MVP-0/1 done+test; MVP-2 Track 4 + OPERATOR Track 5/6 + Track 8.1/8.2/8.3 done | Plan-18: `POST /api/v1/chat/ask`, `GET /api/v1/chat/actions`, `GET /api/v1/chat/agents`, `GET /api/v1/chat/replay`, Command Center `chat_loop` widget + `/chat` + `/command`, backed by harness `chat-turn.py`, `chat-agent-resolver.py`, `chat-replay.py`, and Plan-17 `loop-run.sh` |
| Gitea webhook integration | 📋 planned | trigger gate / publish evidence on push |
## Build (preview)
@@ -29,6 +29,6 @@ scripts/package-release.sh platform # → dist/casan-platform-preview-vX.Y.Z
The bundle includes a `PREVIEW-INCOMPLETE.txt` marker. Do not treat it as a finished product.
## To implement later
Start from Plan-15 RAI view or Plan-18 MVP-2 Track 8.3/8.4 (replay widget / approvals escalation) or CODEGEN-as-loop. Keep new numbers
Start from Plan-15 RAI view, Plan-18 MVP-2 Track 8.4 approvals escalation, or CODEGEN-as-loop. Keep new numbers
evidence-backed with provenance; any write/governed action must continue to route through
harness RBAC, approval, and audit primitives.