feat: add command center chat loop widget
This commit is contained in:
@@ -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. |
|
| **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. |
|
| **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`. |
|
| **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). |
|
| **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. |
|
| **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. |
|
| 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`. |
|
| **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. |
|
| **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+)"
|
## Trần điểm & điều kiện lên "Strong (81+)"
|
||||||
|
|||||||
@@ -35,7 +35,7 @@
|
|||||||
| 15 | `CASAN_PLAN_15_RESPONSIBLE_AI_DATA_GOV.md` | Responsible AI & Data Governance (FPT §14.3–14.4) | 📋 |
|
| 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 |
|
| 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) | 📋 |
|
| 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 |
|
| 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 đã
|
> **Không có plan số 11:** số 11 được bỏ trống có chủ ý — nhánh eval/traceability đã
|
||||||
|
|||||||
@@ -21,8 +21,8 @@
|
|||||||
> **Track 4 offline partial**:
|
> **Track 4 offline partial**:
|
||||||
> FinOps/SLO page reads provider usage + business KPI artifacts and budget cap setting
|
> FinOps/SLO page reads provider usage + business KPI artifacts and budget cap setting
|
||||||
> without fabricating missing values.
|
> without fabricating missing values.
|
||||||
> **Command Center §8.6 built:** `GET /api/v1/command` returns eight evidence-backed
|
> **Command Center §8.6 built:** `GET /api/v1/command` returns nine evidence-backed
|
||||||
> widgets with `{source, artifact_path, commit, run_at, verified}` envelopes; React
|
> 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.
|
> `/command` page has executive briefing VI/EN, live governance ticker, and evidence drawer.
|
||||||
> **Deploy scaffold + local-prod smoke:** `Dockerfile.control-panel-api`,
|
> **Deploy scaffold + local-prod smoke:** `Dockerfile.control-panel-api`,
|
||||||
> `Dockerfile.control-panel-ui`, `docker-compose.control-panel.yml`,
|
> `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;
|
> `docker compose config`; nginx config passes `nginx -t` with a temp cert/upstream aliases;
|
||||||
> `packages/casan-control-panel/scripts/local-prod-smoke.sh` passes
|
> `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
|
> `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,
|
> `prod-readiness-check.sh` validates managed-prod host prerequisites (compose, TLS,
|
||||||
> oauth2-proxy OIDC env, nginx) without printing secrets.
|
> oauth2-proxy OIDC env, nginx) without printing secrets.
|
||||||
> `managed-prod-smoke.sh` validates deployed endpoint auth enforcement (unauth blocked,
|
> `managed-prod-smoke.sh` validates deployed endpoint auth enforcement (unauth blocked,
|
||||||
> spoofed identity headers blocked) and, with a real IdP cookie jar, authenticated
|
> spoofed identity headers blocked) and, with a real IdP cookie jar, authenticated
|
||||||
> `/settings` + `/command` behavior.
|
> `/settings` + `/command` behavior.
|
||||||
> Local-prod now runs that managed smoke with the mock-IdP authenticated cookie jar and
|
> 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.
|
> 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).
|
> **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] 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] 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] 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] 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 readiness checker cho cert/host/enterprise IdP env thật.
|
||||||
- [x] Managed production endpoint smoke script cho unauth/spoof/authenticated-cookie checks.
|
- [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).
|
- [ ] 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.
|
- [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 |
|
| 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.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.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.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 |
|
| 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.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.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.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
|
> **Hiện trạng:** Command Center baseline và Widget Loop/Chat chuyên sâu đã có. RAI view
|
||||||
> nằm ở Plan-17/18/15 follow-up, không còn chặn Plan-13 baseline.
|
> 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)._
|
_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)._
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
!theme plain
|
!theme plain
|
||||||
top to bottom direction
|
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 backgroundColor #FAFAFA
|
||||||
skinparam componentStyle rectangle
|
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 "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 "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 "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 {
|
package "NEXT - offline platform follow-ups" #FFF8E1 {
|
||||||
component "RAI / Data Gov view\nretention + model-card + PII report" as rai #F9E79F
|
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 "Approvals escalation\nturn ESCALATED -> inbox\nJWT + SoD + reason" as approvals #F9E79F
|
||||||
component "Loop/Chat widgets\nbudget + replay drawer + chat ticker\nextends Command Center contract" as loop_widgets #FAD7A0
|
component "CODEGEN-as-loop\nH4 guarded codegen\nloop-certified artifacts" as codegen_loop #F9E79F
|
||||||
}
|
}
|
||||||
|
|
||||||
package "EXTERNAL / MANAGED PROD" #FDEDEC {
|
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 --> local_prod : verified behind auth proxy
|
||||||
api_done --> command_done : GET /api/v1/command
|
api_done --> command_done : GET /api/v1/command
|
||||||
command_done --> gov_core : provenance + audit state
|
command_done --> gov_core : provenance + audit state
|
||||||
|
loop_widgets_done --> command_done : specialized widget
|
||||||
|
|
||||||
lead --> rai : next if staying offline
|
lead --> rai : next if staying offline
|
||||||
rai --> gov_core : read RAI + policy artifacts
|
rai --> gov_core : read RAI + policy artifacts
|
||||||
chat_ro --> gov_core : read-only, audited, guarded
|
approvals --> gov_core : proposals + SoD
|
||||||
loop_widgets --> command_done : add specialized widgets
|
codegen_loop --> gov_core : guarded loop-run
|
||||||
|
|
||||||
managed_oidc ..> local_prod : replace mock IdP/cert
|
managed_oidc ..> local_prod : replace mock IdP/cert
|
||||||
alerts ..> gov_core : route real incidents
|
alerts ..> gov_core : route real incidents
|
||||||
@@ -53,9 +55,9 @@ billing ..> command_done : ground-truth FinOps
|
|||||||
|
|
||||||
note right of rai
|
note right of rai
|
||||||
Recommended next:
|
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.
|
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.
|
4. Keep every metric evidence-backed with provenance.
|
||||||
end note
|
end note
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# KẾ HOẠCH 18 — Governed Chat Console (Chat-as-Loop qua Control Plane)
|
# 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
|
> Đã implement **Ask CASAN — Read-only Evidence Assistant** qua harness + Control
|
||||||
> Panel (`/api/v1/chat/ask`, `/chat` UI): Prompt Router deterministic
|
> Panel (`/api/v1/chat/ask`, `/chat` UI): Prompt Router deterministic
|
||||||
> `READ_ONLY/OPERATOR/BLOCK/NOT_SUPPORTED`, context whitelist, H4 scan in/out, H5
|
> `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.
|
> 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`,
|
> **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
|
> 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
|
> 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
|
> 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
|
> **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.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.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 |
|
| 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]`
|
### 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] 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.
|
- [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:**
|
**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).
|
- [ ] 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.
|
- [ ] **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)
|
## 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,
|
Ask CASAN read-only, Operator registered actions, agent/skill governance foundation,
|
||||||
OPERATOR chat-as-loop/draft-hold, và replay/verify-chain foundation đã có harness
|
OPERATOR chat-as-loop/draft-hold, replay/verify-chain foundation, và Command Center
|
||||||
CLI + Control Panel API. Các phase CODEGEN-as-loop / replay widget / approvals
|
Chat/Loop widget đã có harness CLI + Control Panel API/UI. Các phase CODEGEN-as-loop /
|
||||||
escalation / multi-tenant production vẫn chưa mở.
|
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/
|
- **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-
|
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.
|
loop + agent) mới cần Plan-17 (T1–T3) + Plan-14 RBAC; **MVP-3** mới cần SEC-23 tenant.
|
||||||
|
|||||||
@@ -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 |
|
| 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 |
|
| 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 |
|
| 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 | **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 |
|
| 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) |
|
| B13 | **MVP-3** multi-tenant chat hardening (Track 9) | [18 Track 9](CASAN_PLAN_18_CHAT_CONSOLE.md) | M | 🔗 | A3(SEC-23) |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ Approval inbox / HITL:
|
|||||||
- `POST /api/v1/approvals/decide` — approve/reject with SoD and reason; approved
|
- `POST /api/v1/approvals/decide` — approve/reject with SoD and reason; approved
|
||||||
settings proposals apply through `control-plane-settings.py`.
|
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
|
- `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,
|
`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,
|
- `GET /api/v1/chat/replay?chatId=<id>` — verifies chat-chain integrity,
|
||||||
evidence artifact hashes, and OPERATOR loop trace replay through harness
|
evidence artifact hashes, and OPERATOR loop trace replay through harness
|
||||||
`chat-replay.py`.
|
`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,
|
- `/chat` UI shows actor/role scope, `mode/risk/decision` badges, certified answer,
|
||||||
evidence sources, registered operator actions, agent binding, loop certification,
|
evidence sources, registered operator actions, agent binding, loop certification,
|
||||||
action-gate status, router details, and audit hash. Side-effect requests outside
|
action-gate status, router details, and audit hash. Side-effect requests outside
|
||||||
registered actions return governed `BLOCK` or `NOT_SUPPORTED` responses; operator
|
registered actions return governed `BLOCK` or `NOT_SUPPORTED` responses; operator
|
||||||
side effects are held until the loop draft is certified.
|
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:
|
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
|
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
|
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
|
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
|
`managed-prod-smoke.sh` with the authenticated mock-IdP cookie jar. A passing local run
|
||||||
therefore emits both `CP_LOCAL_SMOKE_PASS ...` and
|
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
|
Managed production readiness, once the host has real TLS files and an enterprise OIDC
|
||||||
env file:
|
env file:
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ export const PATHS = {
|
|||||||
benignFp: env('CASAN_CP_BENIGN_FP', 'docs/output/casan/benign-fp-report.json'),
|
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'),
|
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'),
|
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'),
|
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'),
|
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
|
// 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.
|
// on-disk feeds. Missing feeds degrade to zero/empty + a `stale` flag — never fabricated.
|
||||||
import { existsSync, readFileSync, statSync } from 'node:fs';
|
import { existsSync, readFileSync, statSync } from 'node:fs';
|
||||||
|
import { execFileSync } from 'node:child_process';
|
||||||
import { join, relative } from 'node:path';
|
import { join, relative } from 'node:path';
|
||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
import { APP_ROOT, PATHS, isStale, metricsAgeSeconds, STALE_AFTER_S } from '../common/app-root.js';
|
import { APP_ROOT, PATHS, isStale, metricsAgeSeconds, STALE_AFTER_S } from '../common/app-root.js';
|
||||||
import { readJsonl, readJson, readHead, readTrace } from './telemetry.reader.js';
|
import { readJsonl, readJson, readHead, readTrace } from './telemetry.reader.js';
|
||||||
|
|
||||||
type Row = Record<string, any>;
|
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 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 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);
|
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 };
|
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()
|
@Injectable()
|
||||||
export class TelemetryService {
|
export class TelemetryService {
|
||||||
private freshness() {
|
private freshness() {
|
||||||
@@ -184,6 +298,7 @@ export class TelemetryService {
|
|||||||
const security = readJsonl(PATHS.security);
|
const security = readJsonl(PATHS.security);
|
||||||
const incidents = readJsonl(PATHS.incidents);
|
const incidents = readJsonl(PATHS.incidents);
|
||||||
const actionGate = readJsonl(PATHS.actionGate);
|
const actionGate = readJsonl(PATHS.actionGate);
|
||||||
|
const chatAudit = readJsonl(PATHS.chatAudit);
|
||||||
const traceability = readJson(PATHS.traceability) as any;
|
const traceability = readJson(PATHS.traceability) as any;
|
||||||
const kpi = readJson(PATHS.businessKpi) as any;
|
const kpi = readJson(PATHS.businessKpi) as any;
|
||||||
const drift = readJson(PATHS.drift) as any;
|
const drift = readJson(PATHS.drift) as any;
|
||||||
@@ -313,12 +428,19 @@ export class TelemetryService {
|
|||||||
artifact(PATHS.selfImprove, 'self-improve.py'),
|
artifact(PATHS.selfImprove, 'self-improve.py'),
|
||||||
{ proposals: recent(selfImproveRelated, 5) },
|
{ proposals: recent(selfImproveRelated, 5) },
|
||||||
),
|
),
|
||||||
|
chat_loop: buildChatLoopWidget(chatAudit, metrics),
|
||||||
};
|
};
|
||||||
|
|
||||||
const ticker = [
|
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(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(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(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);
|
].sort((a, b) => String(b.at ?? '').localeCompare(String(a.at ?? ''))).slice(0, 20);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -331,6 +453,7 @@ export class TelemetryService {
|
|||||||
{ label: 'Safety', value: widgets.security.summary, status: widgets.security.status, widget: 'security' },
|
{ 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: 'Economy', value: widgets.finops.summary, status: widgets.finops.status, widget: 'finops' },
|
||||||
{ label: 'Certification', value: widgets.certification.summary, status: widgets.certification.status, widget: 'certification' },
|
{ 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,
|
ticker,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { tmpdir } from 'node:os';
|
|||||||
import { join } from 'node:path';
|
import { join } from 'node:path';
|
||||||
import { readJsonl, readJson, readHead } from '../src/telemetry/telemetry.reader.js';
|
import { readJsonl, readJson, readHead } from '../src/telemetry/telemetry.reader.js';
|
||||||
import { APP_ROOT, PATHS, isStale, metricsAgeSeconds } from '../src/common/app-root.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', () => {
|
test('readJsonl parses valid lines and skips malformed', () => {
|
||||||
const d = mkdtempSync(join(tmpdir(), 'cp-'));
|
const d = mkdtempSync(join(tmpdir(), 'cp-'));
|
||||||
@@ -53,6 +53,7 @@ test('commandCenter() returns evidence-backed widget contract', () => {
|
|||||||
const ids = Object.keys(command.widgets);
|
const ids = Object.keys(command.widgets);
|
||||||
assert.deepEqual(ids.sort(), [
|
assert.deepEqual(ids.sort(), [
|
||||||
'certification',
|
'certification',
|
||||||
|
'chat_loop',
|
||||||
'finops',
|
'finops',
|
||||||
'hitl',
|
'hitl',
|
||||||
'kill_switch',
|
'kill_switch',
|
||||||
@@ -71,6 +72,53 @@ test('commandCenter() returns evidence-backed widget contract', () => {
|
|||||||
assert.ok(Array.isArray(command.ticker));
|
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)', () => {
|
test('freshness helpers behave (age is number|null, isStale is boolean)', () => {
|
||||||
const age = metricsAgeSeconds();
|
const age = metricsAgeSeconds();
|
||||||
assert.ok(age === null || typeof age === 'number');
|
assert.ok(age === null || typeof age === 'number');
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { useQuery } from '@tanstack/react-query';
|
|||||||
import { api, CommandWidget } from '../lib/api';
|
import { api, CommandWidget } from '../lib/api';
|
||||||
import { Card, StatTile, StatusBadge } from '../components/ui/Card';
|
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 = {
|
const COPY = {
|
||||||
en: {
|
en: {
|
||||||
|
|||||||
@@ -88,7 +88,8 @@ assert data.get('success') is True
|
|||||||
command = json.load(open('/tmp/casan-cp-local-command.json'))
|
command = json.load(open('/tmp/casan-cp-local-command.json'))
|
||||||
widgets = command.get('data', {}).get('widgets', {})
|
widgets = command.get('data', {}).get('widgets', {})
|
||||||
assert command.get('success') is True
|
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():
|
for widget in widgets.values():
|
||||||
envelope = widget.get('envelope', {})
|
envelope = widget.get('envelope', {})
|
||||||
assert {'source', 'artifact_path', 'commit', 'run_at', 'verified', 'status'} <= set(envelope), 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]))
|
command = json.load(open(sys.argv[2]))
|
||||||
assert command.get("success") is True, command
|
assert command.get("success") is True, command
|
||||||
widgets = command.get("data", {}).get("widgets", {})
|
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():
|
for widget in widgets.values():
|
||||||
envelope = widget.get("envelope", {})
|
envelope = widget.get("envelope", {})
|
||||||
required = {"source", "artifact_path", "commit", "run_at", "verified", "status"}
|
required = {"source", "artifact_path", "commit", "run_at", "verified", "status"}
|
||||||
|
|||||||
@@ -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. |
|
| 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/` |
|
| Evidence Pack Viewer | 📋 planned | reads `docs/output/casan/evidence-packs/` |
|
||||||
| Attack Battery Viewer | 📋 planned | reads red-team corpus + H4 recall results |
|
| 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 |
|
| Gitea webhook integration | 📋 planned | trigger gate / publish evidence on push |
|
||||||
|
|
||||||
## Build (preview)
|
## 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.
|
The bundle includes a `PREVIEW-INCOMPLETE.txt` marker. Do not treat it as a finished product.
|
||||||
|
|
||||||
## To implement later
|
## 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
|
evidence-backed with provenance; any write/governed action must continue to route through
|
||||||
harness RBAC, approval, and audit primitives.
|
harness RBAC, approval, and audit primitives.
|
||||||
|
|||||||
Reference in New Issue
Block a user